3 votes

Création d'un rouleau de dé de la 5e édition dans AnyDice : comment gérer le bonus de dégâts au premier coup ?

J'ai essayé de créer un générateur de probabilités complet pour les jets d'attaque de la cinquième édition de D&D dans AnyDice, incluant des caractéristiques telles que l'avantage/désavantage, différentes plages de crits, des dégâts de crits bonus, etc. J'ai rencontré un problème avec les capacités qui n'infligent des dégâts supplémentaires qu'une fois par round, comme Sneak Attack.

Pseudocode :

lots of variables
parse variables into dice \advantage=1 turns the attack roll into 1@2d20 etc\

function: attack ROLL:n {
 if crit fail {return:0}
 if crit hit {return: damage +crit damage}
 if normal hit {return: damage}
 if normal miss {return: 0}
}

function: multiattack NUMBER_OF_ATTACKS:n {
 TOTAL:0
 loop N over {1..NUMBER_OF_ATTACKS} {
  TOTAL:TOTAL+[attack ROLL]
 }
}

output [result of multiattack function]

Ce que je veux faire, c'est ajouter des dégâts bonus au premier coup s'il y en a. Comme les fonctions AnyDice n'affectent pas les variables externes et se terminent immédiatement par un "résultat" et que les fonctions booléennes ne fonctionnent qu'avec des nombres et non des ensembles ou des dés, je n'ai pas trouvé de bon moyen de faire une variable capable de dire avec précision si un coup a déjà été porté.

Si je définis une valeur à l'intérieur de la fonction d'attaque, celle-ci sera réinitialisée à cette valeur lors du prochain tour de la boucle. Les variables à l'intérieur d'une fonction ne changent pas à l'extérieur, donc appeler la variable à nouveau ne fera pas de différence (à moins que j'ai raté quelque chose) et je n'ai pas trouvé un moyen de le faire dans le cadre de la fonction d'aide. Je n'ai pas trouvé de moyen de le faire dans le cadre de la fonction d'aide.

5voto

trjh Points 11

Ce qu'il faut faire, c'est écrire une fonction pour "geler" vos jets de touche, et l'appeler récursivement pour chaque attaque (infructueuse), quelque chose comme cet exemple de base :

ATK:  1d20  \ attack roll: use 1@2d20 for advantage or 2@2d20 for disadvantage \
HIT:    +0  \ bonus to attack roll \
AC:      8  \ target AC: attack hits if ATK + HIT >= AC \
CRIT:   20  \ minimum attack roll to crit \
DMG:   1d4  \ damage rolled on successful hit \
BONUS: 1d6  \ bonus damage on first hit \

function: attack ROLL:n {
  \ return the number of times damage is applied on an attack roll of ROLL \
  if ROLL <= 1 { result: 0 }     \ natural 1 is always a miss \
  if ROLL >= CRIT { result: 2 }  \ critical hit deals damage damage twice \
  result: ROLL + HIT >= AC
}

function: multiattack N:n times {
  result: [roll [attack ATK] to multiattack N times]
}
function: roll FIRSTHIT:n to multiattack N:n times {
  if N <= 0 {
    result: 0
  }
  if FIRSTHIT > 0 {
    DAMAGE: FIRSTHIT d (DMG + BONUS)
    loop I over {2..N} {
      DAMAGE: DAMAGE + [attack ATK] d DMG
    }
    result: DAMAGE
  } else {
    result: [roll [attack ATK] to multiattack N-1 times]
  }
}

output [multiattack 3 times]

Les principaux [multiattack N:n times] appelle simplement la fonction d'aide [roll FIRSTHIT:n to multiattack N:n times] . Le premier argument ( FIRSTHIT ) à la fonction d'aide est donnée par [attack ATK] qui renvoie le nombre de fois où les dégâts doivent être appliqués pour ce jet d'attaque (c'est-à-dire 0 pour un échec, 1 pour un succès et 2 pour un coup critique).

Puisque cet argument est marqué comme numérique (avec la mention :n ) dans la définition de la fonction, AnyDice appelle en interne la fonction d'aide avec chaque valeur de retour possible de [attack ATK] et pondère ses résultats en fonction de la probabilité des résultats. Cela signifie que, même si le résultat de [attack ATK] est un dé (biaisé), dans la fonction d'aide sa valeur est "gelée" comme FIRSTHIT qui est un nombre fixe et peut donc être utilisé, par exemple, dans le cadre d'un projet de recherche. if les conditionnels. (Voir la section "Fonctions" de la documentation d'AnyDice pour plus de détails).

La fonction d'aide elle-même vérifie simplement si l'attaque touche (c'est-à-dire si FIRSTHIT > 0 ) et, si c'est le cas, applique les dégâts de cette attaque (plus le bonus, le cas échéant) et de toutes les attaques restantes (sans le bonus). En revanche, si l'attaque est ratée, la fonction d'aide s'appelle récursivement avec un nouveau jet d'attaque et avec un nombre d'attaques restantes réduit d'une unité.


Ps. L'expression FIRSTHIT d (DMG + BONUS) peut être discutée : elle lance simplement les dégâts normaux et les dégâts bonus. FIRSTHIT et additionne les résultats. (D'autre part, FIRSTHIT * (DMG + BONUS) Le résultat est obtenu en lançant une fois les dégâts normaux et les dégâts bonus, et en les multipliant par FIRSTHIT .) Ainsi, si FIRSTHIT est égal à 2, les dégâts (y compris les éventuels bonus d'attaque sournoise ou autres) sont calculés deux fois, comme le précisent les règles du 5e pour les coups critiques. De même, à l'intérieur de la boucle, l'expression [attack ATK] d DMG donne le résultat du roulement DMG soit zéro, soit une, soit deux fois, selon les probabilités données par l'équation. [attack ATK] rouleau.

En fait, tout le code à l'intérieur du if FIRSTHIT > 0 bloc conditionnel pourrait être remplacée avec l'expression suivante :

result: FIRSTHIT d (DMG + BONUS) + (N-1) d ([attack ATK] d DMG)

Ici, (N-1) d ([attack ATK] d DMG) donne le résultat de l'exécution N-1 les jets d'attaque, chaque attaque infligeant DMG dommages [attack ATK] fois.


Il convient également de noter qu'AnyDice ne permet normalement d'imbriquer les appels de fonction qu'à 10 niveaux de profondeur (dont un est utilisé par la fonction wrapper dans cet exemple, et un par la fonction [attack ATK] appel). Si vous souhaitez prendre en charge les multi-attaques avec plus de 8 jets d'attaque successifs, vous devez augmenter la limite de récursivité, par exemple de la manière suivante :

set "maximum function depth" to 999

Cependant, pour faire fonctionner une récursion aussi profonde en un temps raisonnable, il est nécessaire de essentiel pour s'assurer que la fonction d'aide ne s'appelle que sur un valeur spécifique de ses paramètres non fixes (par exemple, dans le cas présent, lorsque FIRSTHIT est égal à zéro). S'il peut récurer sur deux entrées possibles ou plus, le nombre de séquences différentes d'appels récursifs qu'AnyDice doit vérifier augmentera de façon exponentielle avec la profondeur de récursion.

C'est la raison pour laquelle le code ci-dessus utilise l'option résultat de [attack ATK] (qui ne peut être que 0, 1 ou 2) comme paramètre de la fonction d'aide, au lieu de transmettre le jet d'attaque ATK (qui peut aller de 1 à 20) directement en tant que paramètre. Alors que le faire de cette façon fonctionnera correctement pour un petit nombre d'attaques, mais il deviendra très lent très rapidement à mesure que la profondeur de récursion augmentera.

En fait, nous pourrions optimiser un peu plus le code en pré-calculant les résultats de [attack ATK] et les assigner à un dé personnalisé global. Cela permettrait d'accélérer légèrement le code et d'économiser un niveau d'imbrication de fonctions.


Notez également que le code ci-dessus suppose que le bonus de dégâts sera toujours appliqué au premier coup réussi, qu'il s'agisse d'un critérium ou non. Ceci est probablement optimal dans la plupart des situations pratiques, mais en principe, un joueur avec un nombre suffisamment important d'attaques restantes et un changement de hit et/ou de crit suffisamment élevé pourrait Il est préférable de garder son attaque sournoise pour plus tard s'il obtient une touche précoce non critique, en supposant qu'il obtiendra probablement une touche critique (ou au moins une autre touche normale) plus tard. Modélisation de ces stratégies avancées es possible dans AnyDice (généralement aussi par récursion), mais plus compliqué.

AlleGamers.com

AlleGamers est une communauté de gamers qui cherche à élargir la connaissance des jeux vidéo.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X