Skip to content

Effects can't read computed values, and nothing derives a value from an action's parameters #251

Closed

Description

@mpigsley

What you were modeling

D&D 5.2 (5e-srd-5.2) Cunning Strike, adopted as attack parameters in #249: cunningStrike (and cunningStrike2 from Improved Cunning Strike) each pick an effect that costs Sneak Attack dice (Poison, Trip, Withdraw 1; Daze 2; Obscure 3; Knock Out 6). The pair is used only when the rogue can use both and their cost fits in the Sneak Attack; then the dice come off the Sneak Attack and the target saves at the rogue's DC (content/features/rogue/cunning-strike.yaml, rules/class-rogue.yaml).

What's missing

  • A value worked out from a run's parameters. The cost of the chosen pair depends on params.cunningStrike and params.cunningStrike2. Fields can't read params, and an action has no named value derived from its parameters, so every formula that needs the cost restates it: the rules' sneakAttack situational effect (twice: the fit test and the dice removed) and the two strike action effects' when (once each). That is the same nested ternary four times, in two files.
  • Effects reading computed values. The feature declares computed: { strikeDc } for its card, but its op: action effects can't read self.strikeDc (unknown-name at load: content.md, Computed values lists effects among the places that can't). So the DC is a field (rogue.strikeDc) set by an effect, and the computed value only mirrors it.

Workaround

The cost ternary is repeated in the rules' sneakAttack effect and both strike effects; a fix to the costs has to touch all four. The DC lives in the rogue.strikeDc field. A Trip against a Huge or larger target still spends its die, because the cost can't see the target either (README).

Options

  1. (recommended) Derived parameters: a parameter (an action's own or one a params effect adds) may declare formula instead of being given. It is evaluated once every given parameter has its value, before bounds, in declaration order (reading those before it), and read as params.<name> everywhere params are: Cunning Strike adds cunningStrikeCost and the rules' effect and the strike effects read it. The app never passes it (invalid-param if it does); describeAction lists it as derived. In an each step it could also see participant, which would cover the Trip size check.
  2. Let effects that run inside an action (roll, action, changes, params bounds) read the content's computed values as self.<name>, with computed values seeing the running action's params there. Covers strikeDc, but not the rules' sneakAttack effect, which belongs to no content.
  3. Leave it: a lookup in a map field (rogue.strikeCost[params.cunningStrike]) shortens each copy, but the fit test and the pair sum stay repeated.

Option 1 and the second half of option 2 are independent; the computed-value read is small on its own.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions