Skip to content

[#5192, #5489] Allow configuration of initiative grouping#6910

Merged
Fyorl merged 3 commits into
foundryvtt:6.0.xfrom
Aioros:group-initiative
Jun 6, 2026
Merged

[#5192, #5489] Allow configuration of initiative grouping#6910
Fyorl merged 3 commits into
foundryvtt:6.0.xfrom
Aioros:group-initiative

Conversation

@Aioros

@Aioros Aioros commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

This change adds two settings to the Initiative section of the combat configuration:

  1. Roll Once per Creature: actors of the same type (roll formula - disposition - base actor) share a single initiative roll;
  2. Group Combatants: actors of the same type with the same rolled initiative are grouped in the tracker (the current default functionality, but it can now be disabled).

Closes #5192
Closes #5489

@Fyorl Fyorl added the strategic Requires sign-off from a core Foundry VTT developer before merge. label Apr 10, 2026

@Fyorl Fyorl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation looks correct. One additional requirement that I would propose is that when you roll initiative separately for a creature, with the 'roll once' setting enabled, that we check the existing combatants for a getGroupingKey match, and use that initiative so it gets added to the existing group automatically.

I suppose we would only do this if combat hasn't started or if it's the first round of combat.

Comment thread module/documents/combatant.mjs Outdated
Comment thread module/settings.mjs
Comment thread module/documents/combat.mjs Outdated
Comment thread module/documents/combat.mjs Outdated
@arbron arbron changed the base branch from 5.3.x to 6.0.x June 3, 2026 18:34
- take initiative value of existing combatant when applicable;
- changed default for "roll once" to true.
Comment on lines +83 to +84
if ( !updates.length ) return this;
await this.updateEmbeddedDocuments("Combatant", updates, { turnEvents: false });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still want to _recoverUses I think

Suggested change
if ( !updates.length ) return this;
await this.updateEmbeddedDocuments("Combatant", updates, { turnEvents: false });
if ( updates.length ) await this.updateEmbeddedDocuments("Combatant", updates, { turnEvents: false });

Can even remove the guard entirely since updateEmbeddedDocuments will no-op on an empty array.

Comment thread module/documents/combatant.mjs Outdated
if ( this.group ) return this.group.id;
if ( this.token?.actorLink || !this.token?.baseActor
|| !dnd5e.settings.initiativeGroupRoll ) return null;
return `${this.getInitiativeRoll().formula}:${this.token.disposition}:${this.token.baseActor.id}`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the desire is to match getGroupingKey's use of this.initiative, but that is solely for keeping combatants grouped in the tracker so long as they share initiative.

For this method we just want to detect 'is this creature identical to some other creature that has already rolled initiative', where 'identical' is from the wording in the PHB:

For a group of identical creatures, the DM makes a single roll, so each member of the group has the same Initiative.

Let me know if there's some other reason we are using this, otherwise I think it's safe to remove.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use something like this to share some more of the logic:

  /**
   * Key for the group to which this combatant should belong, or `null` if it can't be grouped.
   * @returns {string|null}
   */
  getGroupingKey() {
    if ( this.group ) return this.group.id;
    const uniqueKey = this.getUniqueKey();
    return (uniqueKey === null) || (this.initiative === null) ? null : `${Math.floor(this.initiative)}:${uniqueKey}`;
  }

  /* -------------------------------------------- */

  /**
   * Key used to group unique sets of actors in the combat, or `null` if they shouldn't be grouped.
   * @returns {string|null}
   */
  getUniqueKey() {
    if ( this.token?.actorLink || !this.token?.baseActor ) return null;
    return `${this.token.disposition}:${this.token.baseActor.id}`;
  }

@Aioros Aioros Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opted to use the initiative roll formula in the grouping key because it seemed like a reasonable interpretation of the rule. I might have a group of identical enemies where half of them are surprised at the start of combat, so I would expect two groups to roll (one normal, one with disadvantage); or I dragged four unlinked Mage tokens, changed Dex to -2 for one of them and called it Clumsy Mage, and similarly expect it to roll separately.

Let me know if that seems reasonable. To me, it seems that "having the same kind of initiative" has to be a prerequisite part of "identical creatures" in this context.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a reasonable interpretation. The rules are very much underspecified in this regard. I don't feel strongly, so I have approved the PR. If there's anything to be done with regards to sharing code like Jeff's suggestion, feel free to make those changes. Otherwise I will merge in due course.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it is a method, I suppose we can just pass in an initiative parameter that falls back to this.initiative if not present.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it can go back to being a single method (except the parameter will need to tell if it's for rolling or just for final grouping, since we need to check the appropriate setting). I'll do that and the other final fixes (probably on Monday though)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a canonical getUniqueKey, but I think we still need separate methods since they check different settings. If I've done something silly, feel free to just amend it.

@Fyorl Fyorl merged commit 9572e33 into foundryvtt:6.0.x Jun 6, 2026
@Fyorl Fyorl added this to the D&D5E 6.0.0 milestone Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: high strategic Requires sign-off from a core Foundry VTT developer before merge. system: combat

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Combat Tracker Groups Cannot Be Disabled [Suggestion] Group Initiative Rolls

4 participants