Trying to make a new Disc Launcher type weapon....

Sami has expressed his desire for a Disc Launcher that has no splash damage & in the future would inherit the player's velocity. This is my first venture in to modding weapons; I think I have maxxed myself out on making maps 😉

I found the AVM Tutorial database, and it has many examples in it for all types of weapons. However, it seems like the information is maybe a little dated & jumbled up. I have not had 100% success yet with adding a weapon of my own making.

Does anyone know of a weapon Mod that was released with the modified .cs script files intact? I would like to look at an example like that & see how they made it work. I could then have a better chance at making the new Disc Launcher work once I see what all was changed from Base to the Mod. I don't want to "steal" anyones code; I just want to learn from it. I would give full credit in my weapons file to any original coders.

Comments

  • edited August 15

    While I wouldn't say they're always the cleanest, almost every mod that isn't distributed in dso form should have an example of how a weapon being added/modified can work. Some of them do handle things like the inventory listing differently, but it's all the same in principle. Grab that mega pack of mod content and have a look through each one and you should find a few good ones, and a whole bunch of very rough ones that get the job done.

    To add a new alternate spinfusor to alter those factors, you're mainly going to be creating new copies of a set of datablocks, making sure to give them new names:

    • An ItemData block weapon itself, which describes the weapon as a pickup object in the world
    • An ItemData block for the ammo pickup, unless you want it to share the same ammo as the original
    • A LinearProjectileData block to define the projectile and its parameters, where you'll apply the splash damage and velocity inheritance
    • A ShapeBaseImageData block, which describes the weapon in your hand and its behaviours. For a simple change, this will most importantly need to be updated to reference your other new datablocks: item, ammo, projectile.

    The easiest way to get started understanding how this behaves on your own server is just to change out the projectile datablock alone and reload the script, but because these are datablocks, you can't see the changes live without restarting the server unless %client.transmitDataBlocks(0); is being run on your client every time the script is compile/exec'd. Some mods implement a reload("mydisc.cs"); function to do this for every client on a server.

    For more advanced weapon behaviours, you can start to look at state transitions and their script callbacks in the ShapeBaseImageData datablock. "onFire" is the standard included trigger callback, and you'll see ShapeBaseImageData::onFire in projectiles.cs for the default action where the projectile is created. This can be completely overridden by creating a function with the same structure for your specific datablock, e.g. function DiscImage::onFire(%data, %obj, %slot) which can inherit the ShapeBaseImageData::onFire logic by calling %projectile = Parent::onFire(%data, %obj, %slot); You'll see a lot of examples of this if you look at one of the construction mods or something like Meltdown.

    Similarly, the projectile/explosions may have callbacks that are of interest for further advanced behaviours, e.g. having players within range of the impact have some special effect applied to them.


    For actually adding it as a new weapon with how it's handled in the base scripts, you'll want to look at weapons.cs for how reticles are specified to be sent to clients, and inventoryHud.cs for how it gets added to the inventory list for players to select. You'll also need to modify the player datablocks in player.cs to set new max[MyDisc] counts for inventory restrictions.

    If you don't want to bother going that far for testing a new gun, you can directly modify the disc datablocks to just try the changes on the spinfusor itself, or you can use %client.player.setInventory(%datablockName, %count, %force); with the ItemData pickup block names e.g. MyDisc, 1, 1 and MyDiscAmmo, 1000, 1

  • Wow !!! 😎 Thanks for the quick response !!! 😊 I have a ton of reading to do now 😃 I really appreciate the advice !!! 👍️👍️

    I'll go grab the Mega pack now & play around with it 😉

  • Using Krash's input on another thread, this is the function I've been using to reload mod changes. The last call binds the function to `ctrl + ;`. Refreshing the file manager is needed if you add a new .cs file while the game is already running, otherwise exec() will not be able to find it.

    function reloadModScripts(%a0) {
        if (!%a0) {
            return;
        }
    
        // Refresh file manager
        setModPaths(getModPaths());
    
        // ADD YOUR SCRIPT EXECS HERE
        // E.g. exec("scripts/weapons/my_cool_gun.cs");
    
        // Transmit datablocks to non-bots
        for (%i = 0; %i < ClientGroup.getCount(); %i++) {
            %client = ClientGroup.getObject(%i);
            if (!%client.isAIControlled()) {
                %client.transmitDataBlocks(0);
            }
        }
    }
    
    GlobalActionMap.bind("keyboard", "ctrl ;", "reloadModScripts");
    

    -Arg0

  • I just finished extracting the Mega pack to an empty directory. It looks like the Triumph mod has all of the .cs script files & I will be able to use those as how-to-do stuff examples 😃 Many thanks to the guys who put that together !!! 👍️

    I also read thru the scripting FAQ included in the Mega pack...... Very good information & I am glad to have found it ☺️

    Thanks for the advice & direction, guys.... I'll probably be back in about a month or two once I get all of this down pat ☺️

  • Well, after quite a few attempts I seem to have made some progress 😉 I am using the Triumph mod by PinkPanther as an example, as it has the .cs files included in the script directory. He made pretty good notes on his work inside the files, and the structure is easy to follow. I have tracked down all of the relevant files & WinMerge is a very helpful utility to use for seeing the differences between the original script file & the modified script file. These guys did a LOT of work 😃

    I originally could not get the weapon to be visible on my arm; Now I have that fixed. My current issue is trying to get the reticle of my choosing to display when the weapon is called up & mounted on the player's arm. As of now, there is no reticle visible. I will continue changing one thing at a time until I figure it out.

    I have tested the projectile at faster speeds & slower speeds to confirm that it is working, and boy was I happy when I saw that stuff was working perfectly 😁 I think in a few weeks I will have The Sami a unique weapon 😀

    The best part of all of this will be that I am trying to keep it a server-side only Mod, available to only Human players. I do not know if that will be possible. If it is not, then I will have to start the tedious process of adding it to the bots 😐️

  • As long as you don't need things like custom models, keeping your mod server-side shouldn't be a problem. Custom maps do need any new interiors or custom terrains downloaded if you need them, but many maps just reuse existing assets.


    Reticles are changed with the setWeaponsHudActive clientCmd (RPC messages sent to clients with commandToClient), which is in part tied to some basic init in DefaultGame::setupClientHuds when a client finishes loading: you'll see the setWeaponsHudBitmap calls which send $WeaponsHudData entries for names and the hud icons to associate with each index slot. Remember to increment $WeaponsHudCount when you add your own $WeaponsHudData -- it's important to set these up in order for your preferred icon to be shown in the client's hud. The "itemDataName" you'll be using is the exact name of your ItemData datablock (e.g. MyDisc in the previous example), in order for it to map the lookup correctly.

    If you look over clientCmdSetWeaponsHudActive itself, you'll see that you can't apply custom reticles to the default ItemData datablock names, so if you want to override them on the server side you need to rename the datablock and serverside references to it, including the $WeaponsHudData entries. What you'll also notice is that if you are not using a default datablock name, you can freely tell the client to apply any reticle bitmap, not just the one you've setup in the $WeaponsHudData entry; essentially, if you wanted to implement custom mode switching or something where you wanted to change reticles dynamically, you could send the setWeaponsHudActive command yourself at any time. You'll just want to keep track of the slot you've told the client to use for the hud icon.

    An basic example of extending the default set up of the entries in your weapon script as well as (optionally!) overriding the active reticle selection on mount could look something like:

    if (MyDisc.slot $= "")
    {
        MyDisc.slot = $WeaponsHudCount;
    
        $WeaponsHudData[MyDisc.slot, bitmapName] = "gui/hud_disc";
        $WeaponsHudData[MyDisc.slot, itemDataName] = "MyDisc";
        $WeaponsHudData[MyDisc.slot, ammoDataName] = "MyDiscAmmo";
        $WeaponsHudData[MyDisc.slot, reticle] = "gui/ret_disc";
        $WeaponsHudData[MyDisc.slot, visible] = "true";
    
        $WeaponsHudCount++;
    }
    
    function MyDiscImage::onMount(%this, %obj, %slot)
    {
        Parent::onMount(%this, %obj, %slot);
    
        // override the $WeaponsHudData sourced reticle that was just applied by the parent function
        commandToClient(%obj.client, 'setWeaponsHudActive', MyDisc.slot, "gui/ret_chaingun", "false");
    }
    

    Note that rather than throwing the slot index info in a global variable, it's being stored directly in the datablock here. This isn't strictly necessary, and it doesn't have any special properties, it's just for your convenience.

  • Krash, thank you so much for the advice !!!!! 😉 I should have some more time to dive in to it next weekend !!! ☺️

  • Well, I have had some time to devote to this & I have made some progress. My current problem is that I cannot get a reticle to show up on the middle of the screen where it is supposed to be. I then started a Triumph server offline & joined it with my regular Classic game installation client & of course I discovered that a basic Triumph install also does not have a reticle for the Slug Rifle 🙄

    Hah...... I chose that one to use as an example exactly because it is a Linear Projectile. I guess I did learn that my copying & pasting & editing skillz are on par 😉

    To start off, I read the notes on adding weapons from the Unofficial Tribes 2 Coding FAQ that was in the Mega Pack installation. I then looked at the .cs files in the /scripts directory in the Triumph mod directory & did a one-to-one comparison to a Classic /scripts installation.

    I settled on needing to modify the following files.....

    hud, inventory, inventoryHud, OptionsDlg, player, & weapons. In /packs, ammopacks. In /weapons, slugrifle.

    Next, I edited the original Classic files in my TestMod /scripts directory, with the intention of just adding one of the working Triumph weapons to my TestMod server & to see if I could get some success that way. Tada, I had success. My added weapon functions just like the weapon does in the Triumph mod. Neither one shows a reticle, and I don't think either one is supposed to show a reticle.

    hud file additions are as follows..... ( I will only list the files where I am pretty sure the problem exists)

       case "SlugRifle":

         reticleHud.setBitmap("gui/hud_ret_targlaser");

         reticleFrameHud.setVisible(false); (Note: I have also tried setting this to true with no visible effect taking place)

    weapons.cs file additions are here:

    $WeaponsHudData[11, bitmapName]  = "gui/hud_sniper";

    $WeaponsHudData[11, itemDataName] = "SlugRifle";

    $WeaponsHudData[11, ammoDataName] = "SlugRifleAmmo";

    $WeaponsHudData[11, reticle] = "gui/hud_ret_targlaser";

    $WeaponsHudData[11, visible] = "true"; (I think I even tried this as false with no visible effect taking place)

    ......ibid.......

    $WeaponsHudCount = 19;

    exec("scripts/weapons/slugrifle.cs");

    I won't go in to ammopacks & slugrifle.cs, as I am certain the problem is not there.

    I also tried putting this at the bottom of my slugrifle.cs file:

    if (MyDisc.slot $= " ") ......ibid.... Where I changed every occurrence of MyDisc to slugrifle - Still no reticle.

    My guess is that the reason it is not showing a reticle for the slugrifle is somewhere in the /scripts hud.cs & weapons.cs file.

    I did try adding it to /scripts/controlDefaults.cs function useslugrifle( %val ) ....ibid.... but that didn't help either - I was just trying that as a last resort.

    I am sooooo close to getting it working perfectly - I just need a reticle on the screen - Once I figure that out, I will be done with the weapon for Sami.

  • edited September 7

    The change you mentioned in the hud file is client-side (meaning if you did make it work, it may only work if the client has the same file changes), as are changes to things like OptionsDlg and ControlDefaults. You can however modify clientCmdSetWeaponsHudActive for debugging purposes, for example by echoing the %slot, %ret, and %vis inputs to your console, as well as the client's $WeaponNames[%slot] value.

    The hardcoded $WeaponsHudData indexed weapons aren't something you really want to change if you intend on easily sharing your weapon on its own. Obviously you can, and many mods do keep all their item config details centrally there, but arguably it's better to keep it with the item itself (better still stored in the datablock itself, but that hasn't been common practice and requires more extensive modding).

    The example I gave of the if (MyDisc.slot $= "") setting of $WeaponsHudData entries is sufficient server-side to select the correct reticle for any clients that use the standard scripts. That is, in a mod following the patterns used in base/classic, if I were to add a new script loaded in/after disc.cs with nothing but the following, this would apply the reticle as intended (and echo "slug!" to the console) whenever the "SlugRifle" weapon in the player's inventory [client].player.setInventory(SlugRifle, 1, 1); is selected:

    datablock ShapeBaseImageData(SlugRifleImage) : DiscImage
    {
        item = SlugRifle;
        ammo = DiscAmmo; // just reusing the standard disc ammo for the reticle example
    };
    
    datablock ItemData(SlugRifle) : Disc
    {
        image = SlugRifleImage;
        pickUpName = "a slugfusor";
    };
    
    if (SlugRifle.slot $= "")
    {
        SlugRifle.slot = $WeaponsHudCount;
    
        $WeaponsHudData[SlugRifle.slot, bitmapName] = "gui/hud_sniper";
        $WeaponsHudData[SlugRifle.slot, itemDataName] = "SlugRifle";
        $WeaponsHudData[SlugRifle.slot, ammoDataName] = "DiscAmmo";
        $WeaponsHudData[SlugRifle.slot, reticle] = "gui/hud_ret_targlaser";
        $WeaponsHudData[SlugRifle.slot, visible] = "true"; // this toggles the little circle around the reticle
    
        $WeaponsHudCount++;
    }
    
    function SlugRifleImage::onMount(%this, %obj, %slot)
    {
        Parent::onMount(%this, %obj, %slot);
    
        echo("slug!");
    }
    


  • Krash, thank you so very much for your reply !!! It is very appreciated !!! 😊 I am not a C coder by any means; My forte is G Code & Fanuc Macro B, which are used in CNC machine tools. I've got some experience with Boolean operators & some very minimal Basic stuff. This Torque script stuff is very nice, and I am slowly picking up on it, bit by bit 🙂

    You mentioned adding a new script loaded in or after disc.cs.... Am I correct in assuming it would be something like the following scenario?:

    In the Mod-directory-name/scripts/weapons.cs file, I would need to add my new force-reticle script call like below, with a script file named JebsWillMakeThatReticleReallyAppearThisTimeDadgumIt.cs residing in the Mod-directory-name/scripts/ directory?

    exec("scripts/weapons/disc.cs");

    exec("scripts/weapons/grenadeLauncher.cs");

    exec("scripts/weapons/slugrifle.cs");

    exec("scripts/weapons/JebsWillMakeThatReticleReallyAppearThisTimeDadgumIt.cs");

    Or, would I also be able to put your latest script example inside of the /scripts/weapons/slugrifle.cs file itself at the end of the file?

    Thank you so much for your help !!! 😎

  • Hmmmmm - I made a very interesting observation this morning. When connecting to my offline Triumph mod server, and connecting to it with my normal T2 Classic client for playing, the only weapons that have visible reticles are the base Classic weapons like the Blaster, Chaingun, Spinfusor, etc etc........ I did not see reticles visible for any of the weapons that were added to the Triumph mod. Is there a possibility the problem is located on my client?

    I did try adding a forcedreticle.cs script in my /scripts/weapons/ directory & I modified the /scripts/weapons.cs file to load the forcedreticle.cs file. I did not have any success with that method 😔

  • edited September 7

    The load order being after disc is only important for the example because the datablocks as shown inherit from Disc/DiscImage parents and re-use DiscAmmo. Since you already have your own complete versions of these named datablocks, for your own $WeaponsHudData entries to be added in the same way as shown, you only need to make sure it runs the slugrifle script after $WeaponsHudCount has been set in weapons.cs – otherwise the index count would be blank and it'd be useless. Loading it alongside the other weapons as you're showing is fine.

    The example doesn't force anything out of the ordinary, it's just showing about as succinctly as possible that adding $WeaponsHudData entries is all that's needed for custom reticles on any server mod using the same SetWeaponsHudActive setup as base/classic.

    If you're not sure if the mod you're running on your client supports standard server-side reticles this way, open up hud.cs and go to function clientCmdSetWeaponsHudActive, then scroll to the end of the function. You should see the switch$ block end with a default case as follows:

          default:
             reticleHud.setBitmap(%ret);
             reticleFrameHud.setVisible(%vis);
    

    This is where those custom reticles are applied on the client, and every server-side mod expects it to be there.

  • Krash, I will read over your latest post in a couple of hours from now when I have some time to absorb it & ruminate on it. I need to go help my wife with some stuff in her flowerbed now lol 😂

    On a whim, & just before I read your latest post, I did try to rule some things out by doing a couple of tests. On my client, I ran a manual DSO clean .bat file to get rid of old DSOs (possibly from some crazy attempt at running a Host server for mission editing). I made sure all of the client DSOs were gone, & I connected to BlakHarts server & everything worked good as far as Classic should run.

    I then edited the hud.cs & weapons.cs in my Mod-Directory-name/scripts/ folder, & I just changed the default disc reticle to a plasma reticle in both of those files. I wanted to see if I could make the original as-shipped with Classic upper level reticles switch out. I didn't change my slugrifle stuff on the lines lower down in the file.

    Alas, upon testing with my client, the disc reticle was still a disc reticle & not a plasma reticle as hoped - It ignored any changes I made to those upper level lines.

    I did check function clientCmdSetWeaponsHudActive in both my mod & client hud.cs files, & they are both set the same as in your default example above.

    After the flowerbed shenanigans are done, I will make another go at the JebsWillMakeThatReticleReallyAppearThisTimeDadgumIt.cs method of forcing a reticle.

    2 or 3 posts above, you mentioned [client].player.setInventory(SlugRifle, 1, 1); ---- I can only find the following usage in my inventory.cs file....

    %player.setInventory(SlugRifle, 1);

    Is the 1, 1) a typo or something that is missing in my files? Or, is it coming from some $ variable higher up the food chain somewhere?

  • edited 4:36AM

    If you made modifications to the static entries in clientCmdSetWeaponsHudActive and didn't see them when you pulled out that gun, it might not be using the script you're looking at. Try running compile or exec on the script in your client console manually; if you're just making changes and restarting the game, it's super common to miss a syntax error, in which case it'll load an old dso compiled version if it still has one.

    The quickest way to check if the clientCmdSetWeaponsHudActive you're looking at is the one that's actually being used is to throw an echo in there, scroll through your weapons, then check your console to see if there's a bunch of spam. Or, hell, you could intentionally just add a syntax error to the file to test, and if it doesn't report any errors, it's most likely loading from another mod -- check your shortcut's -mod selection if so. The shortcut's another easy one to miss if you've been testing different mods.


    The optional third argument on setInventory calls after the datablock name and quantity is the "force" toggle, which skips inventory limit checks. You don't need to include this if you've already added the max[SlugRifle] and max[SlugRifleAmmo] entries to your PlayerData armor datablocks, but even then it's convenient to use for testing: if you force it to bypass the limits, you can always give yourself hundreds of shots of ammo.

Sign In or Register to comment.