Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

How else can I code this?

No access to API, wouldn't be against paying for it, but learning curve is too steep, so staying with free resources even though I do have Plus membership. System is the FFG 40k RPG, but this is more of a roll-system question than a system-specific one.

I'm using the function where filling an identical query will pull the same answer through multiple times to establish a multi-hit damage ability. Essentially the first hit queries ?{Number of hits|1} while the second hit uses ?{Number of hits|1}-1 and the third uses ?{Number of hits|1}-2 and so on, then divide by a large number, round up, and use that to multiply damage results to zero out the ones that are needed. Thus whatever number you enter will become x1 for rolls that should apply, and x0 for ones that shouldn't.

Now, individual damage results are on d10s where a natural 10 is a critical that has additional effect but there is no critical failure, so the damage dice are d10cf0cs>10 or for the occasional weapon where 10s explode, d10!!cf0cs>10, and a few that can change the cs>10 to a different number if they have a larger critical range. Now, a critical that deals zero damage after target soak still gets a single point of chip damage through, so it's important to see which rolls are criticals.

This creates a problem, in that hits that are zeroed out because they aren't needed (they missed) can still generate a critical result, which I need a way to turn off. Now, a given hit can generate anywhere from 1-8 dice damage, plus several more for the Tearing quality where it drops the lowest results. Additionally, there's a quality called Proven where dice below a certain number are treated as a minimum value.

This is proving to be somewhat difficult to code, but I'm wondering if there's a way I can shorten things into fewer operations or fewer characters.

A single d10 with proven 4 would be {1d10cf0cs10, 0d0+4}k1 and having the ?{Number of hits|1} would be {[[(ceil((?{Total number of hits?|1})/100))]]d10cf0cs10, 0d0+4}k1. Okay, but what about when it's 2d10? 3d10? How about 6d10k4 The code bloats badly when the number of hits query needs to be duplicated onto each d10. While I can do {1d10cf0cs10, 1d10cf0cs10, 0d0+4, 0d0+4}k2 for 2 dice with proven 4, is there a way to turn off those d10s with fewer operations? Failing that, can anyone think of a lightweight string to make it visually obvious when a hit has been zeroed out rather than failing to exceed the target's soak without needing to read through the spaghetti code to see the dice results? I can't use critical failure as that's already earmarked for field save overload (which also zeros out a hit in the same way that the number of hits query does, only on a percentage chance instead of a query). I can't group the rolls together using the single sub roll example in the roll reference because proven is 0d0+X to force it to be a static number and I don't think you can mix single sub roll and multiple sub roll together.

Any thoughts?

November 13 (6 years ago)

Edited November 13 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

How are the numbers used? lets say you roll 3d10 and get 3, 7, 9. What does that do in the system?

Also, why do you need to have multiple hits (i think your other thread said up to 24)? Is that standard in WH40K? 

I will say if you switch to the API, you can probably get help in the Power Cards thread and solve it in a few days. You'll then be able to take advantage of things like being able to apply damage to character sheets automatically, without having to enter stuff manually.

November 13 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

Actually can you describe exactly what this system works like and what you hope to achieve with the macro. Don't describe how you've attempted tp do it, but imagine starting from a clean slate, and explaining to someone else how the system works and what the macro is supposed to do.

You might write something like (I;m inventing the numbers here, for illustration purposes):

  • The macro needs to roll up to 8 attacks
  • Each attack has a difficulty of 7, and criticals vary from 8-10 depending on weapon.
  • For each attack that lands, i need to roll 1-8 damage, and roll a location.
  • Damage dice are added together, and 10s roll again.
  • Targets have a soak rating that does what exactly

And so on, itemise the list detailing what you need.


Ultimately, I'm trying to automate things as much as possible.

First roll is to-hit, that's d100 below a target number, with every 10 points below counting as a degree of success. So, if you have total stat + modifiers of 75 and you roll a 43, that's 4 degrees of success because you get 1 for succeeding at all and another for every 10 below (effectively 75, 65, 55, and 45). Degrees of success trigger things like Razor Sharp (greater armour penetration if you get enough degrees of success), but are mostly for counting numbers of hits. Full auto weapons trigger 1 hit per degree of success, up to the weapon's maximum rate of fire, and there are qualities that do things like doubling your number of hits, or giving additional degrees of success on successful rolls. I have a macro that handles all of that just fine.

Hit location is determined by swapping the two dice on your to-hit roll and comparing to a different table. I know that can be automated via API, but without that I have to do it manually. That means that 43 to hit becomes a 34 on the location table, which is Torso.

Multiple hits follow a specific pattern based on where the first shot landed. In the case of a torso hit, that would be Torso, Torso, Arm, Head, Arm, Torso, with all further hits landing on the torso, in that order. There's no location chart for hits (although I would consider implementing one instead if there were an easy way to implement that, but without the API that's a no-go). As a result, I'm making a series of damage macros to automatically roll groups of hits all together, because automatic weapons are rather common.

As a result, what I have so far for each weapon (using excel to compile code chunks) is:

  • A to-hit macro that queries all of the common modifiers that adjust chances to hit, works properly, yields a total number of hits and location.
  • The to-hit macro whispers a set of [Target](~macro) buttons to the controlling player, allowing me to keep the 30 or so other macros hidden on the character sheet and only pop when needed. After the target chooses whether or not to dodge/parry (rapid melee attacks use the same mechanics), determining however many hits are left, the attacker can click one of the buttons from text to get the correct damage location set.
  • The damage macro rolls a set of hits, using the above ?{How many hits?|1} logic to zero out the extras and has them pre-sorted by location, spitting out a table that shows something like Head: 12 damage, Right Arm: 11 damage, Torso: 14, 17, 11 damage, and so on.

Damage has a ton of separate factors, but is ultimately an add/subtract system. An autocannon does 3d10+8 damage, penetration 6. Standard method adds the 3d10+8 for a result of 11-38. Target subtracts toughness (zero for vehicles, 3-8 for infantry and critters), and armour for the hit location from damage. Penetration lowers armour, but excess penetration once armour reaches zero is ignored. So a target with 4 armour hit by pen 6 effectively has zero armour against that attack. There's a ton more factors like that but they all act much the same way - Unnatural Toughness blocks damage but is negated by Felling, Daemonic blocks damage but is negated by Sanctified/Psychic damage, certain types of armour plating have a better value against particular damage types, etc, but they're all basically add/subtract with a fair amount of 'not-less-than-zero' logic, I can code all of those. The last chunk is to automate field saves, which are a percent chance that a given hit will be nullified by a protective field, which I'm also setting to zero out the damage when successful.

The problem I'm running into is that trying to have a single macro for damage (copied and pasted with hundreds of permutations and compiled in excel) is that Roll20 freaks out when a given formula is over 10,000 characters long, or takes more than 10 seconds to process (not sure which is triggering it). Ultimately, I need to trim my code down smaller to avoid that, which is why I'm dropping it to 8 hits at a time instead of 24, but that doesn't fix the underlying bloat issue. Needing to have the same query repeat once on every single d10 individually to kill the possibility of a critical success showing is wasting a ton of space, but given that someone with a particular talent firing an autocannon can easily have "3d10 where the results on each d10 that are below 4 become 4, 10s are critsuccess and there are no critfails" the best way I can think to code that is {d10cf0cs10, d10cf0cs10, d10cf0cs10,0d0+4, 0d0+4, 0d0+4}k3 which works fine until I need to have a way to turn off individual hits.

Yes, I could just have a separate macro to roll for each hit, but the result there is that a player with an automatic weapon that scores a ton of hits then needs to click damage, select target, and answer like a half dozen queries before he gets a damage roll. When an Avenger Gatling Cannon scoring 16 hits requires selecting the target and answering the same six queries 16 times but all of the six queries will have the same answer for that particular burst, that kinda defeats the point of automating it.

I'm avoiding the API not because I don't want to pay for it, but because I read the GitHub entries for things like PowerCards and it made my eyes cross. It already took a ton of effort just to learn the Roll20 dice language, I don't have the headspace to learn javascript, html, and css as well. I would happily pay for an API subscription if it would help me, hell I would happily pay someone just to code all of this for me if I could, as it's already eaten like 2 months of free time during which games have suffered hard while I've tried to optimize a combat system that is otherwise terribly clunky and cantankerous.

November 13 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

I see why you are having problems doing that with macros, you are pushing the macro system way beyond what its designed for.

I honestly dont think I'll be able to streamline your process there. Hopefully someone will be along that might have ideas.

However, I think I can demonstrate how much simpler doing this with the API would be than you are thinking. Don't be put off by Power cards, you jumped in way at the deep end looking at the code for that.

First, a misconception: you don't need to learn html and css to make a script. You only need to know javascript. You can output text from a script using rolltemplates if you want pretty text. 

Second, another misconception: you don't need to write a script to use an existing script, so if there's a way to do what you need with existing scripts, you can do it without learning any javascript.

I can think of ways of using some existing scripts to simplify your existing macros. You'd still use your macros, in combination with scripts, and get some extra benefits in the process (like having damage applied automatically to the relevant character sheets).

For instance, you said this:

Yes, I could just have a separate macro to roll for each hit, but the result there is that a player with an automatic weapon that scores a ton of hits then needs to click damage, select target, and answer like a half dozen queries before he gets a damage roll. 

Without writing any scripts yourself, you could have two macros: one for First Hit, which has a bunch of queries and then uses the script ChatSetAttr to save those details in hidden fields in a character sheet. Then a second macro could be used for Extra hits, and it doesn't ask any queries, it reads the values from the character sheet. 

With a custom script, those scripts could be combined into one, with the first query being "new Hit?" and if yes, it goes through the needed prompts, and if no, it does it automatically, and you could easily specify how many hits.

But even without a custom script, being able to save variables and reuse them in later macros would massively reduce the complexity of your macros.

If you want to investigate this, I can set up a campaign with the needed scripts and give you access, for us to test. I'll be back very late tonight if you want to discuss it. Just let me know what character sheet you're using.





Do I need the API in order to see API editing stuff in someone else's game? I was under the impression that while only one person in a given game needed Pro access to use API stuff, only the owner would be able to see/manipulate it, while the players would only see the results that the owner added to the game?

The character sheets I'm using are Dark Heresy: 2nd Edition and Black Crusade, both written by Daniel D. They have different editors since then that have made minor changes, but 99% of the attributes on the sheets are the same because the BC sheet was built using the DH2 sheet as a base.

I've been making lists of invisible attributes on the back end of these sheets to act as Boolean flags to turn individual effects on and off - example @{Selected|TalentMightyShot} set to a 0 if the character doesn't have the talent, and set to 1 if the character does, so that I can incorporate the mighty shot damage into all ranged damage macros and multiply that chunk by x0 or x1 to turn it off without needing to rewrite the macro if the character learns the talent at a later time. It's clunky, but it's a way around the lack of IF/THEN/ELSE support in the Roll20 dice system, which I have probably stretched be all semblance of sanity.

I initially looked into API access to see if I could edit character sheets to fix some of the over-simple math or add further functionality like querying for certain modifiers automatically, or to make nice looking roll templates that fill the needs of my games (the current one in both of those sheets doesn't allow more than title and one line with no column break), but when I looked at how to actually do that, I wouldn't make heads or tails of it, so I didn't bother.

I should be home about 6pm tonight, UTC-7 and awake for maybe 6 hours before I absolutely need to crash for work tomorrow morning. I would be delighted to see what can be done with the API that doesn't require extensive knowledge, because even the basic tutorials I've seen for the API make no sense to me (that or all of the tutorials I've seen are way out in the deep end). It doesn't help that 95% of the API stuff that I've found is for D&D5e, which has almost nothing in common with any of the systems that I run.

November 13 (6 years ago)
keithcurtis
Forum Champion
Marketplace Creator
API Scripter

Anyone who has been promoted to the GM of a given game can see and edit that game's API page.

Okay, I bit the bullet, I have a pro sub and API access now.

I puzzled out enough of the API to get PowerCards and ChatSetAttr from GitHub (which _sound_ useful for what I'm trying to do, but I could be way off base there), copied the raw script into the scripts section outside of the game, saved them, they _seem_ to be saved and available, and... nothing. I have no idea what I'm doing, and there doesn't seem to be anything new inside of the test game beyond an animated visual effects thing (which is nifty, but not what I'm looking for).

I assume the API stuff is entirely backend logic that allows Roll20 to watch the chat parser for certain other triggers and do things based on that, but I don't even know where to start with that. I already have the sneaking suspicion (rising dread?) that I'm going to have to throw out all of the macro work I've done so far and start over from scratch in effectively an entirely different language.

I've looked over the usual list of links that KeithCurtis posts in many API-related threads, the API: Introduction, API: Use-Guide, Script: PowerCards, etc stuff on the wiki (the link to Codecademy to learn Javascript is 404'd by the way). Manual searching did find Codecademy's intro to Javascript (I think the name changed), but that's a full 30 hours just for the basics.

Isn't there an API-for-Dummies guide or something, at least a page that puts some of the concepts into plain English that doesn't require someone to already have a background in programming to be able to use even the simplest stuff?

November 13 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter


Inquisitor Daedalus Cain said:

I assume the API stuff is entirely backend logic that allows Roll20 to watch the chat parser for certain other triggers and do things based on that, but I don't even know where to start with that. 

This is correct. Scripts respond to things written in chat, or changes to entities like tokens and character sheets.

Did you use the one-click installer for your scripts? If not, delete the scripts you have installed and re-install them using the one-click installer.

See here: http://blog.roll20.net/post/142306352590/update-45-api-one-click-install-new-api

This ensures that the scripts you install are automatically kept up to date. Plus, each script once installed has instructions on its page how to use it.


I already have the sneaking suspicion (rising dread?) that I'm going to have to throw out all of the macro work I've done so far and start over from scratch in effectively an entirely different language.
You could go this way, sure, and to be honest for the complexity you want, it might be best. 

But you dont have to, you can use chatsetattr and other scripts to work with macros, as in the example i mentioned in my last post.

I'll be back later.

Okay, I'm an API user now, and I've been looking through the various scripts. Javascript remains impenetrable to me, so writing this myself isn't possible, though I do have a friend that's fluent in Javascript who has been experimenting and sending me his code to run in a test game a chunk at a time to puzzle it out.

I've been looking at things like PowerCards and ChatSetAttr and seeing that there's a lot of functionality that I ultimately don't need included there, plus it seems that you can crash the API sandbox by running a PowerCards template without having an icon selected, which I really need to avoid.

Ultimately, the majority of what I'm trying to do I can do in the default template with the dice engine, I just need a way to have a simple if/then conditional to shut chunks of it off.

Example:

&{template:default} {{name=Character deals damage to Target}} - with the character and target using @{Selected| or @{Target| logic

{{Head=[[3d10+lengthy damage expression goes here}}

{{Torso=[[3d10+lengthy damage expression goes here}}

{{Right Arm=[[3d10+lengthy damage expression goes here}}


I just need a way to have the individual hits check if they should process or drop entirely, which will avoid all of the bloated number of hits macro stuff I was doing above to zero out the hits instead. That method works, but with the number of factors for each damage expression, the character limit becomes prohibitive. I need to be able to turn off individual hits (ideally hide the entire line) so that even if the dice roll on the back end, they don't show critical success or failure on the unneeded hits. PowerCards seems to be able to do this with [$atk] to set a particular roll into short-term memory, then later lines checking "If $ATK >=1, then [[Damage]]", then another line "If $ATK >=2, then [[Damage]]",and so on, but that comes with a ton of baggage since PowerCards was made primarily for formatting and presentation, not the conditional statements.

Is there an existing script that supports such simple IF-THEN-ELSE expressions without having tons of other features that are only going to decrease stability?

November 14 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

I dont think there is one, but it would be pretty easy to write.

You wouldn't write it like that.

lets say the script is called cain40k, your macro text would look something like

!cain40k --attacker|@{selected|character_id} --target|@{target|character_id} --head|[[complex roll here]] --torso|[[complex roll here]] --right arm|[[complex roll here]]

and so on.

Then in the script (I'll write a bare bones version later) would intercept those values, do what needs to be done with them, then end with a command like

sendChat(whoever, "&{template:default} {{name=Character deals damage to Target}} {{rest of roll template here}}");

All the rolls are done by the roll20 engine in the !cain40k line above, and the script then uses them to build the needed output string.

It would help to know exactly how the roll works though, and all the inputs you need and what should be done with them.

Can you type out in pseudocode what you need to happen?

Overview of a single damage roll:
((DamageDice + DamageStatic) - (Toughness + {(Unnatural - Felling), 0}kh1 + (Daemonic * (1 - Sanctified)) + {(Armour + Cover + Machine + Plating) - (Penetration + (Warp * (1 - Warded) * 1000)), 0}kh1)) * (FieldSave)

Actual damage:
DamageDice - set of d10s added together, usually 1d10 to maybe 4d10 at most
DamageStatic - flat bonus added to the d10s, ranging from -3 to perhaps +20

Soak: (subtracts from damage, cannot reduce damage below zero)
Toughness - value ranging from 0 to perhaps 8, subtracts from damage
Unnatural - like Toughness, but can be reduced by Felling
Felling - reduces Unnatural but not regular Toughness
Daemonic - like Toughness, but can be deactivated entirely by holy weapons
Sanctified - holy damage makes any Daemonic value count as zero
Armour - location-based, subtracts from damage
Cover - like armour, but by surroundings
Machine - like armour, but from cybernetics
Plating - modifications to armour that trigger against certain damage types
Penetration - reduces all armour types, not below zero
Warp - counts any armour that isn't warded as zero
Warded - makes the warp quality do nothing, I'm shortcutting by making Warp just count as 1000 pen which is functionally the same
FieldSave - percent chance that the incoming hit will deal zero damage

Important factors:
Damage dice can sometimes be d5s, which are functionally d10s divided by 2
Damage dice cannot crit-fail, so no red results
Damage dice that roll a natural 10 (sometimes other numbers) trigger special effects, even when the attack would deal no damage, so green results are important
Damage dice can have a Proven or Primitive rating - Proven is a minimum possible result, so Proven 3 counts results of 1 or 2 as 3, effectively making a d10 have the sides 3, 3, 3, 4, 5, 6, 7, 8, 9, 10, Primitive does the same for a maximum value instead
Field Save has no crit-success state (so no green results), but has a chance of overload (so red results are important)
For this reason, simply multiplying damage by zero to zero out a hit isn't acceptable - the red and green critical effects still show, and have to be manually checked, which comes up overly often when rapid-fire weapons are involved

I have written expressions that can take all of these factors into account. The only part I can't factor is zeroing out the unneeded hits - they need to not trigger critical success/fail results but I can leave them as zeroes if needed. While I _can_ write further expressions to turn off the hits, the expression is around 30 characters long and needs to be applied to every single dice roll within that single damage expression, plus once for the static modifiers to ensure that the dice themselves don't roll, which bloats the code to unreasonable levels.

Hitting a target:

Need a query that asks total number of hits, outputs a template that shows something like
&{template:Default}
{{Name=@{Selected|Character_Name} attains ?{Number of Hits?|1} on target @{Target|Character_Name} with his @{Selected|PrimaryWeaponName}! }}
Then with the hits sorted in further entries. Power Cards could work for this too, but the syntax would be different.

Hit location has to be manually determined anyway - I can't tie the to-hit roll and damage roll together because the target needs to be able to attempt a dodge/parry on incoming hits before knowing their damage, which will reduce the number of hits by the degrees of success on that evasion test. The actual hit location is taken by flipping the digits on the original to-hit roll (percentile system) and consulting a table with 6 results, but players can do that manually anyway, or I can use a rollable table or similar logic to get around it regardless.

The actual damage results are different only based on how much soak a location has. Multiple hits follow a specific progression based on where the first hit landed. Examples
Head, Head, Arm, Torso, Arm, Torso, with all remaining hits striking the torso
Torso, Torso, Arm, Head, Arm, Torso, with all remaining hits striking the torso
Arm, Arm, Torso, Head, Torso, Arm, with all remaining hits striking the arm
Leg, Leg, Torso, Arm, Head, Torso, with all remaining hits striking the torso

A weapon with a very high rate of fire may hit as many as 24 times. Even with the weird location order, I can still sort the hits to put locations together with different methods. The result is something like the following:
&{template:Default}
{{Name=@{Selected|Character_Name} attains ?{Number of Hits?|1} on target @{Target|Character_Name} with his @{Selected|PrimaryWeaponName}! }}
{{Head=[[hit 5]]}}
{{Right Arm=[[hit 4]]}}
{{Torso=[[hit 3]], [[hit 6]], [[hit 7]], [[hit 8]], etc}}
{{Right Leg=[[hit 1]], [[hit 2]]}}

Then I end up needing one macro for each location combination, which ends up being a lot but not impossible if I use self-whisper with the hit locations that list them like [Head](~HeadDamage), [Torso](~TorsoDamage), etc

Ideally, I would love to have some method of using a Boolean 1 or 0 to have entire segments of code removed before the roll engine calculates them. Something like:
If X=0, then %% This all gets hidden because it's between triggering symbols %% then continues running after that, and if X=1, then the %% is ignored (or whatever symbol you want as a trigger. Doesn't even need to be more complicated than that, because I can always use the roll engine with division and rounding operations to turn a complex value into a 1 or 0 flag. While it would be nice to have the system remember a roll and apply it further down, I have ways around that with just the dice system and query system too.

November 15 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

Assuming you were playing this at the table and not using roll20, what's the total number of rolls needed? 24 attack rolls, and 24 damage rolls? (Each with their relevant modifiers of course)

I'll look through this in more detail a little later, but a couple of things jumped out at me:

Hit location has to be manually determined anyway - I can't tie the to-hit roll and damage roll together because the target needs to be able to attempt a dodge/parry on incoming hits before knowing their damage, which will reduce the number of hits by the degrees of success on that evasion test. 

When using a script, you aren't limited to the macro's limitations. You can separate the roll and modifiers, and combine them in the script, so the script could easily determine locations automatically. Likewise the pattern for multiple hits can be automatically calculated.

And scripts can use loops, handling multiple hits vs the same location very easily.

Ideally, I would love to have some method of using a Boolean 1 or 0 to have entire segments of code removed before the roll engine calculates them. Something like:
If X=0, then %% This all gets hidden because it's between triggering symbols %% then continues running after that, and if X=1, then the %% is ignored (or whatever symbol you want as a trigger. Doesn't even need to be more complicated than that, because I can always use the roll engine with division and rounding operations to turn a complex value into a 1 or 0 flag. While it would be nice to have the system remember a roll and apply it further down, I have ways around that with just the dice system and query system too.

In principle, this should be easy to do. It's exactly the kind of process that scripts excel at. You'll be able to skip completely the need to use the roll ending with division to turn values into 1 or 0. 

The tricky bit is for you to communicate to me exactly the specific steps needed. I can't program this without knowing exact details.


Some thoughts on how to proceed

I'm thinking the best way might be something like this:

Your macro sends a list of dice rolls and parameters to the script, like 

!cain40k --attacker --target --attackrolls (a list of up to 24 attack rolls, however many are needed) 
--flags (a list of which flags that apply on this attack or damage roll) 
--damage (the number of dice to roll - not actually rolled here)

then the script takes the attack rolls, determine how many hit, and where each hit lands, sorting them into location groups.

(Note: it is certainly possible to just say "roll x attacks" and have the script roll them all without ever using the roll20's roll engine, which simplifies the script but it's also less transparent on what's going on. It may turn out to be simpler, though - I'm still theorising at this point.)

Then  an output string is built using the damage roll and flags to build the correct damage string, and printed to chat like

&{template:Default}
{{Name=character (already calculated) attains X hits on target (name) with his @{Selected|PrimaryWeaponName}! }}
{{Head (5 Hits)=[[damage string 1]], [[damage string 2]], [[damage string 3]], [[damage string 4]], [[damage string 5]]}} {{Right Arm (4 Hits)=etc}}

and so on

It could be taken a step further, by having the damage rolls predone in that first call, and analyse them in the script to apply flags like 

!cain40k --attacker --target --attackrolls (a list of attack rolls, however many are needed) 
--flags (a list of which flags that apply on this attack or damage roll) 
--damage (some damage rolls, however many might be needed, without any special flags applied)

And then the script can apply the flags to each damage roll, and determines the actual damage result, outputting it as above but also applying it to the target character's sheet. Things like Proven and Primitive would be very easy to handle this way.

Regarding your flags:

DamageDice - set of d10s added together, usually 1d10 to maybe 4d10 at most
DamageStatic - flat bonus added to the d10s, ranging from -3 to perhaps +20

Soak: (subtracts from damage, cannot reduce damage below zero)
Toughness - value ranging from 0 to perhaps 8, subtracts from damage
Unnatural - like Toughness, but can be reduced by Felling
Felling - reduces Unnatural but not regular Toughness
Daemonic - like Toughness, but can be deactivated entirely by holy weapons
Sanctified - holy damage makes any Daemonic value count as zero
Armour - location-based, subtracts from damage
Cover - like armour, but by surroundings
Machine - like armour, but from cybernetics
Plating - modifications to armour that trigger against certain damage types
Penetration - reduces all armour types, not below zero
Warp - counts any armour that isn't warded as zero
Warded - makes the warp quality do nothing, I'm shortcutting by making Warp just count as 1000 pen which is functionally the same
FieldSave - percent chance that the incoming hit will deal zero damage

Are these manually applied to each attack, or are they encoded in a character sheet or weapon stat list?

You might be able to collect these automatically in the script without needing to manually declare them. It's a good idea to split them into two lists, those that need to be declared manually, and those that can be grabbed from the character sheet automatically.


November 15 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

Also

Damage dice that roll a natural 10 (sometimes other numbers) trigger special effects, even when the attack would deal no damage, so green results are important

This is another strength of scripting. If your attack roll flags the possible special effects, when they occur the script can list them, you don't have to rely on spotting the green colour.

It's actually only 1 to-hit roll, with degrees of success determining the number of hits and reversing the digits determining the location. The damage rolls are separate though, but they fall into a handful of predictable patterns which all map back to the original location hit.

The reason why it's easier to just do it manually is that there's two opposing decisions between steps:

1. Attacker rolls to hit, which is a d100 rolling equal or under a target value based on skill and a laundry list of circumstances. Natural 96+ is usually a weapon jam or auto-miss (depends on the weapon), over the target number is a miss, under the target number is a hit, and every 10 under the target number is an additional degree. Target numbers can easily be over 100 themselves, resulting in near-automatic hits where the only way to miss is to jam the gun. Single-shot weapons just hit once, semi-auto hits on odd degrees, full-auto hits on all degrees, plus other mechanics can do things like allowing something an additional degree if at least X degrees are rolled, or hitting more than once on a given degree. For the sake of argument, the fastest shooting weapons hit twice for each degree, up to around 12 degrees. So, if your target number is about 140 (possible but takes some munchkin), and you roll a 15, that's 13 degrees (calculated as 140, 130, 120, 100, 90, 80, 70, 60, 50, 40, 30, 20, but not 10). With a certain Avenger Gatling Cannon that scores 2 hits per degree up to the max rate of fire, that's all 24 hits. I have non-script macros that handle this handily.

2. Defender chooses whether or not to evade. Most units only have 1 or 2 dodges/parries per turn, but often get hit with lots more fire than that, leading to the tactical decision of whether or not to dodge incoming attacks based only on how many degrees of success, how many hits, predicted location, and what kind of weapon, but before damage has been rolled. This is another d100 roll-under and count degrees test. Each degree typically avoids one incoming hit, sometimes two. I have non-script macros that handle this as well.

3. Remaining hits roll damage, applying them to the target in a predictable order to the target's locations. Each one generates damage, and penetration, plus a series of usually static special abilities. Defender has no decisions, simply applying soak against the incoming damage based on the location hit, and qualities versus qualities that alter these numbers. Again, this can all be done with attribute calls in the dice language.

The main issue is that Step 3 will often require a lot of queries, which are the same answer for every hit, or are in a 100% predictable pattern after the first question. So Query 1 would be "How many hits" so it knows how many steps to progress along Query 2 which is "Which location was the first hit" and there's a predictable series of locations that result from it, Query 3 can be "What was the range on the original attack", Query 4 can be "How many degrees of success on the original attack" plus some miscellaneous ones like "Are you using X once-per-encounter ability for attacks this round" and so on.

Query 1 would be answered by comparing how many hits Step 1 generated minus how many hits were avoided by Step 2, but I don't see how to automate that without it rapidly becoming very complicated to script with minimal gain. Query 2 is determined entirely by the original roll in Step 1 with the digits flipped (unless it was a called shot which overrides location), but that requires storing a value temporarily, and is easy enough to reference at a glance. Query 3 is also asked as part of the original to-hit roll in Step 1 anyway, but again would require a stored value. Query 4 is the result of the Step 1 roll which no factors can adjust.

The special effects for natural 10s on damage dice are simple and can be spotted at a glance - if a natural 10 comes up and the attack did damage after soak, it's a 1d5 roll on a set of tables in the book. If a natural 10 comes up and the attack did not do damage after soak, then it gets 1 point of chip damage through anyway. This is why I can't just zero them out, because a zeroed out hit can still generate natural 10s which will look identical to criticals that failed to do enough damage to get past soak but qualify for getting a point of chip damage through.

Likewise a field save will typically be something like d100 against a target number, typically 20-50, with an overload number of 1-10. If the roll is over the target, the damage goes through normally. If it's under the target, the damage is blocked (which would zero it out, but should also kill any greens because you can't get chip damage through a successful field save), and if it's under the overload number, it blocks the hit while also deactivating the field (which would show as red so I can see crits and overloads at a glance). If hits are zeroed out to make them miss, the field can still generate an overload result, which is incorrect.

I'm sure you can see why the 40k damage system seriously needs automation in actual play, given the tons of dice rolls that often require no real input from the players, just pulling static attributes from character sheets. I can do most of that just fine with the macro language.

November 15 (6 years ago)

Edited November 15 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

So it sounds like what you need is a script take takes an initial hit location, number of hits, degree of success, damage rating, and then all the various tweaks that apply to damage. Something And the script then generates the output (number of hits vs each location, how much damage they do, etc). 

Is that about right?

Can you show a complete, typical, damage macro so i can get a better idea of what this looks like?

I think the logic is a little screwed up, but this was close to where I was working
This would be for the 4th hit in a sequence

[[({[[(ceil((?{Total number of hits?|1}-3)/48))]]d10cf0cs>10, 0d0+4, [[(ceil((?{Total number of hits?|1}-3)/48))]]d10cf0cs>10, 0d0+4, [[(ceil((?{Total number of hits?|1}-3)/48))]]d10cf0cs>10, 0d0+4}k3 + 10 [Base] + [[@{Selected|TalentMightyShot} * (ceil(((floor(@{Selected|BallisticSkill}/10)) + @{Selected|UnBS})/2))]] [Mighty Shot] - ((@{Target|FieldFlare}*@{Target|FieldActive})*((1-(floor((?{Range to target?|Melee (up to 5m), 5|Point Blank (up to 18m), 4|Short Range (up to 375m), 3|Medium Range (up to 750m), 1|Long Range (up to 1500m), 1|Extreme Range (up to 3000m), 1}-@{Target|FieldMelee})/5)))*10)) [Ionic Flare] - (((floor(@{Target|Toughness}/10)) + @{Target|FrenzyActive}) * (1 - @{Target|IsVehicle})) [Toughness] + {((@{Target|UnT} * (1 - @{Target|IsVehicle})) - 0),0}k1 [Unnatural Toughness vs Felling] + (@{Target|TraitDaemonic} * (1 - 0)) [Daemonic] + {((@{Target|ArmourHead} + @{Target|TraitMachine} + @{Target|TraitNaturalArmour} + (@{Target|ArmourMongerActive} * 2) + (@{Target|BreachingShieldActive} * @{Target|PlatingBreachingShield}) + (@{Target|AblativeHead} * 4) + (@{Target|PlatingDeflective}*4*@{Target|HasFaceplate}) + ((floor(?{Vertical Cover?|Hull Up/Fully Exposed,0|Hull Down/Legs Concealed,1|Turret Down/Torso Concealed,2|Hide Position,3}/2))*?{Cover AP value (Ignore if Hull Up/Fully Exposed)?|8})) * ({(1 - 0), @{Target|ArmourWarded}}kh1)) - (ceil((8 [Base] + [[@{Target|IsVehicle}*@{Selected|TalentTankHunter}*((floor(@{Selected|BallisticSkill}/10))+@{Selected|UnBS})]] [Tank Hunter]) * ((2 - @{Target|PlatingGrav})/2))),0d0}k1))*(ceil(((((1-((ceil(?{Range to target?|Melee (up to 5m), 5|Point Blank (up to 18m), 4|Short Range (up to 375m), 3|Medium Range (up to 750m), 1|Long Range (up to 1500m), 1|Extreme Range (up to 3000m), 1}/5))*(1 - @{Target|FieldMelee})))*@{Target|FieldActive}*[[(ceil((?{Total number of hits?|1}-3)/48))]]*@{Target|FieldRating})+1)-[[((1-((ceil(?{Range to target?|Melee (up to 5m), 5|Point Blank (up to 18m), 4|Short Range (up to 375m), 3|Medium Range (up to 750m), 1|Long Range (up to 1500m), 1|Extreme Range (up to 3000m), 1}/5))*(1 - @{Target|FieldMelee})))*@{Target|FieldActive}*[[(ceil((?{Total number of hits?|1}-3)/48))]])]]d100cs0cf<[[@{Target|FieldOverload}]])/100))*[[(ceil((?{Total number of hits?|1}-3)/48))]]]]

So, to take that apart without the clunky zeroing out logic

[[
({d10cf0cs>10, 0d0+4, d10cf0cs>10, 0d0+4, d10cf0cs>10, 0d0+4}k3
 + 10 [Base]
 + [[@{Selected|TalentMightyShot} * (ceil(((floor(@{Selected|BallisticSkill}/10)) + @{Selected|UnBS})/2))]] [Mighty Shot]
 - ((@{Target|FieldFlare}*@{Target|FieldActive})*((1-(floor((?{Range to target?|Melee (up to 5m), 5|Point Blank (up to 18m), 4|Short Range (up to 375m), 3|Medium Range (up to 750m), 1|Long Range (up to 1500m), 1|Extreme Range (up to 3000m), 1}-@{Target|FieldMelee})/5)))*10)) [Ionic Flare]
 - (((floor(@{Target|Toughness}/10)) + @{Target|FrenzyActive}) * (1 - @{Target|IsVehicle})) [Toughness]
 + {((@{Target|UnT} * (1 - @{Target|IsVehicle})) - 0),0}k1 [Unnatural Toughness vs Felling]
 + (@{Target|TraitDaemonic} * (1 - 0)) [Daemonic]
 + {((@{Target|ArmourHead} + @{Target|TraitMachine} + @{Target|TraitNaturalArmour} + (@{Target|ArmourMongerActive} * 2) + (@{Target|BreachingShieldActive} * @{Target|PlatingBreachingShield}) + (@{Target|AblativeHead} * 4) + (@{Target|PlatingDeflective}*4*@{Target|HasFaceplate}) + ((floor(?{Vertical Cover?|Hull Up/Fully Exposed,0|Hull Down/Legs Concealed,1|Turret Down/Torso Concealed,2|Hide Position,3}/2))*?{Cover AP value (Ignore if Hull Up/Fully Exposed)?|8})) * ({(1 - 0), @{Target|ArmourWarded}}kh1))
 - (ceil((8 [Base] + [[@{Target|IsVehicle}*@{Selected|TalentTankHunter}*((floor(@{Selected|BallisticSkill}/10))+@{Selected|UnBS})]] [Tank Hunter]) * ((2 - @{Target|PlatingGrav})/2))),0d0}k1))*(ceil(((((1-((ceil(?{Range to target?|Melee (up to 5m), 5|Point Blank (up to 18m), 4|Short Range (up to 375m), 3|Medium Range (up to 750m), 1|Long Range (up to 1500m), 1|Extreme Range (up to 3000m), 1}/5))*(1 - @{Target|FieldMelee})))*@{Target|FieldActive}*[[(ceil((?{Total number of hits?|1}-3)/48))]]*@{Target|FieldRating})+1)-[[((1-((ceil(?{Range to target?|Melee (up to 5m), 5|Point Blank (up to 18m), 4|Short Range (up to 375m), 3|Medium Range (up to 750m), 1|Long Range (up to 1500m), 1|Extreme Range (up to 3000m), 1}/5))*(1 - @{Target|FieldMelee})))*@{Target|FieldActive})]]d100cs0cf<[[@{Target|FieldOverload}]])/100))
]]

That's giving 3d10 with zero crit-fail chance and crit success on 10, but where each individual die cannot have lower than 4
Plus 10 static modifier
Plus half your BS attribute if you have the Mighty Shot talent (zeroed out if you don't)
Minus 5 if the target has an ionic flare shield that's active, which can be turned off if the shield doesn't block melee-ranged shots
Then the soak portion
Target toughness (zeroed out for vehicles)
plus unnatural toughness (reduced by felling, but this weapon doesn't have felling, so that element is missing)
plus armour from the location and facing, machine trait, natural armour, whether the target has the armour monger talent, whether the target has a breaching shield and how much that breaching shield blocks, whether the target has ablative armour added, whether the target has deflective plating (which doesn't apply if the faceplate is blown off), cover (which has a value and a check based on location as to whether hull down or turret down applies it and how)
Minus penetration, calculated from
Base pen
Tank hunter talent adding BS bonus if the target is a vehicle
Halving pen if the target has grav plating
Using the same range query to trigger whether the field save applies, which means most of that range query is irrelevant
If the field save applies, it tests a d100 and zeros out the damage if successful, and shows a crit fail if it overloads

I'm fairly certain there may be some order of operations errors in there as this hasn't been debugged yet, but that should give an idea of how many factors I'm including and roughly how they fit together. Running that formula a dozen times taxes the Roll20 parser, but adding [[(ceil((?{Total number of hits?|1}-3)/48))]] onto every since dice roll to turn it on or off bloats it to uselessness, thus why I began this adventure which has now taken up perhaps 3 months of my life trying to code.

November 15 (6 years ago)

Edited November 15 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

Thanks, that's very informative.  And omg i would not like to have to calculate all that without automation!

It looks like the queries needed are

  • Number of hits
  • Location of First Hit
  • Degrees of success of first hit
  • Range to target
  • Vertical Cover
  • Cover AP value

Are all the other variables grabbed from the character sheet? Any other queries needed?

I notice in there @{Target|ArmourHead}. Is this hit example specifically for the head? 

Which other variables are based on hit location? Do different locations all follow the same structure (just with different armour variable names), or are there differences depending on location?

Also for each damage roll, you have the initial numbers of d10 for damage. It looks like you also have a d100 for Field save. Are there any other dice rolls you'd make with each damage roll?


November 15 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

One final question tonight:

In your routine you have a cover calculation that requires two queries and only applies if target is in cover

((floor(?{Vertical Cover?|Hull Up/Fully Exposed,0|Hull Down/Legs Concealed,1|Turret Down/Torso Concealed,2|Hide Position,3}/2))
*?{Cover AP value (Ignore if Hull Up/Fully Exposed)?|8})

It occurs to me you could use chatsetattr, and give players a macro to set when they are in cover, like this:

!setattr --silent --sel 
--coverModifier|?{Entering Cover?|Hull Up/Fully Exposed,0|Hull Down/Legs Concealed,1|Turret Down/Torso Concealed,2|Hide Position,3}

(All on one line)

That would create a coverModifier attribute on the character sheet, and set it with a value of 0 to 3

And so when making damage rolls, you only need to query the CoverAP value.

While this is only a tiny bit of streamlining for the damage macro, I'm mentioning this mainly so you can start thinking about other ways to use setAttr to simplify making changes to character stats. It's very powerful.

November 15 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

By the way, a little tip for shortening your macros:

You have this query:

?{Range to target?|Melee (up to 5m), 5|Point Blank (up to 18m), 4|Short Range (up to 375m), 3|Medium Range (up to 750m), 1|Long Range (up to 1500m), 1|Extreme Range (up to 3000m), 1}

It gets repeated at least once, probably many times.

For queries, you only need the full options the first time it appears in the code. Every time after that you just need the text up to the first |

So second and subsequent query calls, this should be

?{Range to target?}



November 15 (6 years ago)

Edited November 15 (6 years ago)
GiGs
Pro
Sheet Author
API Scripter

By the way, deciphering the damage macro is pretty tricky, can you break down how each of the following work?

Mighty Shot

 + [[@{Selected|TalentMightyShot} * ceil( (floor(@{Selected|BallisticSkill}/10) + @{Selected|UnBS})/2 )]] [Mighty Shot]
 Plus half your BS attribute if you have the Mighty Shot talent (zeroed out if you don't)

This says half BS if you have Mighty Shot. But it seems to be adding half UnBS to 1/10th BS. Is this right? Is uUnBS your ballistic skill score?

(I removed a bunch of what i think are redundant brackets, by the way)

Ionic Flare

Minus 5 if the target has an ionic flare shield that's active, which can be turned off if the shield doesn't block melee-ranged shots
- (@{Target|FieldFlare}*@{Target|FieldActive}*((1-(floor((?{Range to target?}-@{Target|FieldMelee})/5)))*10)) [Ionic Flare]

I have tried to parse this section, but the brackets are throwing me off. 

I assume the first two flags @{Target|FieldFlare}*@{Target|FieldActive} are both 0 or 1, so that the rest is either 0, or its calculated value. Is that correct?

I'm having trouble understanding the next bit:

((1-(floor((?{Range to target?}-@{Target|FieldMelee})/5)))*10)

Range to target is a number from 5 at close range to 1 at extreme range. What ranges can FieldMelee have?

If I am reading this expression properly, it suggests that if the total of (range - fieldmelee) = 5, the value = 10, otherwise it is zero. Is that correct?

The Armour Section:

+ {((@{Target|ArmourHead} + @{Target|TraitMachine} + @{Target|TraitNaturalArmour} + (@{Target|ArmourMongerActive} * 2) +
 (@{Target|BreachingShieldActive} * @{Target|PlatingBreachingShield}) + (@{Target|AblativeHead} * 4) + 
(@{Target|PlatingDeflective}*4*@{Target|HasFaceplate}) + 
((floor(?{Vertical Cover?|Hull Up/Fully Exposed,0|Hull Down/Legs Concealed,1|Turret Down/Torso Concealed,2|Hide Position,3}/2))*
?{Cover AP value (Ignore if Hull Up/Fully Exposed)?|8})) * ({(1 - 0), @{Target|ArmourWarded}}kh1))

Are the numbers 2, 4 fixed at those scores, or do they ever vary?

cover (which has a value and a check based on location as to whether hull down or turret down applies it and how)

Does the cover calculation differ from that shown, if you are hit in the chest, arms, or legs?

I'll have more questions later, probably :)


G G said:

Are all the other variables grabbed from the character sheet? Any other queries needed?

I notice in there @{Target|ArmourHead}. Is this hit example specifically for the head? 

Which other variables are based on hit location? Do different locations all follow the same structure (just with different armour variable names), or are there differences depending on location?

Also for each damage roll, you have the initial numbers of d10 for damage. It looks like you also have a d100 for Field save. Are there any other dice rolls you'd make with each damage roll?

Yes, I believe pretty much all of the others can be grabbed from the character sheet, although a Misc Modifier prompt is usually a good idea.

Yes, that hit was specifically for the head. List of locations depends on the type of target:

Character: Facing doesn't matter, locations are Head, Torso, Right Arm, Left Arm, Right Leg, Left Leg

Vehicle: Facing is more important than location, Facings are Front, Right Side, Left Side, Rear, but vehicles with a Turret have that as a possible location from each side. They technically have locations of Hull, Weapon Systems, and Motive Systems within each of those facings, but they're functionally identical until critical damage is suffered, and critical damage has to manually consult a series of charts for special effects anyway.

Horde: Facings and locations don't matter, hits mostly do 1 point per hit regardless of actual damage so long as they get past the horde's soak (which is virtually guaranteed in all but the weirdest circumstances), but special qualities like Blast can multiply how many points are inflicted.

Engine: Since this is for a giant-robot-centric game, I've had to house rule to add more support for them. Effectively this means a hybrid of character and vehicle, with an Engine having a Head, Torso Front/RightSide/LeftSide/Rear, Right Arm Front/Side/Rear, Left Arm F/S/R, Right Leg F/S/R, Left Side F/S/R for locations, which results in 17 possible locations on an Engine, but no more than 6 of them from a given facing

Facings are always broken into 90 degree quarters, front/sides/rear, where relevant.

Damage dice are almost always d10s, but there are a handful of weapons that do d5s, which are factored as a d10/2 rather than an actual d5 because a natural 10 still triggers the critical/chip damage effect (guess they didn't want to have double the crit chance on d5s).

Field saves are on a d100, but not all targets have a field save. There shouldn't be any further rolls beyond the damage dice and possible field save, although there are special effects such as flame weapons having a further save to check if the target catches on fire.


chatsetattr to set a state for being in cover isn't a bad idea. That could likewise be used to set a state for "ran last turn" given that running triggers adjustments to incoming attacks as well.


Yes, I know I only need ?{Range to Target?} for further checks, but I've been compiling in Excel, and since some queries don't always come up in the same order, if I shorten some of them, I occasionally get bugs where the list is missing because the first instance is a shortened one. While I can scrape those out, it's work intensive when going through about 2000 macros to cover all of the weapons and damages for each location across all of the PC's engines over 2 games (2 games, 7 PC engines each, ~6 average weapons per engine, ~4 average methods of fire per weapon, ~20 damage location possibilities = ~2500 total macros under the method I was using)


 + [[@{Selected|TalentMightyShot} * ceil( (floor(@{Selected|BallisticSkill}/10) + @{Selected|UnBS})/2 )]] [Mighty Shot]
 Plus half your BS attribute if you have the Mighty Shot talent (zeroed out if you don't)

This says half BS if you have Mighty Shot. But it seems to be adding half UnBS to 1/10th BS. Is this right? Is uUnBS your ballistic skill score?


Screwy notation. Ballistic skill is a percentile value, typically 25-70, but the talent wants the BS Bonus, which is the tens digit only. Unnatural Ballistic Skill is a special quality that adds directly to the BS Bonus, and thus is not divided. So a character might have 60 Ballistic Skill (and thus a BS Bonus of 6), but have a psychic power used on them that gives Unnatural BS 5 (pushing their BS Bonus up to 11), which the Mighty Shot talent would need to convert into 6 (half 11, round up). One iteration of the system says round up, another iteration says round down, but I'm just leaving it with rounding up.


Ionic Flare

Minus 5 if the target has an ionic flare shield that's active, which can be turned off if the shield doesn't block melee-ranged shots
- (@{Target|FieldFlare}*@{Target|FieldActive}*((1-(floor((?{Range to target?}-@{Target|FieldMelee})/5)))*10)) [Ionic Flare]

I have tried to parse this section, but the brackets are throwing me off. 

I assume the first two flags @{Target|FieldFlare}*@{Target|FieldActive} are both 0 or 1, so that the rest is either 0, or its calculated value. Is that correct?

I'm having trouble understanding the next bit:

((1-(floor((?{Range to target?}-@{Target|FieldMelee})/5)))*10)

Range to target is a number from 5 at close range to 1 at extreme range. What ranges can FieldMelee have?

If I am reading this expression properly, it suggests that if the total of (range - fieldmelee) = 5, the value = 10, otherwise it is zero. Is that correct?


Ionic Flare Fields are tough to write. I'm trying to keep the number of queries to a minimum, thus why ?{Range to Target?} is being divided and rounded down. An Ionic Flare Field is supposed to give -5 to incoming damage, or -10 if that damage has the Blast or Spray quality, but no effect if the field is disabled or if the attack is from within melee distance. The field can be modified to still apply to attacks within melee distance though. However, there are other weapons that trigger at short or long ranges, which is why the Range to Target query is a set of arbitrary numbers and a rounding. Scatter gives extra damage at point blank only (so range query /5 round down so that it only yields 1 at point blank range), but gives a bonus to hit at Point Blank or Short (so same query +1, /5, round down, so that it yields 1 at PB and Short only), and so on. This particular weapon doesn't have all of those qualities, but the system was built to compile for any weapon, which I know will result in dead or misleading code in some areas like this.


The Armour Section:

Are the numbers 2, 4 fixed at those scores, or do they ever vary?


They're specific mods. The ablative armour is a yes/no check - if you have ablative armour, it's set to yes on every location, but when you suffer a single critical of damage, that plate is blasted off of that location and the +4 bonus is lost.

The +2 for deflective applies only to weapons that deal explosive or rending damage types (which this example cannon does), and is another yes/no check. There's a similar one that only applies to impact damage, and another that applies certain effects to energy damage which I would need to look up.


Does the cover calculation differ from that shown, if you are hit in the chest, arms, or legs?


The cover is a simplification because I couldn't think of a better way to handle cover applying to different locations. The system officially says that you can have any/all locations behind cover in any sensible combination, and that cover will add a value to your armour to represent bullets needing to pass through it before they hit you. My attempt to simply that and not have a dozen queries that all require asking the GM "Which locations are behind cover? How much does it count?" was to create a few states - hull down where it only applied to your lower locations, turret down where it applied to all locations and you just had some guns sticking out, a hide position where you're dug so far in that the bottom part would get double value (handled by a divisor and forced round up/down by location). I couldn't think of a better way to handle that without tons and tons of queries to slow everything down.

Additionally, cover is supposed to be reduced by 1 point each time an attack passes through it, representing cover slowly degrading as it accumulates holes, but even with ChatSetAttr that seems like it would be a beast to apply.


I'll answer any other questions as I can. I have a friend who knows javascript experimenting to see how to build similar things, though he's spent the last 2 days just poking the system with calls and experiments to see how things connect and interact. He claims things will come together quickly and easily once I gets it all worked out, but we'll see (he has several questions in the API forum under the name Shin)