Sunday 6 November 2016

A Shadowrun 5e setup for Roll20

I recently started a game of Shadowrun 5e on Roll20 with a group of friends. I wanted to make use of as much of my previous experience with the API and the various scripts I have learned to use. SR5e however is a difficult game to adapt to Roll20 because it has a number of major difference that vary it away from the major game systems.
Some of the major changes that required work were:
  • The initiative system – a variable value generated every turn, with variable multiple action phases during each turn. 
  • A movement rate that is expended during a turn, over one or more actions, that adjusts from walking to running to sprinting as you accumulate movement.
  • A pool based roll resolution system, with limiters and exploders.
    Initially I entered the game with a simpler ambition than what I ended up with. It got more complicated due to The Aaron (Group-Init) and DarKDinDoN (MovementTracker), who responded to my requests for help by modifying their scripts and giving me copies to test, with specific settings for Shadowrun. IE they gave me what I asked for, the bastids. Some of these changes are still working their way through the Roll20 system.
    I am truly thankful for their help, even to the point of going into my games and playing with my setup to try to iron out issues.

    The other really useful tool is the SR5 character sheet template: Authors:Gerdofal - Chad on Roll20. Based on a sheet by Neirin D. and JoeIngledew.

    It took me a while to get used to this sheet, and a bit of effort to get parts of it working (mostly my own fault for not filling ALL fields, like it tells you to). But I am now very happy with it, if only my players would fill it out correctly.

    Scripts
    So the following is a list of all the scripts I have loaded. I will group them into two groups – essential ones for SR, and others.

    @ indicates scripts available through the Library… but I may have an early release.

    Essential:
      Optionals:
        The following system scripts are also used, but should be loaded as a result on one of the above:
        • VectorMath @
        • MatrixMath @
        • PathMath @
        • TokenCollisions @
        • IsGM @
        • HTMLBuilder @

          GroupInitiative (v0.9.21) + TurnMarker.
          Essential to controlling the SR turn order. Then Aaron added a whole bunch of features to this shortly after I spoke to him about using it for SR. I’m not sure if he had them in the works already or if my questions prompted them but they are great.
          Setting it up:
          Note: this uses attributes from the Character Sheet, which must be loaded and created. Players can generate their initiative scores from the character sheet itself, but it is much easier to generate a whole bunch of player+npc scores at once using this script. Not to mention the other advantages that will be listed below.
          !group-init –add-group –bare|reaction|current –bare|intuition|current –bare|initiativemisc|current –tiebreaker|reaction|current
          !group-init –del-group 1 (remove the default group it comes set to).
          You need to set the following buttons from the menu:
          Roller = Individual-Roll
          Sort options = Descending
          Initiative Die size = 6
          Initiative Dice Count =0
          Dice count Attribute = initiativedice
          Max decimal places = 2
          Auto Open Init = On
          Replace Roll = On
          Announcer = Partial.

          Once you have set that up you can create the following buttons:

          ClearInit: 
          • !group-init –clear
          SetInit: 
          • !group-init
          Sort: 
          • !group-init –sort
          Modify5-10: 
          • [[?{SELECT TOKEN!!!|5,5|10,10} &{tracker:-}]]
          End of Action: 
          • !group-init --adjust -10
          End of Turn: 
          • !group-init --reroll
          • !group-init --sort
          • !mt reset-turn
            Most of these should be obvious, but some notes: Modify5-10 is using the Tracker option because the option in Group-Init only works on the current active token. Aaron is working on adding an option to apply it to a selected token. The mod5-10 is needed to tokens that take interrupt actions. Be careful however not to fall for the trap of re-Sorting after Modifying, you need to adjust their position manually, because a Sort will re-sort the entire list, including g those that have already acted.
            As End of Actions are performed init values will eventually drop below 0, any token with a 0 or negative score does not get a turn and is skipped. 
            End of Turn rerolls everyones init (I don’t keep the same scores, but if you wanted to you can use the ‘stack’ features), sorts them, them the MoveTracker status is reset to allow new movement, see below.

            MoveTracker v0.1.0
            Use the menu system buttons to turn Tracker ON, strict mode ON and the important one SR Mode ON. 
            The SR mode turns move tracking resets due to an “eot” event (a players eot) off and allows move tracker to reduce progressively through the actions of a turn. 
            You can set two rings for movement limits, Walking and Running. To do this click the ADD button and add a RUN option with Agility *4, with Constraint ON. Then set a WALK option with Agility*2, with Constraint OFF.
            MT will constrain movement to these limits unless you free them up by using the Move-Constraint option.
            In my game I give the players access to a Move button with !MT on it, which allows them to access the MT Menu system for players. Options include taking moves back, but in my game I verbally tell players to not drop their tokens unless they are sure, because their vision will be updated.
            For the GM I have 3 buttons:
            Move-reset:
            • !mt reset-turn
            Move-constraint:
            • !mt toggle-turn-constraints
            Move:
            • !mt
            The reset is simply there incase something unusual happens. The constraints is for tokens that wish to SPRINT, a third class of movement in SR, which requires a skill roll and is variable in its results.
            MT is a really nice tool, but it does have a few short comings:
            The area effect layer it applies can be annoying at times, covering things. It can also, at rare times, get in the way of selecting things if the layering is not quite right. This doesn’t happen very often and I have yet to pin down under what conditions it occurs. 
            The area effect layer can also be less effective if you are using dynamic lighting and you have a very tight field of view… like in a group of offices with walls everywhere.
            I would like for it to have a ring lines rather than an area of effect colour, as an optional.

            TurnMarker.
            TM tracks the current token at the top of the init order. It places a nice graphic under it and if you turn the AUTOPULL option ON it will re-center on that token. Its one failing is that it doesn’t ‘select’ that token (I believe there is an PAI issue for why that doesn’t happen).
            There are a bunch of announcement options you can use so that message appear in chat when a token has finished, or starts its turn. This connects nicely with StatusTracker, which will display Status marker messages at the same time for the new token.
            One thing you have to remember with TurnMark is that it is linked to the current Page Focus, the flag you move around on your pages that determine what players see. 
            I have had occasions where the TM icon/image gets lost or confused, especially with MT. Most of this confusion will be resolved by simply moving the current token one square. I have other occasions where the icon/image seems to vanish, or just not deploy at all. I strongly suspect that this is my problem, I forgot to do something, or I did something wrong, but I haven’t worked out what it is that I did, yet.
            The TM icon can be cleared from screen by closing the Init Tracker, or clearing all tokens from the InitTracker (using the Clear button).

            TokenNameNumber.
            The purpose of this script is simply to add a number to the end of any token you deploy from the Journal. This allows you to deploy multiple copies of a token so it appears as Token 1, Token 2, Token 3 etc. It does this automatically, and adjusts for numbers currently in use, or removed, from the table.
            There is a process to setting it up correctly however, and as I don’t like RTFM it took me a while to get it right. But it is worth doing.
            What I use it for is to create a Journal entry for a character/creature, assign values and images to it (using the SR character sheet or manually creating your own as I will describe later).
            Once you have a token for your Journal, open it and assign the Journal to it (Represents Character). Then add %%NUMBERED%% to what ever Name you assign it, so Wolf would become Wolf %%NUMBERED%%. Save the token. Open the Journal and edit it, remove the current token image (Default Token) and assign the new one. Close. Deploy a new token from the Journal to the table and it will be renumbered. You can delete the token with the %%NUMBERED%% still showing.
            The advantage of this, assuming you do NOT bind any attributes to the token, is that you can setup a single Journal for a monster and then deploy multiple copies of it. Further, if you assign values to Bar1,2,3 before you save it to the journal above, it will deploy with those values, ready to use. Alternatively you can use a macro to quickly assign values, as I will show below. For my purposes I use Bar 1=HPs (single track only for NPCs), Bar2=Ammo and Bar3=armour value. How I use these, and why, I explain below.

            TokenMod, GSAttk, ChatSetAttr, Reset.
            Each of these modify Attributes in a Journal in some way. Each of them is good for doing a few things that I wanted, but each of them seems to have a hole in some area, mainly to do with handling current vs max attribute values. It is actually very likely that I am simply missing something and I don’t really need all of them, but that’s the way it worked out, I am happy to be educated.
            Reset: is a collection of small programs that someone wrote, which I have hatcheted into various forms at times. Simple one use functions.
            The main places I use them is with four buttons used for setting up NPCs and player tokens. So I will just go through what each does.
            Template: a macro for apply attributes to an NPC token. Rather than use the full character sheet I only need a few values for NPCs, so I create them with this script, and the following ones. I probably don’t even need this many values, old habits.

            Template.
            !gsa -c @{selected|character_id} {{
            --body|?{body|3}
            --agility|?{Agility|3}
            --reaction|?{Reaction|3}
            --strength|?{strength|3}
            --willpower|?{willpower|3}
            --logic|?{logic|3}
            --intuition|?{Intuition|3}
            --charisma|?{charisma|3}
            --initiativemisc|?{initiativemisc|0}
            --initiativedice|?{initiativedice|1}
            --matrixinitiativemisc|?{matrixinitiativemisc|0}
            --matrixinitiativestat|?{matrixinitiativestat|7}
            --matrixinitiativedice|?{matrixinitiativedice|1}
            --astralinitiativemisc|?{astralinitiativemisc|0}
            --astralinitiativedice|?{astralinitiativedice|3}
            --npcphysical|?{npcphysical|10}
            --magic|?{magic|6}
            --skills|?{skills|3}
            --armour|?{armour|9}
            }}
            !setattr --sel --Ammo|10|10
            !token-mod --set bar1_link|npcphysical bar2_link|Ammo bar3_link|armour
            !setbars
            !token-mod --set bar1_link| bar2_link| bar3_link|

            QuikTemplate: (no prompting)
            !gsa -c @{selected|character_id} {{
            --body|3
            --agility|3
            --reaction|3
            --strength|3
            --willpower|3
            --logic|3
            --intuition|3
            --charisma|3
            --initiativemisc|0
            --initiativedice|1
            --matrixinitiativemisc|0
            --matrixinitiativestat|7
            --matrixinitiativedice|1
            --astralinitiativemisc|0
            --astralinitiativedice|3
            --npcphysical|10
            --magic|6
            --skills|3
            --armour|9
            }}
            !setattr --sel --Ammo|10|10
            !token-mod --set bar1_link|npcphysical bar2_link|Ammo bar3_link|armour
            !setbars
            !token-mod --set bar1_link| bar2_link| bar3_link|

            SetNPCBars (for resetting tokens quickly).

            !token-mod --set bar1_link|npcphysical bar2_link|Ammo bar3_link|armour
            !setbars
            !token-mod --set bar1_link| bar2_link| bar3_link|

            The thing that you might wonder about is the !setattr command. As far as I can tell tokenmod doesn’t set MAX values for attributes… so I use setattr to put a max value into ammo, so I can reload it if need be (see Ammo later).
            The rest of the optional scripts should be reasonably self evident, and rather than go through and explain all about them (follow the links if you want to know), I will instead go through what other buttons I have set up and what they do.

            GM Buttons.


            NPC-Attack:
            !power {{
            --format|atwill
            --name|@{selected|token_name} attacks.
            --Target:| ~R@{target|token_name}~R
            --AttackRoll:|~R[[ (@{selected|agility}+@{selected|skills} + ?{Mods|0} - [[floor(@{selected|bar1|max}-@{selected|bar1})/3) ]])d6s>5 ]] ~R
            }}
            !token-mod {{
            --set bar2_value|-1 
            --ids @{selected|token_id}
            }}

            The last bit reduces the NPCs Ammo value by 1. As a house rule we give NPCs 10 ammo and deduct 1 everytime they shoot, regardless of attack type or weapon ammo supply. Its just a simplification.

            NPCReload:

            !token-mod --set bar2_reset|

            NPC-Skill

            !power {{
            --format|atwill
            --name|@{selected|token_name}.
            --SkillRoll:|~R ?{Skill|Skill} [[( ?{Dice Pool|7} + ?{Mods|0} - [[floor(@{selected|bar1|max}-@{selected|bar1})/3]] )d6s>5]] ~R
            }}

            NPC-Avoid/Soak

            !power {{
            --format|atwill
            --name|@{selected|token_name} Avoidance roll
            --AvoidRoll:|~R[[(@{selected|reaction} + @{selected|intuition|0} + ?{ModAvoid|0} - [[floor(@{selected|bar1|max}-@{selected|bar1})/3) ]])d6s>5 ]] ~R
            }}
            !power {{
            --format|atwill
            --name|@{selected|token_name} Soak roll.
            --SoakRoll:|~R[[(@{selected|body} + @{selected|armour} +?{AP|0} - [[floor(@{selected|bar1|max}-@{selected|bar1})/3) ]])d6s>5 ]] ~R
            }}
            You have to be careful with this one, the results sometime come out not in the same order.

            Roll-Simple

            !power {{
            --format|atwill
            --name|@{selected|token_name} Simple Roll.
            --PoolRoll:|~RSuccesses:[[?{Dice Pool|0}d6s>5]]~R
            }}

            Mark-clear

            !mark-clear

            Bump and BumpCr8

            !bump-slave –push

            SFX

            /fx ?{Type|beam|bomb|breath|bubbling|burn|burst|explode|glow|missile|nova|splatter}-?{Colour|acid|blood|charm|death|fire|frost|holy|magic|slime|smoke|water} @{target|token_id}

            Fire-x

            /fx nova-fire @{target|token_id}
            Various sound effects, eg Shotgun
            !sfx song:Shotgun01 action:play unique:true volume:50
            !sfx song:Machinegun action:play unique:true volume:50

            The Player Bar.

            For players we try to track ammo…

            AdjustGunClip

            !ammo @{selected|token_id} ?{Gun name} ?{Adjust clip|0,0|-1,-1|-2,-2|-3,-3|-6,-6|-10,-10|-20,-20|1,1|5,5|10,10}

            ANewGun

            !setattr --sel --?{GunName}|?{Clip}|?{Clip}

            aReload

            !resetattr --sel --?{Gun name}
            Roll-Edge
            !power {{
            --format|atwill
            --name|@{selected|token_name} Simple Roll.
            --PoolRoll:|~RSuccesses:[[?{Dice Pool|0}d6!s>5]]~R
            }}

            SetLight

            !token-mod --set light_radius|?{light radius|20} light_dimradius|?{dim radius|5} light_angle|?{Angle|360} light_otherplayers|?{YesNo|yes}

            SetSight

            !token-mod --set light_multiplier|?{Light xMod|1} light_losangle|?{Angle|360} light_hassight|?{YesNo|yes}

            StatAdd

            !StatusAdd ?{Status name} ?{Status Duration, -1 for perm} ?{Status description} ?{Type GM for GM only} // ?{Status Indicator|Purple|Red|Blue|Green|Brown|Pink|Yellow|Skull|Broken-heart|broken-skull|death-zone }
            StatAll, StatClrAll, StatDel… should be easy to work out.

            Whisper

            /w ?{Target|gm|Storme|Jackson|Moonraker|Buzz|Brock|Bruce|Indigo|Kat|Solomon|Wolfric} ?{Message}




            MT active...



            PS If you are wondering how I have my macro bar over three lines: https://app.roll20.net/forum/post/476678/slug%7DNot perfect but simple and works.



            Reset script….no idea where I got it from sorry.
            ;on("chat:message", function(msg) {
                'use strict';
                // Exit if not an api command
                if (msg.type != "api") return;
            
            
                // Get the API Chat Command
                msg.who = msg.who.replace(" (GM)", "");
                msg.content = msg.content.replace("(GM) ", "");
                var command = msg.content.split(" ", 1);
            
            
                // Removes all status markers from selected tokens...
                // Usage: !clear
                if (command == "!clear") {
                    _.each(msg.selected, function(obj) {
                        var Token = getObj("graphic", obj._id);
                        var DefaultIcons = "";
                        if (Token.get("represents") !== "") {
                            DefaultIcons = findObjs({_type: "attribute", name: "DefaultIcons", _characterid: Token.get("represents")})[0];
                            DefaultIcons = (DefaultIcons !== undefined) ? DefaultIcons.get("current") : "";
                        }
                        Token.set("statusmarkers", DefaultIcons);
                    });
                }
            
            
                if (command == "!setbars") {
                    _.each(msg.selected, function(obj) {
                        // Get the Token and its max bar values...
                        var Token = getObj("graphic", obj._id);
                        var DefaultIcons = "";
                        var bar1value = Token.get("bar1_value")
                        var bar2value = Token.get("bar2_value")
                        var bar3value = Token.get("bar3_value")
            
            
                        // If the Token represents a character, check to see if the Token
                        // has any default icons to set. Usage should be markername@value
                        // Example: grenade@2
                        if (Token.get("represents") !== "") {
                            DefaultIcons = findObjs({_type: "attribute", name: "DefaultIcons", _characterid: Token.get("represents")})[0];
                            DefaultIcons = (DefaultIcons !== undefined) ? DefaultIcons.get("current") : "";
                        }
            
            
                        // Reset bar values and remove all status markers except those 
                        // marked as default icons...
                        //if (Bar1Max !== null) Token.set("bar1_value", Bar1Max);
                        Token.set("bar1_max", "");
                        Token.set("bar2_max", "");
                        Token.set("bar3_max", "");
                        Token.set("bar1_max", bar1value);
                        Token.set("bar2_max", bar2value);
                        Token.set("bar3_max", bar3value);
                        Token.set("statusmarkers", DefaultIcons);
                    });
                } 
            
            
            
            
                if (command == "!sort") {
                    if (msg.type !== 'api' || msg.content !== '!sort') return;
                    let order = Campaign().get('turnorder');
                    if (order.length) {
                        order = _.sortBy(JSON.parse(order), (t) => parseFloat(t.pr));
                        order.reverse();
                    }
                    Campaign().set('turnorder', JSON.stringify(order));
                } 
            
            
            });
            
            

            No comments:

            Post a Comment