#SWADE Targeted Damage

1 messages · Page 1 of 1 (latest)

latent lotus
#

@errant yarrow I'd love to hear more about what could make it smoother.

errant yarrow
#

Well, PF2e is the ulimate in terms of 'smoothness' imo

latent lotus
#

Of course. Just wanted to see what might be feasible

errant yarrow
#

In that its:
Target enemy, press attack roll button
Module then automatically applies modifiers, range, blinded, etc, and outputs the result.
Module then also does the damage roll on the targeted enemy.

Currently, with raise calculator and targeted damage, its:
Target enemy, roll attack roll
Open raise calculator and do the thing
Go back to the weapon/power message and roll damage
Click resolve damage and fill out the box

#

Having the resolve damage box popping up automatically is probably the most you can do without also reworking the entire module to do everything attack and damage related

latent lotus
#

Yeah, part of the challenge is that there are things like spending a Benny to reroll your attack, like if you succeed with a hit, but you really want that raise, or if there's some conditional that's not accounted for that would influence success, failure, or raises. PF doesn't have any of that. It's very binary. You hit or you don't (excluding conditional modifiers of course).

errant yarrow
#

Hm, I see your point, there's a lot more choices regarding each roll rather than static numbers

latent lotus
#

I considered adding a similar workflow to attack rolls. Roll while a target is selected, prompt for adjustments like weaknesses or unaccounted modifiers, and then including damage roll buttons on the attack result chat card.

errant yarrow
#

Better rolls 4 swade was the best for applying modifers imo

#

Copying that, where modifiers are openly laid out at the time of rolling the attack, would be the most user friendly option

latent lotus
#

It does a really good job of that. I personally struggle with the layout of all those modifiers though. It feels overwhelmingly cluttered in a chat card. I've wanted to at least learn how to handle range and possibly illumination based on scene lighting conditions. (SWADE uses more levels of lighting than Foundry can handle, IIRC. Specifically, Bright, Dim, Dark, and Pitch Black versus Bright, Dim, and Pitch Black.)

#

It seems like though that managing a workflow for attack rolls like they're handled for damage rolls could be a starting point. As a starting point, give an opportunity to prompt for adjustments to the final roll result. Then, at some point in the future, see about autogettiing modifier values based on data on the token, actor, or scene.

errant yarrow
#

I think the damage portion is fine right now, maybe change the 'close' button to 'finalise' or something for clarity ( I didn't realise how to 'apply' the damage at the beginning ) but I honestly couldn't ask for more

latent lotus
#

I'm not sure I follow. Once you take a wound or apply shaken, it should close automatically. At what stage does present 'close'?

errant yarrow
#

Sorry, guess I mis-remembered what it said

latent lotus
#

Hmmm... I might relabel Submit to "Apply Adjustments".

#

So for clarity, did you find Apply Shaken Status to be unclear with respect to what would happen or was it because of the context of the Submit button?

errant yarrow
#

I think it was the 'submit' and 'apply' buttons both kinda implying the same thing

tawdry rivet
#

While still not Better Rolls, it covers a lot of ground

latent lotus
#

Oooh! Michael's tearing it up over there.

#

I'm curious about illumination. I can see scene regions contributing to that, but I still want to explore if scene global illumination can be accounted for, too.

latent lotus
#

I think I have an idea for illumination penalties. I'm considering adding a setting where you can set the a series of values for bright, dim, and dark darkness values. Pitch Black would just be whatever the threshold is configured to be on the scene. I could then have a small script that modifies the translation string for the darkness setting description text to include the values set for reference. At that point, when an attack is rolled, the module could check the current level of darkness, whether the target token is in light, the brightness of that light, and apply illumination penalties (if any).

#

Getting Scene.environment.darknessLevel and comparing will be easy enough. It's checking if a Token is in light that I'll struggle with.

magic sentinel
#

like, we're not exactly the only ones who care about illumination

latent lotus
#

I had a whole post I wrote up that I was going to post about it, but I figured it's not likely going to happen anytime soon anyway.

#

Figured I'd just try to put it in a module, and if it inspires others to do something similar, cool.

#

You know, I might even be able to just derive dim and dark based on whatever the darkness threshold is set to. just slice the difference into halves. 0 is bright, threshold is pitch black, threshold to 49% of diff is dark. 50% of diff to 0.05 is dim.

magic sentinel
#

Should definitely document

latent lotus
#

Next, read the values during an attack roll.

latent lotus
#

Working...

Hooks.on('preUpdateScene', (scene, changed, options, userId) => {
    if (game.userId !== userId) return;

    if (!changed.tokenVision) {
        changed['flags.swade-targeted-damage.illumination'] = null;
        return;
    }

    const pitchBlack = changed.environment.globalLight.darkness.max;
    const illuminationRollModifiers = CONFIG.SWADE.prototypeRollGroups.find((rollGroup) => rollGroup.name === game.i18n.localize('SWADE.Illumination._name'))?.modifiers;
    const darknessLevel = changed.environment.darknessLevel;
    let illuminationMod;

    if (darknessLevel >= pitchBlack || !changed.environment.globalLight.enabled) {
        illuminationMod = illuminationRollModifiers.find((m) => m.label = game.i18n.localize('SWADE.Illumination.Pitch'));
    } else if (darknessLevel >= pitchBlack / 2) {
        illuminationMod = illuminationRollModifiers.find((m) => m.label = game.i18n.localize('SWADE.Illumination.Dark'));
    } else if (darknessLevel >= 0.05) {
        illuminationMod = illuminationRollModifiers.find((m) => m.label = game.i18n.localize('SWADE.Illumination.Dim'));
    } else {
        illuminationMod = null;
    }

    changed['flags.swade-targeted-damage.illumination'] = illuminationMod;
});

const swadePreRollHookEvents = ['swadePreRollSkill', 'swadePreRollAttribute'];

for (const hookEvent of swadePreRollHookEvents) {
    Hooks.on(hookEvent, (actor, skill, roll, modifiers, options) => {
        const token = game.scenes.current.tokens.find((t) => t.actorId === actor.id);
        const illuminationModifier = game.scenes.current.getFlag('swade-targeted-damage', 'illumination');

        if (token && illuminationModifier) {
            const hasBrightLight = token.light.bright > 0;
            const hasDimLight = token.light.dim > 0;

            if (!hasBrightLight && !hasDimLight) {
                modifiers.push(illuminationModifier);
            }
        }
    });
}
#

If those illumination modifier objects are stored anywhere in the system, I'd prefer to get them from there and add on the level to pass it to the flag.

latent lotus
#

Found them.

latent lotus
#

@magic sentinel I simplified it some. Let me know what you think.

latent lotus
# errant yarrow Hm, I see your point, there's a lot more choices regarding each roll rather than...

Another example is that the system doesn't know whether you're making an attack roll or a test to make the target vulnerable or distracted. Right now I'm trying to figure out how I might be able to determine when to consider whether the target is in light or the acting token is in light and whether to consider adding a penalty or not. I think at best I can just add the modifier to the roll and let the user deactivate the modifier if needed.

tawdry rivet
latent lotus
#

Yup, my thoughts exactly.

#

NGL, I think this is pretty slick.

latent lotus
#

I could set it to apply it but not have it active, just in case, but that might be confusing.

small sail
#

There's also a "are you in the radius of a dim light source" and "are you in the radius of a bright light source" bit of code I've used (basically iterating over all lights on the scene) but that doesn't account for multiple sources of stacked dim light or anything

small sail
#

Come to think of it maybe I should provide the bestCover and bestIllumination in an object so that they too can be mutated by listeners (eg if the system says "no penalties" and you say "actually dark" and then some other module comes along and wants to say "no, dim" it would be able to know what the "current" - as determined by you - illumination modifier is)

latent lotus
#

@small sail You're blowing my mind. I have clarification questions, but I need coffee and to take care of a few work things first.

latent lotus
#

So if my coffee is working and I'm understanding things correctly, canvas.effects.getDarknessLevel(Token.getSnappedPosition()) should get the darkness level to determine illumination penalties for the given token. Then I could assume that if the user has a targeted token, to include that token's illumination. I could then add both modifiers and allow the user to deactivate whichever one doesn't apply.

small sail
#

Potentially, depending on how you wanna do the logic. getDarknessLevel purely takes into account the literal "darkness level," so the majority of the time it'd be identical to the scene's environment.darknessLevel; the only time that's not the case is if there's a region with the "Adjust Darkness Level" behavior, and the position you're checking is in that region

latent lotus
#

It doesn't account for light sources though?

small sail
#

Right, for that you'd need some other special logic

#

Something like:

function getLightLevel(token) {
    let c = Object.values(token.center);
    let lights = canvas.effects.lightSources.filter(src => !(src instanceof foundry.canvas.sources.GlobalLightSource) && src.shape.contains(...c));
    if (!lights.length) return 'dark';
    let inBright = lights.some(light => {
        let {data: {x, y}, ratio} = light;
        let bright = ClockwiseSweepPolygon.create({'x': x, 'y': y}, {
            type: 'light',
            boundaryShapes: [new PIXI.Circle(x, y, ratio * light.shape.config.radius)]
        });
        return bright.contains(...c);
    });
    if (inBright) return 'bright';
    return 'dim';
}
``` (use that in CPR; haven't looked at it since acquiring a higher understanding of canvas things yet though)
#

Naturally, that's not ideal for two reasons:

  1. Doesn't care if you have 50 dim lights on you, you're still only in "dim"
  2. SWADE (as mentioned before) has a greater determination than just "bright, dim, dark."
    Perfect world, there'd be a function that would sum the cumulative light levels and spit out a hard number
latent lotus
small sail
#

You could probably adjust the above function to tell you the number of dim lights you're in (presumably if you're in any bright light you're well lit), if that'd be relevant

latent lotus
#

So I don't really need to set the flags in preUpdateScene then.

#

Originally I had it storing the various darkness levels for each Illumination modifier. That might still be useful.

small sail
#

Yeah you could technically just grab the darkness threshold from the scene each time; storing "the" illumination modifier in the flag wouldn't necessarily make sense now that you're accounting for local illumination

latent lotus
#

My idea was to store each Illumination level's value range so when you want to determine which modifier to apply, you just check the value from getDarknessLevel against those ranges since the Threshold can vary scene to scene. If it matches, you've then got the modifier details for the roll already in the same object.

small sail
#

Yeah that'd be clean. Guess it's either that or grabbing the threshold and doing the math each time, and constructing the modifier each time (which is one of the things I think could do with some refactoring at some point in the future)

latent lotus
small sail
#

Yeah in the MR linked above I'm building them all in-place, but would be neat to be able to do CONFIG.SWADE.prototypeRollGroups.illumination.dim for instance to get back the RollModifier for dim illumination

latent lotus
#

Example:

[
    {
        "label": "Dim Light",
        "value": -2,
        "levels": {
            "max": 0.375,
            "min": 0.05
        }
    },
    {
        "label": "Darkness",
        "value": -4,
        "levels": {
            "max": 0.7999999999999999,
            "min": 0.425
        }
    },
    {
        "label": "Pitch Black",
        "value": -6,
        "levels": {
            "max": 1,
            "min": 0.85
        }
    }
]
#

need to trim those decimals

latent lotus
#

Just a heads up, token.center is undefined, but token.object.center has a value.

small sail
#

Ah right, that function expects the placeable as input not the document

latent lotus
#

Ah, I see.

latent lotus
#

I'm thinking if there's more than one dim light source intersecting with the token's center, consider it bright.

small sail
#

That seems reasonable to me

tawdry rivet
#

Very reasonable but in some ways not supported by the rules where the GM would say whether that is true or not rather than a formula

#

Which makes it hard to put in the system proper

latent lotus
#

Yeah, and in actuality, it doesn't look any brighter on the scene.

#

That token is in the dim overlap of all of those light sources, but it's no brighter than any other dim area.

#

So maybe it should represent what the user sees and just treat it as dim regardless.

#

It can be overridden in the roll dialog anyway

tawdry rivet
#

I think that is most Savage

latent lotus
small sail
latent lotus
#

I've already accounted for the token's own light

#

The labels could be clearer, but here's the idea.

#

Global Darkness and Nearby Light perhaps

#

If the token is in a bright light source.

small sail
#

Neat, yeah I can see that being handy

latent lotus
#

So now if it has its own light source or if it's in nearby scene light(s), it still includes the global illumination modifier but it's set to ignore by default.

small sail
#

I like that. I was considering something similar and also trying to make use of vision modes or something but figured that was a step too far for a system implementation. The "ignore by default" is (imo) a good solution for "this might apply, but it might not"

latent lotus
#

The next thing is to check for targeted tokens and their lighting situations and add another "this might apply" modifier.

#

But I think I'll only account for the token in the darkest condition.

small sail
#

I would argue that for attacks specifically, the attacking token's illumination isn't ever gonna be relevant

#

(If no tokens are targeted then it's a solid fallback though)

latent lotus
#

Right, the problem is, you never know 100% if the roll is an attack

#

or if they aren't targeting something and can't check the lighting

#

it's just a hot mess of possibly maybe

small sail
#

True - the best I've managed so far is:

const isAttack = options.item?.type === 'weapon';
const { isRanged=null, isMelee=null } = options.item?.system ?? {};
const isRangedAttack = isRanged && (!isMelee || (skill?.system.swid !== 'fighting'));
const isMeleeAttack = isMelee && (!isRanged || (skill?.system.swid === 'fighting'));
``` which doesn't factor in powers at all. You can _basically_ guarantee that it _is_ an attack if `isAttack` is true, but there are times where it _won't_ be true that it still is one
latent lotus
#

Without code, that's about as far as I got in my head

small sail
#

I yoinked the first bit of logic from RollDialog#isAttack

#

Figured hey if it's good enough that we show the attack modifiers in the roll dialog dropdown, it's good enough to auto-add em

latent lotus
#

I just realized an issue with the flags approach. A script (module, system, or macro) could modify the darkness levels, which wouldn't be captured in the flags.

small sail
#

Hmm. Remind me what you're storing as a flag now? Away from PC so can't read the code prettily

latent lotus
#

Oh, I was capturing the modifier data but with min and max darkness values for each.

small sail
#

Ah I see. Hmm

latent lotus
#

I'm just gonna do the comparisons in the preRoll hook events

small sail
#

Probably safer there

latent lotus
latent lotus
#

Need an opinion and then I'm off to bed. The Actor has a Dim light source and is targeting 4 other tokens. One of the other tokens also has a Dim light source and another is within range of that Dim light source. The other two are in Pitch Darkness. Which of these three roll dialogs makes the most sense in terms of what the penalties are attributed to?

magic sentinel
#

#2 I think?

latent lotus
#

Token, Target, Ambient?

#

And why that one over the others, just so I know what qualities make it better?

magic sentinel
#

For me, I'm associating the ambient with ambient lights

latent lotus
#

Ah, so you didn't know that it was also including other tokens with light?

magic sentinel
#

Hmm yeah maybe that's a good reason not to use ambient

latent lotus
#

Which tells me Proximity doesn't mean anything, but perhaps "Ambient/Token" or "Ambient & Token"?

#

Then Actor was implying the acting Actor's own light.

latent lotus
#

OK, so I'm getting all targets and assessing their lighting conditions. No issues there. However, now I'm getting some duplicate modifier entries, and I want to include only unique ones. Nothing I do seems to generate a unique set of modifiers. I've tried the converting it to a Set and back trick. No, luck. Built a new array and assigned it to modifiers, still no luck.

latent lotus
#

I think I've been overthinking it. This is what we really need to know.

latent lotus
#

I think I'll roll this out as a beta feature disabled by default and collect feedback.

latent lotus
#

SWADE Targeted Damage v3.1.0

  • New Feature - Suggested Illumination Penalties (Beta): Adds inclusion of Illumination Penalties to Trait rolls based on the current Scene's global illumination settings and darkness levels as well any Dim lighting the Token is currently in.
    • The calculation for Dim, Darkness, and Pitch Dark is based on the Global Illumination Threshold Setting on the Scene. If the Scene's current darkness level is greater than or equal to the threshold, it's considered Pitch Dark. Dim is applied if there's any amount of darkness on the scene (0.05). Darkness starts at half the threshold value.
    • The penalty can be overridden in the Roll Dialog to account for a variety of circumstances in which the penalty might be lessened or not apply at all.
    • This feature is disabled by default and can be enabled in the module's settings.
    • If you run into any issues with this feature or have suggestions for improvements, please submit an issue on GitHub.
river cedar
#

I like it and some quick tests have it working well. You may (or may not) want to incorporate looking through the source actor's item SWIDs for the common limits to illumination penalties (low-light-vision, darkvision, night-vision) and ignore certain illumination penalties based on what you find.

#

if you're interested in expanding this, I have a private module that does similar for Deflection (based on active effect), Vulnerable, Glow/Shroud (based on AEs also), Arcane and Environmental Resistince/Weakness, Gang Up, Scale, Range, and Dodge, with preTraitRoll and preDamageRoll hooks for targeted things. It's a little wonky and not localized 'cause it's only me using it, but I could share.

#

I haven't cleaned it up and released it mostly 'cause I didn't want to compete with BR2 or swade tools in that space... but if YOU want to do a closer-to-system-default-rolls automation tool I won't get in the way.

#

and I'd be happy to contribute 🙂

latent lotus
#

I like the idea, but it assumes the SWIDS will be identical (i.e., dark-vision vs darkvision). I'd need to research if those are consistent across settings.

river cedar
#

some of my private module gets REALLY close to the edge of coding game behavior from the copyrightable rule set as well, so I've been worried about releasing it for that reason as well.

latent lotus
#

I might be able to create a config setting where a GM could create those pairs themselves. Default to the core and FC, and allow others to be added.

small sail
#

I should jump in and mention that I've done some work in the system (not yet merged, but should be) that hits some of those areas, pre-attack only currently, and handling of illumination specifically is relegated to a new region type so there's no incompatibility, but in terms of stuff like vulnerable, gang-up, scale, range, dodge, etc those should soon(ish) be covered

latent lotus
#

Nice

river cedar
#

(if I contribute to the system it'll finally make this backend programmer learn about this typescript thing the kids are using these days)

small sail
river cedar
#

yeah it was surprisingly easy to do

small sail
#

The PR started as "illumination regions" and swiftly scope creeped hard

#

But yeah what'll be neat is that it'll have the ability to define such regions, but it'll also offer a hook for pre-attack specification of roll modifiers (besides the already existing pre trait roll stuff) which'll contain the existing "best cover"/"best illumination" that can be overridden by modules. So for instance if Kristian's implementation wants to toss the system-determined illumination (which, currently, is purely if you've manually set up a region of the new type), it totally can do so

river cedar
#

Nice

#

ok reading this over I'm extremely excited for that to be merged and released 'cause it means I can rip out large portions of my own stuff. Thanks for sharing the sneak peak!

latent lotus
small sail
#

It should be all good tbh, without intentionally trying to, neither of us should clobber the other

latent lotus
#

I'm also debating creating an extended Scene sheet that adds slider fields for Dim and Darkness. The max value could be determined by the Threshold value of the others. So for example, Darkness' max value would be the Scene's Pitch Dark Threshold minus 0.05 (maybe even relabel the core Threshold to Pitch Dark). Then Dim's max would be Darkness' Threshold value minus 0.05. And then finally Dim's Threshold could be set to whatever of course.

small sail
#

That'd be a cool way of bending core's darkness settings to the will of the system's rules

latent lotus
#

As a module, I could either store that data as flags or register a new Scene subtype. Perhaps the system could extend a SwadeScene to add those properties at some point.

small sail
#

Yeah I mean I know John is a fan of a global scene penalty for illumination, that might be a good reason to subtype

#

It could sorta come automatically with a combo of the actual darkness setting and the threshold settings

latent lotus
#

Something like this

river cedar
#

you'd do that as a scene-specific setting instead of a global setting? Are there cases where the aesthetic look of (say) 0.25 would feel like 'dim' on one scene but not on another?

small sail
#

Scene to scene differences in "default" darkness is part of what would make it so difficult to say "that's dim" without making assumptions. Some maps just have brighter content than others. So being able to specify per-scene would clean up some of that gray area. Though global defaults as a setting would make sense too imo

river cedar
#

Fair point.

tawdry rivet
#

For sure, one way I use darkness on a scene is more like atmosphere or visual look than actual illumination leels

#

kinda like adjusting brightness on the image

latent lotus
#

Now to update the script that determines the suggested illumination penalties.

#

I'm also debating appending "(Pitch Dark)" to the Global Illumination label

#

And whether this should be a new module altogether.

small sail
#

"SWADE Auto-Illumination" sounds nice I must say.
It would be cool (I don't know if it's feasible without a buncha extra coding) if the sliders for the thresholds stayed the same width, but whichever section was "off limits" was simply disabled

latent lotus
#

That gets confusing when your dealing with max values on the slider. There's no way to stop the drag behavior that I'm aware of, but I could be wrong.

small sail
#

Yeah that's the part I was wondering about specifically. I suspect you're right, but looking into the range picker code now just in case

latent lotus
#

I was wrong. There might be a way

small sail
latent lotus
#

Possibly by setting the value on click if it's value is max already

#

But the user might just think it's broken

#

An alternative might be to push the others up. So if dim goes up, it pushes darkness up

small sail
#

Ideally it could

  1. Stop the slider thing early
  2. Style the out-of-range areas of the slider at least
latent lotus
#

Now lower levels of darkness can push the thresholds of higher levels of darkness when adjusting the values.

small sail
#

Very nice

latent lotus
#

I've got the labels and hints customized now.

latent lotus
#

I'll roll out an update tomorrow. Not sure if I want to break it out into another module simply because I need another module like I need another hole in my head.

#

But knowing me, I likely will make it a new module. We'll see.

latent lotus
small sail
#

I like it. I’m famously great at naming things.
Module that lets you attach regions to templates? Region Atttacher.
Module that lets you have a follow-up save activity in dnd5e? Dnd5e follow-up save.
Module that lets you have aura effects? Aura Effects