RnK with some bugs
This commit is contained in:
@@ -4,7 +4,11 @@
|
||||
"Maintainers": ["Team L5R"]
|
||||
},
|
||||
"SETTINGS": {
|
||||
"None": "No option"
|
||||
"None": "No option",
|
||||
"RollNKeep": {
|
||||
"DeleteOldMessage": "RnK Delete previous chat message",
|
||||
"DeleteOldMessageHint": "Choose to keep or delete the previous message for a RnK series"
|
||||
}
|
||||
},
|
||||
"ACTOR": {
|
||||
"TypeCharacter": "Player Character",
|
||||
@@ -105,7 +109,7 @@
|
||||
"swap_drop_here": "Swap",
|
||||
"keep_drop_here": "Keep",
|
||||
"keep_chat": "New roll from an exploding die",
|
||||
"bt_validate": "Finalize"
|
||||
"bt_validate": "Finalize this step"
|
||||
},
|
||||
"max": "Max",
|
||||
"current": "Current",
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
"Maintainers": ["Team L5R"]
|
||||
},
|
||||
"SETTINGS": {
|
||||
"None": "Sin opción"
|
||||
"None": "Sin opción",
|
||||
"RollNKeep": {
|
||||
"DeleteOldMessage": "RnK Delete previous chat message",
|
||||
"DeleteOldMessageHint": "Choose to keep or delete the previous message for a RnK series"
|
||||
}
|
||||
},
|
||||
"ACTOR": {
|
||||
"TypeCharacter": "Personaje jugador",
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
"Maintainers": ["Team L5R"]
|
||||
},
|
||||
"SETTINGS": {
|
||||
"None": "Aucune option"
|
||||
"None": "Aucune option",
|
||||
"RollNKeep": {
|
||||
"DeleteOldMessage": "RnK Delete previous chat message",
|
||||
"DeleteOldMessageHint": "Choose to keep or delete the previous message for a RnK series"
|
||||
}
|
||||
},
|
||||
"ACTOR": {
|
||||
"TypeCharacter": "Personnage Joueur",
|
||||
@@ -105,7 +109,7 @@
|
||||
"swap_drop_here": "Swap",
|
||||
"keep_drop_here": "Keep",
|
||||
"keep_chat": "New roll from a exploding dice",
|
||||
"bt_validate": "Finalize"
|
||||
"bt_validate": "Finalize this step"
|
||||
},
|
||||
"max": "Max",
|
||||
"current": "Actuel",
|
||||
|
||||
@@ -41,7 +41,7 @@ export class CombatL5r5e extends Combat {
|
||||
|
||||
// Get score for each combatant
|
||||
const updatedCombatants = [];
|
||||
ids.forEach((combatantId) => {
|
||||
for (const combatantId of ids) {
|
||||
const combatant = game.combat.combatants.find((c) => c._id === combatantId);
|
||||
|
||||
// Skip if combatant already have a initiative value
|
||||
@@ -77,31 +77,41 @@ export class CombatL5r5e extends Combat {
|
||||
formula = createFormula.join("+");
|
||||
}
|
||||
|
||||
const roll = new game.l5r5e.RollL5r5e(formula);
|
||||
roll.actor = combatant.actor;
|
||||
roll.l5r5e.stance = data.stance;
|
||||
roll.l5r5e.skillId = skillId;
|
||||
roll.l5r5e.skillCatId = skillCat;
|
||||
roll.l5r5e.summary.difficulty =
|
||||
messageOptions.difficulty !== undefined ? messageOptions.difficulty : cfg.difficulty;
|
||||
roll.l5r5e.summary.difficultyHidden =
|
||||
messageOptions.difficultyHidden !== undefined
|
||||
? messageOptions.difficultyHidden
|
||||
: cfg.difficultyHidden;
|
||||
roll.l5r5e.summary.voidPointUsed = !!messageOptions.useVoidPoint;
|
||||
let roll;
|
||||
const flavor =
|
||||
game.i18n.localize("l5r5e.chatdices.initiative_roll") +
|
||||
" (" +
|
||||
game.i18n.localize(`l5r5e.conflict.initiative.prepared_${isPrepared}`) +
|
||||
")";
|
||||
|
||||
roll.roll();
|
||||
roll.toMessage({
|
||||
flavor:
|
||||
game.i18n.localize("l5r5e.chatdices.initiative_roll") +
|
||||
" (" +
|
||||
game.i18n.localize(`l5r5e.conflict.initiative.prepared_${isPrepared}`) +
|
||||
")",
|
||||
});
|
||||
if (messageOptions.rnkRoll instanceof game.l5r5e.RollL5r5e && ids.length === 1) {
|
||||
// Specific RnK
|
||||
roll = messageOptions.rnkRoll;
|
||||
// Ugly but work... i need the new messageId
|
||||
combatant.actor.rnkMessage = await roll.toMessage({ flavor });
|
||||
} else {
|
||||
// Regular
|
||||
roll = new game.l5r5e.RollL5r5e(formula);
|
||||
roll.actor = combatant.actor;
|
||||
roll.l5r5e.isInitiativeRoll = true;
|
||||
roll.l5r5e.stance = data.stance;
|
||||
roll.l5r5e.skillId = skillId;
|
||||
roll.l5r5e.skillCatId = skillCat;
|
||||
roll.l5r5e.summary.difficulty =
|
||||
messageOptions.difficulty !== undefined ? messageOptions.difficulty : cfg.difficulty;
|
||||
roll.l5r5e.summary.difficultyHidden =
|
||||
messageOptions.difficultyHidden !== undefined
|
||||
? messageOptions.difficultyHidden
|
||||
: cfg.difficultyHidden;
|
||||
roll.l5r5e.summary.voidPointUsed = !!messageOptions.useVoidPoint;
|
||||
|
||||
roll.roll();
|
||||
roll.toMessage({ flavor });
|
||||
}
|
||||
|
||||
// if the character succeeded on their Initiative check, they add 1 to their base initiative value,
|
||||
// plus an additional amount equal to their bonus successes.
|
||||
const successes = Math.min(roll.l5r5e.summary.ringsUsed, roll.l5r5e.summary.totalSuccess);
|
||||
const successes = roll.l5r5e.summary.totalSuccess;
|
||||
if (successes >= roll.l5r5e.summary.difficulty) {
|
||||
initiative = initiative + 1 + Math.max(successes - roll.l5r5e.summary.difficulty, 0);
|
||||
}
|
||||
@@ -111,7 +121,7 @@ export class CombatL5r5e extends Combat {
|
||||
_id: combatant._id,
|
||||
initiative: initiative,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Update all combatants at once
|
||||
await this.updateEmbeddedEntity("Combatant", updatedCombatants);
|
||||
|
||||
@@ -72,13 +72,7 @@ export class L5rBaseDie extends DiceTerm {
|
||||
this._evaluateModifiers();
|
||||
|
||||
// Combine all results
|
||||
this.l5r5e = { success: 0, explosive: 0, opportunity: 0, strife: 0 };
|
||||
this.results.forEach((term) => {
|
||||
const face = this.constructor.FACES[term.result];
|
||||
["success", "explosive", "opportunity", "strife"].forEach((props) => {
|
||||
this.l5r5e[props] += parseInt(face[props]);
|
||||
});
|
||||
});
|
||||
this.l5rSummary();
|
||||
|
||||
// Return the evaluated term
|
||||
this._evaluated = true;
|
||||
@@ -87,24 +81,33 @@ export class L5rBaseDie extends DiceTerm {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarise the total of success, strife... for L5R dices for the current Die
|
||||
*/
|
||||
l5rSummary() {
|
||||
this.l5r5e = { success: 0, explosive: 0, opportunity: 0, strife: 0 };
|
||||
this.results.forEach((term) => {
|
||||
const face = this.constructor.FACES[term.result];
|
||||
["success", "explosive", "opportunity", "strife"].forEach((props) => {
|
||||
this.l5r5e[props] += parseInt(face[props]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll the DiceTerm by mapping a random uniform draw against the faces of the dice term
|
||||
* @override
|
||||
*/
|
||||
roll(options) {
|
||||
const roll = super.roll(options);
|
||||
|
||||
//roll.l5r5e = this.l5r5e;
|
||||
|
||||
return roll;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static fromData(data) {
|
||||
const roll = super.fromData(data);
|
||||
|
||||
roll.l5r5e = data.l5r5e;
|
||||
|
||||
return roll;
|
||||
}
|
||||
|
||||
@@ -114,9 +117,7 @@ export class L5rBaseDie extends DiceTerm {
|
||||
*/
|
||||
toJSON() {
|
||||
const json = super.toJSON();
|
||||
|
||||
json.l5r5e = this.l5r5e;
|
||||
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export class RollnKeepDialog extends FormApplication {
|
||||
*/
|
||||
object = {
|
||||
currentStep: 0,
|
||||
submitDisabled: true,
|
||||
submitDisabled: false,
|
||||
swapDiceFaces: {
|
||||
rings: [],
|
||||
skills: [],
|
||||
@@ -52,6 +52,7 @@ export class RollnKeepDialog extends FormApplication {
|
||||
title: game.i18n.localize("l5r5e.roll_n_keep.title"),
|
||||
width: 660,
|
||||
height: 454,
|
||||
closeOnSubmit: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -62,21 +63,23 @@ export class RollnKeepDialog extends FormApplication {
|
||||
return `l5r5e-roll-n-keep-dialog-${this.message._id}`;
|
||||
}
|
||||
|
||||
set message(msg) {
|
||||
this.message = msg instanceof ChatMessage ? duplicate(msg) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the Roll n Keep dialog
|
||||
* @param {number} messageId
|
||||
* @param {FormApplicationOptions} options
|
||||
*/
|
||||
constructor(messageId, options = {}) {
|
||||
console.clear(); // TODO TMP
|
||||
|
||||
super({}, options);
|
||||
this.message = game.messages.get(messageId);
|
||||
this.options.editable = this.message?._roll.l5r5e.actor.owner || false;
|
||||
this._initializeDiceFaces();
|
||||
this._initialize();
|
||||
this.options.editable =
|
||||
this.message?.isAuthor || this.message?._roll.l5r5e.actor?.owner || this.message?.owner || false;
|
||||
|
||||
console.log(this.object); // TODO TMP
|
||||
this._initializeDiceFaces();
|
||||
this._initializeHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +89,7 @@ export class RollnKeepDialog extends FormApplication {
|
||||
if (!this.message) {
|
||||
return;
|
||||
}
|
||||
this._initialize();
|
||||
this._initializeHistory();
|
||||
this.render(false);
|
||||
}
|
||||
|
||||
@@ -105,10 +108,10 @@ export class RollnKeepDialog extends FormApplication {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the dialog with the message
|
||||
* Initialize the dice history list
|
||||
* @private
|
||||
*/
|
||||
_initialize() {
|
||||
_initializeHistory() {
|
||||
if (!this.message) {
|
||||
return;
|
||||
}
|
||||
@@ -118,12 +121,18 @@ export class RollnKeepDialog extends FormApplication {
|
||||
|
||||
// Already history
|
||||
if (Array.isArray(this.roll.l5r5e.history)) {
|
||||
this.object.currentStep = this.roll.l5r5e.history.length;
|
||||
this.object.dicesList = this.roll.l5r5e.history;
|
||||
|
||||
let currentStep = this.roll.l5r5e.history.length - 1;
|
||||
if (!this._haveChoice(currentStep)) {
|
||||
currentStep += 1;
|
||||
}
|
||||
this.object.currentStep = currentStep;
|
||||
return;
|
||||
}
|
||||
|
||||
// New
|
||||
this.object.dicesList = [[]];
|
||||
this.roll.terms.forEach((term) => {
|
||||
if (typeof term !== "object") {
|
||||
return;
|
||||
@@ -132,7 +141,6 @@ export class RollnKeepDialog extends FormApplication {
|
||||
this.object.dicesList[0].push({
|
||||
type: term.constructor.name,
|
||||
face: res.result,
|
||||
img: term.constructor.getResultSrc(res.result),
|
||||
choice: RollnKeepDialog.CHOICES.nothing,
|
||||
});
|
||||
});
|
||||
@@ -145,14 +153,10 @@ export class RollnKeepDialog extends FormApplication {
|
||||
*/
|
||||
_initializeDiceFaces() {
|
||||
// All faces are unique for rings
|
||||
this.object.swapDiceFaces.rings = Object.keys(game.l5r5e.RingDie.FACES).map((id) => {
|
||||
return { id, img: game.l5r5e.RingDie.getResultSrc(id) };
|
||||
});
|
||||
this.object.swapDiceFaces.rings = Object.keys(game.l5r5e.RingDie.FACES);
|
||||
|
||||
// Only unique for Skills
|
||||
this.object.swapDiceFaces.skills = [1, 3, 6, 8, 10, 11, 12].map((id) => {
|
||||
return { id, img: game.l5r5e.AbilityDie.getResultSrc(id) };
|
||||
});
|
||||
this.object.swapDiceFaces.skills = [1, 3, 6, 8, 10, 11, 12];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,6 +174,15 @@ export class RollnKeepDialog extends FormApplication {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Define whether a user is able to begin a dragstart workflow for a given drag selector
|
||||
* @param selector The candidate HTML selector for dragging
|
||||
* @return Can the current user drag this selector?
|
||||
*/
|
||||
_canDragStart(selector) {
|
||||
return this.options.editable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback actions which occur at the beginning of a drag start workflow.
|
||||
* @param {DragEvent} event The originating DragEvent
|
||||
@@ -192,8 +205,12 @@ export class RollnKeepDialog extends FormApplication {
|
||||
*/
|
||||
getData(options = null) {
|
||||
// Check only on 1st step
|
||||
const kept = this.object.currentStep === 0 ? this._getKeepCount() : 1;
|
||||
this.object.submitDisabled = kept < 1 || kept > this.roll.l5r5e.summary.ringsUsed;
|
||||
if (this.object.currentStep === 0) {
|
||||
const kept = this._getKeepCount();
|
||||
this.object.submitDisabled = kept < 1 || kept > this.roll.l5r5e.summary.ringsUsed;
|
||||
} else if (!this.object.dicesList[this.object.currentStep]) {
|
||||
this.options.editable = false;
|
||||
}
|
||||
|
||||
return {
|
||||
...super.getData(options),
|
||||
@@ -263,39 +280,16 @@ export class RollnKeepDialog extends FormApplication {
|
||||
}
|
||||
|
||||
current.choice = type;
|
||||
current.img = game.l5r5e[current.type].getResultSrc(current.newFace ?? current.face);
|
||||
|
||||
// Little time saving : on 1st step, if we reach the max kept dices, discard all dices without a choice
|
||||
if (this.object.currentStep === 0 && this._getKeepCount() === this.roll.l5r5e.summary.ringsUsed) {
|
||||
this._discardDiceWithoutChoice();
|
||||
}
|
||||
|
||||
this.render(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll a new die avec return the result
|
||||
* @private
|
||||
*/
|
||||
async _newRoll(dieType, actionType) {
|
||||
const roll = await new game.l5r5e.RollL5r5e(dieType === "RingDie" ? "1dr" : "1ds");
|
||||
roll.actor = this.message.roll.l5r5e.actor;
|
||||
roll.l5r5e.stance = this.message.roll.l5r5e.stance;
|
||||
// roll.l5r5e.skillId = this.message.roll.l5r5e.skillId;
|
||||
// roll.l5r5e.skillCatId = this.message.roll.l5r5e.skillCatId;
|
||||
|
||||
await roll.roll();
|
||||
await roll.toMessage({
|
||||
flavor: game.i18n.localize(`l5r5e.roll_n_keep.${actionType}_chat`),
|
||||
});
|
||||
|
||||
const dice = roll.terms[0];
|
||||
const result = dice.results[0].result;
|
||||
|
||||
return {
|
||||
type: dieType,
|
||||
face: result,
|
||||
img: dice.constructor.getResultSrc(result),
|
||||
choice: RollnKeepDialog.CHOICES.nothing,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current number of dices kept
|
||||
* @private
|
||||
@@ -321,6 +315,255 @@ export class RollnKeepDialog extends FormApplication {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the player can make a choice for the current step
|
||||
* @private
|
||||
*/
|
||||
_haveChoice(currentStep) {
|
||||
return (
|
||||
this.object.dicesList[currentStep] &&
|
||||
this.object.dicesList[currentStep].some((e) => !!e && e.choice === RollnKeepDialog.CHOICES.nothing)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard all dices without a choice for the current step
|
||||
* @private
|
||||
*/
|
||||
_discardDiceWithoutChoice() {
|
||||
this.object.dicesList[this.object.currentStep]
|
||||
.filter((e) => !!e)
|
||||
.map((e) => {
|
||||
if (e.choice === RollnKeepDialog.CHOICES.nothing) {
|
||||
e.choice = RollnKeepDialog.CHOICES.discard;
|
||||
}
|
||||
return e;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all choices to build the next step
|
||||
* @returns {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _applyChoices() {
|
||||
const nextStep = this.object.currentStep + 1;
|
||||
|
||||
// Foreach kept dices, apply choices
|
||||
const newRolls = {};
|
||||
this.object.dicesList[this.object.currentStep].forEach((die, idx) => {
|
||||
if (!die) {
|
||||
return;
|
||||
}
|
||||
switch (die.choice) {
|
||||
case RollnKeepDialog.CHOICES.keep:
|
||||
// Exploding dice : add a new dice in the next step
|
||||
if (game.l5r5e[die.type].FACES[die.face].explosive) {
|
||||
if (!newRolls[die.type]) {
|
||||
newRolls[die.type] = 0;
|
||||
}
|
||||
newRolls[die.type] += 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case RollnKeepDialog.CHOICES.reroll:
|
||||
// Reroll : add a new dice in the next step
|
||||
if (!newRolls[die.type]) {
|
||||
newRolls[die.type] = 0;
|
||||
}
|
||||
newRolls[die.type] += 1;
|
||||
break;
|
||||
|
||||
case RollnKeepDialog.CHOICES.swap:
|
||||
// FaceSwap : add a new dice with selected face in next step
|
||||
if (!this.object.dicesList[nextStep]) {
|
||||
this.object.dicesList[nextStep] = Array(this.object.dicesList[0].length).fill(null);
|
||||
}
|
||||
this.object.dicesList[nextStep][idx] = {
|
||||
type: this.object.dicesList[this.object.currentStep][idx].type,
|
||||
face: this.object.dicesList[this.object.currentStep][idx].newFace,
|
||||
choice: RollnKeepDialog.CHOICES.keep,
|
||||
};
|
||||
delete this.object.dicesList[this.object.currentStep][idx].newFace;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// If new rolls, roll and add them
|
||||
if (Object.keys(newRolls).length > 0) {
|
||||
const newRollsResults = await this._newRoll(newRolls);
|
||||
|
||||
if (!this.object.dicesList[nextStep]) {
|
||||
this.object.dicesList[nextStep] = Array(this.object.dicesList[0].length).fill(null);
|
||||
}
|
||||
|
||||
this.object.dicesList[this.object.currentStep].forEach((die, idx) => {
|
||||
if (!die) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
die.choice === RollnKeepDialog.CHOICES.reroll ||
|
||||
(die.choice === RollnKeepDialog.CHOICES.keep && game.l5r5e[die.type].FACES[die.face].explosive)
|
||||
) {
|
||||
this.object.dicesList[nextStep][idx] = newRollsResults[die.type].shift();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a array (of int or object) into a formula ring/skill
|
||||
* @param rolls
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
_arrayToFormula(rolls) {
|
||||
const formula = [];
|
||||
if (rolls["RingDie"]) {
|
||||
const rings = Array.isArray(rolls["RingDie"]) ? rolls["RingDie"].length : rolls["RingDie"];
|
||||
formula.push(rings + "dr");
|
||||
}
|
||||
if (rolls["AbilityDie"]) {
|
||||
const skills = Array.isArray(rolls["AbilityDie"]) ? rolls["AbilityDie"].length : rolls["AbilityDie"];
|
||||
formula.push(skills + "ds");
|
||||
}
|
||||
if (formula.length < 1) {
|
||||
return "";
|
||||
}
|
||||
return formula.join("+");
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll all new dice at once (better performance) and return the result
|
||||
* @private
|
||||
*/
|
||||
async _newRoll(newRolls) {
|
||||
const out = {
|
||||
RingDie: [],
|
||||
AbilityDie: [],
|
||||
};
|
||||
|
||||
const roll = await new game.l5r5e.RollL5r5e(this._arrayToFormula(newRolls));
|
||||
roll.l5r5e = {
|
||||
...this.message.roll.l5r5e,
|
||||
summary: roll.l5r5e.summary,
|
||||
};
|
||||
|
||||
await roll.roll();
|
||||
|
||||
// Show DsN dice for the new roll
|
||||
if (game.dice3d !== undefined) {
|
||||
game.dice3d.showForRoll(roll, game.user, true);
|
||||
}
|
||||
|
||||
roll.terms.forEach((term) => {
|
||||
if (typeof term !== "object") {
|
||||
return;
|
||||
}
|
||||
term.results.forEach((res) => {
|
||||
out[term.constructor.name].push({
|
||||
type: term.constructor.name,
|
||||
face: res.result,
|
||||
choice: RollnKeepDialog.CHOICES.nothing,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the message roll
|
||||
* @private
|
||||
*/
|
||||
async _rebuildRoll() {
|
||||
// Get all kept dices + new (choice null)
|
||||
const diceList = this.object.dicesList.reduce((acc, step) => {
|
||||
step.forEach((die) => {
|
||||
if (!!die && die.choice !== RollnKeepDialog.CHOICES.discard) {
|
||||
if (!acc[die.type]) {
|
||||
acc[die.type] = [];
|
||||
}
|
||||
acc[die.type].push(die);
|
||||
}
|
||||
});
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Re create a new roll
|
||||
const roll = await new game.l5r5e.RollL5r5e(this._arrayToFormula(diceList));
|
||||
roll.l5r5e = {
|
||||
...this.message.roll.l5r5e,
|
||||
summary: roll.l5r5e.summary,
|
||||
};
|
||||
|
||||
// Fill the data
|
||||
roll.evaluate();
|
||||
|
||||
// Modify results
|
||||
roll.terms.map((term) => {
|
||||
if (term instanceof game.l5r5e.L5rBaseDie) {
|
||||
term.results.map((res) => {
|
||||
const die = diceList[term.constructor.name].shift();
|
||||
res.result = die.face;
|
||||
return res;
|
||||
});
|
||||
term.l5rSummary();
|
||||
}
|
||||
return term;
|
||||
});
|
||||
|
||||
// Recompute summary
|
||||
roll.l5rSummary();
|
||||
|
||||
// Add roll & history to message
|
||||
this.message._roll = roll;
|
||||
this.message._roll.l5r5e.history = this.object.dicesList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the new roll in chat and delete the old message
|
||||
* @returns {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _toChatMessage() {
|
||||
// Keep old Ids
|
||||
const appId = this.id;
|
||||
const msgId = this.message._id;
|
||||
|
||||
if (this.message._roll.l5r5e.isInitiativeRoll) {
|
||||
await this.message._roll.l5r5e.actor.rollInitiative({
|
||||
rerollInitiative: true,
|
||||
initiativeOptions: {
|
||||
messageOptions: {
|
||||
rnkRoll: this.message._roll,
|
||||
},
|
||||
},
|
||||
});
|
||||
// Adhesive tape to get the messageId :/
|
||||
this.message = this.message._roll.l5r5e.actor.rnkMessage;
|
||||
delete this.message._roll.l5r5e.actor.rnkMessage;
|
||||
} else {
|
||||
// Send it to chat, switch to new message
|
||||
this.message = await this.message._roll.toMessage();
|
||||
}
|
||||
|
||||
// Refresh viewers
|
||||
game.l5r5e.sockets.updateMessageIdAndRefresh(appId, this.message._id);
|
||||
|
||||
// Delete old chat message related to this series
|
||||
if (game.settings.get("l5r5e", "rnk.deleteOldMessage")) {
|
||||
if (game.user.isGM) {
|
||||
const message = game.messages.get(msgId);
|
||||
if (message) {
|
||||
message.delete();
|
||||
}
|
||||
} else {
|
||||
game.l5r5e.sockets.deleteChatMessage(msgId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called upon form submission after form data is validated
|
||||
* @param event The initial triggering submission event
|
||||
@@ -334,74 +577,26 @@ export class RollnKeepDialog extends FormApplication {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("**** _updateObject");
|
||||
// Discard all dices without a choice for the current step
|
||||
this._discardDiceWithoutChoice();
|
||||
|
||||
console.log(this.object.dicesList);
|
||||
// Apply all choices to build the next step
|
||||
await this._applyChoices();
|
||||
|
||||
// Actions p 26 : change, ignore/discard, reroll, reserve, change face
|
||||
// let addNewRoll = false;
|
||||
// switch (type) {
|
||||
// case RollnKeepDialog.CHOICES.keep:
|
||||
// // current.explosive = term.constructor.FACES[current.face / newFace].explosive
|
||||
// if (current.explosive) {
|
||||
// addNewRoll = true;
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case RollnKeepDialog.CHOICES.reroll:
|
||||
// addNewRoll = true;
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// // New roll
|
||||
// if (addNewRoll) {
|
||||
// if (!this.object.dicesList[data.step + 1]) {
|
||||
// this.object.dicesList[data.step + 1] = Array(this.object.dicesList[0].length).fill(null);
|
||||
// }
|
||||
// this.object.dicesList[data.step + 1][data.die] = await this._newRoll(current.type, type);
|
||||
// }
|
||||
// *** Below this the current step become the next step ***
|
||||
this.object.currentStep += 1;
|
||||
|
||||
// if (isInitiativeRoll) {
|
||||
// // Initiative roll
|
||||
// this._actor.rollInitiative({
|
||||
// initiativeOptions: {
|
||||
// formula: formula.join("+"),
|
||||
// // updateTurn: true,
|
||||
// messageOptions: {
|
||||
// skillId: this.object.skill.id,
|
||||
// difficulty: this.object.difficulty.value,
|
||||
// difficultyHidden: this.object.difficulty.hidden,
|
||||
// useVoidPoint: this.object.useVoidPoint,
|
||||
// rerollInitiative: true,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// } else {}
|
||||
// Rebuild the roll
|
||||
await this._rebuildRoll();
|
||||
|
||||
// Notify the change to other players
|
||||
// game.l5r5e.sockets.refreshAppId(this.id);
|
||||
// Send the new roll in chat and delete the old message
|
||||
await this._toChatMessage();
|
||||
|
||||
// this.message._roll.l5r5e.history = {test: "yahooo"};
|
||||
|
||||
// await message.update({
|
||||
// data: {
|
||||
// roll: roll
|
||||
// }
|
||||
// });
|
||||
// message.render(false);
|
||||
|
||||
// console.log(roll.toJSON(), this.message);
|
||||
|
||||
// ui.chat.updateMessage(message);
|
||||
// ui.chat.postOne(message, false);
|
||||
|
||||
// if (game.user.isGM) {
|
||||
// message.delete();
|
||||
// } else {
|
||||
// game.l5r5e.sockets.deleteChatMessage(messageId);
|
||||
// }
|
||||
|
||||
// return this.close();
|
||||
// If a next step exist, rerender, else close
|
||||
if (this.object.dicesList[this.object.currentStep]) {
|
||||
return this.render();
|
||||
}
|
||||
return this.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -411,6 +606,7 @@ export class RollnKeepDialog extends FormApplication {
|
||||
*/
|
||||
static async onChatAction(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Extract card data
|
||||
const button = $(event.currentTarget);
|
||||
|
||||
@@ -21,7 +21,7 @@ export class RollL5r5e extends Roll {
|
||||
difficulty: 2,
|
||||
difficultyHidden: false,
|
||||
voidPointUsed: false,
|
||||
ringsUsed: 0,
|
||||
ringsUsed: null,
|
||||
totalSuccess: 0,
|
||||
totalBonus: 0,
|
||||
success: 0,
|
||||
@@ -30,6 +30,7 @@ export class RollL5r5e extends Roll {
|
||||
strife: 0,
|
||||
},
|
||||
history: null,
|
||||
isInitiativeRoll: false,
|
||||
};
|
||||
|
||||
// Parse flavor for stance and skillId
|
||||
@@ -68,24 +69,10 @@ export class RollL5r5e extends Roll {
|
||||
|
||||
// Roll
|
||||
super.evaluate({ minimize, maximize });
|
||||
|
||||
// Current terms - L5R Summary
|
||||
this.terms.forEach((term) => this._l5rSummary(term));
|
||||
|
||||
// Check inner L5R rolls - L5R Summary
|
||||
this._dice.forEach((term) => this._l5rSummary(term));
|
||||
|
||||
// Store final outputs
|
||||
this._rolled = true;
|
||||
this.l5r5e.dicesTypes.std = this.dice.some(
|
||||
(term) => term instanceof DiceTerm && !(term instanceof game.l5r5e.L5rBaseDie)
|
||||
); // ignore math symbols
|
||||
this.l5r5e.dicesTypes.l5r = this.dice.some((term) => term instanceof game.l5r5e.L5rBaseDie);
|
||||
this.l5r5e.summary.totalBonus = Math.max(0, this.l5r5e.summary.totalSuccess - this.l5r5e.summary.difficulty);
|
||||
this.l5r5e.summary.ringsUsed = this.dice.reduce(
|
||||
(acc, term) => (term instanceof game.l5r5e.RingDie ? acc + term.number : acc),
|
||||
0
|
||||
);
|
||||
|
||||
// Compute summary
|
||||
this.l5rSummary();
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -93,10 +80,43 @@ export class RollL5r5e extends Roll {
|
||||
/**
|
||||
* Summarise the total of success, strife... for L5R dices for the current roll
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
l5rSummary() {
|
||||
const summary = this.l5r5e.summary;
|
||||
|
||||
// Reset totals
|
||||
summary.success = 0;
|
||||
summary.explosive = 0;
|
||||
summary.opportunity = 0;
|
||||
summary.strife = 0;
|
||||
summary.totalSuccess = 0;
|
||||
|
||||
// Current terms - L5R Summary
|
||||
this.terms.forEach((term) => this._l5rTermSummary(term));
|
||||
|
||||
// Check inner L5R rolls - L5R Summary
|
||||
this._dice.forEach((term) => this._l5rTermSummary(term));
|
||||
|
||||
// Store final outputs
|
||||
this.l5r5e.dicesTypes.std = this.dice.some(
|
||||
(term) => term instanceof DiceTerm && !(term instanceof game.l5r5e.L5rBaseDie)
|
||||
); // ignore math symbols
|
||||
this.l5r5e.dicesTypes.l5r = this.dice.some((term) => term instanceof game.l5r5e.L5rBaseDie);
|
||||
summary.totalBonus = Math.max(0, summary.totalSuccess - summary.difficulty);
|
||||
summary.ringsUsed = this.dice.reduce(
|
||||
(acc, term) => (term instanceof game.l5r5e.RingDie ? acc + term.number : acc),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarise the total of success, strife... for L5R dices for the current term
|
||||
*
|
||||
* @param term
|
||||
* @private
|
||||
*/
|
||||
_l5rSummary(term) {
|
||||
_l5rTermSummary(term) {
|
||||
if (!(term instanceof game.l5r5e.L5rBaseDie)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@ export const RegisterHandlebars = function () {
|
||||
return game.i18n.localize("l5r5e.techniques." + techniqueName.toLowerCase());
|
||||
});
|
||||
|
||||
/* ------------------------------------ */
|
||||
/* Dice */
|
||||
/* ------------------------------------ */
|
||||
Handlebars.registerHelper("getDiceFaceUrl", function (diceClass, faceId) {
|
||||
return game.l5r5e[diceClass].getResultSrc(faceId);
|
||||
});
|
||||
|
||||
/* ------------------------------------ */
|
||||
/* Utility */
|
||||
/* ------------------------------------ */
|
||||
|
||||
@@ -174,7 +174,7 @@ export default class HooksL5r5e {
|
||||
}
|
||||
|
||||
/**
|
||||
* DiceSoNice Hook
|
||||
* DiceSoNice - Add L5R DicePresets
|
||||
*/
|
||||
static diceSoNiceReady(dice3d) {
|
||||
const texturePath = `${CONFIG.l5r5e.paths.assets}dices/default/3d/`;
|
||||
@@ -218,4 +218,16 @@ export default class HooksL5r5e {
|
||||
"d12"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* DiceSoNice - Do not show 3D roll for the Roll n Keep series
|
||||
*
|
||||
* @param {string} messageId
|
||||
* @param {object} context
|
||||
*/
|
||||
static diceSoNiceRollStart(messageId, context) {
|
||||
if (context.roll.l5r5e?.history) {
|
||||
context.blind = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,3 +129,4 @@ Hooks.on("renderSidebarTab", (app, html, data) => HooksL5r5e.renderSidebarTab(ap
|
||||
Hooks.on("renderChatMessage", (message, html, data) => HooksL5r5e.renderChatMessage(message, html, data));
|
||||
Hooks.on("renderCombatTracker", (app, html, data) => HooksL5r5e.renderCombatTracker(app, html, data));
|
||||
Hooks.on("renderCompendium", async (app, html, data) => HooksL5r5e.renderCompendium(app, html, data));
|
||||
Hooks.on("diceSoNiceRollStart", (messageId, context) => HooksL5r5e.diceSoNiceRollStart(messageId, context));
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
* Custom system settings register
|
||||
*/
|
||||
export const RegisterSettings = function () {
|
||||
/* ------------------------------------ */
|
||||
/* User settings */
|
||||
/* ------------------------------------ */
|
||||
game.settings.register("l5r5e", "rnk.deleteOldMessage", {
|
||||
name: "SETTINGS.RollNKeep.DeleteOldMessage",
|
||||
hint: "SETTINGS.RollNKeep.DeleteOldMessageHint",
|
||||
scope: "world",
|
||||
config: true,
|
||||
default: true,
|
||||
type: Boolean,
|
||||
});
|
||||
|
||||
/* ------------------------------------ */
|
||||
/* Update */
|
||||
/* ------------------------------------ */
|
||||
|
||||
@@ -25,6 +25,10 @@ export class SocketHandlerL5r5e {
|
||||
this._onRefreshAppId(data);
|
||||
break;
|
||||
|
||||
case "updateMessageIdAndRefresh":
|
||||
this._onUpdateMessageIdAndRefresh(data);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(new Error("This socket event is not supported"), data);
|
||||
break;
|
||||
@@ -40,9 +44,13 @@ export class SocketHandlerL5r5e {
|
||||
});
|
||||
}
|
||||
_onDeleteChatMessage(data) {
|
||||
// Only delete the message if the user is a GM (otherwise it have no real effect)
|
||||
// Currently only used in RnK
|
||||
if (!game.user.isGM || !game.settings.get("l5r5e", "rnk.deleteOldMessage")) {
|
||||
return;
|
||||
}
|
||||
const message = game.messages.get(data.messageId);
|
||||
// only delete the message if the user is a GM and the event emitter is one of the recipients
|
||||
if (game.user.isGM && message.data["whisper"].includes(data.userId)) {
|
||||
if (message) {
|
||||
message.delete();
|
||||
}
|
||||
}
|
||||
@@ -65,4 +73,25 @@ export class SocketHandlerL5r5e {
|
||||
}
|
||||
app.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change in app message and refresh (used in RnK)
|
||||
* @param appId
|
||||
* @param msgId
|
||||
*/
|
||||
updateMessageIdAndRefresh(appId, msgId) {
|
||||
game.socket.emit(SocketHandlerL5r5e.SOCKET_NAME, {
|
||||
type: "updateMessageIdAndRefresh",
|
||||
appId,
|
||||
msgId,
|
||||
});
|
||||
}
|
||||
_onUpdateMessageIdAndRefresh(data) {
|
||||
const app = Object.values(ui.windows).find((e) => e.id === data.appId);
|
||||
if (!app || !app.message || typeof app.refresh !== "function") {
|
||||
return;
|
||||
}
|
||||
app.message = game.messages.get(data.msgId);
|
||||
app.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,11 +47,11 @@
|
||||
<div class="l5r5e item-display dices-l5r">
|
||||
{{!-- Dices list --}}
|
||||
{{#each l5r5e.dices}}
|
||||
{{#if this.diceTypeL5r}}
|
||||
{{#each this.rolls}}
|
||||
<span class="l5r5e chat-dice">{{{this.result}}}</span>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#if this.diceTypeL5r}}
|
||||
{{#each this.rolls}}
|
||||
<span class="l5r5e chat-dice">{{{this.result}}}</span>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
|
||||
{{!-- Roll & Keep Button --}}
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
</legend>
|
||||
|
||||
{{#each data.swapDiceFaces.rings}}
|
||||
<div class="dice dropbox faces-change" data-type="swap" data-face="{{id}}" data-die="RingDie">
|
||||
<img src="{{img}}" alt="{{id}}" />
|
||||
<div class="dice dropbox faces-change" data-type="swap" data-face="{{this}}" data-die="RingDie">
|
||||
<img src="{{getDiceFaceUrl 'RingDie' this}}" alt="{{this}}" />
|
||||
</div>
|
||||
{{/each}}
|
||||
</td>
|
||||
@@ -83,15 +83,20 @@
|
||||
<tr>
|
||||
{{#each item as |dice idxDie|}}
|
||||
<td>
|
||||
<div
|
||||
class="dice {{this.choice}} {{#ifCond ../../data.currentStep '==' idxStep }}draggable{{/ifCond}}"
|
||||
data-step="{{idxStep}}"
|
||||
data-die="{{idxDie}}"
|
||||
>
|
||||
{{#if this.img}}
|
||||
<img src="{{this.img}}" alt="{{idxStep}}_{{idxDie}}" />
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if face}}
|
||||
<div
|
||||
class="dice {{choice}}{{#ifCond face '&&' (ifCond ../../data.currentStep '==' idxStep) }} draggable{{/ifCond}}"
|
||||
data-step="{{idxStep}}"
|
||||
data-die="{{idxDie}}"
|
||||
>
|
||||
|
||||
{{#if newFace}}
|
||||
<img src="{{getDiceFaceUrl type newFace}}" alt="{{idxStep}}_{{idxDie}}" />
|
||||
{{else}}
|
||||
<img src="{{getDiceFaceUrl type face}}" alt="{{idxStep}}_{{idxDie}}" />
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</td>
|
||||
{{/each}}
|
||||
</tr>
|
||||
@@ -119,8 +124,8 @@
|
||||
</legend>
|
||||
|
||||
{{#each data.swapDiceFaces.skills}}
|
||||
<div class="dice dropbox faces-change" data-type="swap" data-face="{{id}}" data-die="AbilityDie">
|
||||
<img src="{{img}}" alt="{{id}}" />
|
||||
<div class="dice dropbox faces-change" data-type="swap" data-face="{{this}}" data-die="AbilityDie">
|
||||
<img src="{{getDiceFaceUrl 'AbilityDie' this}}" alt="{{this}}" />
|
||||
</div>
|
||||
{{/each}}
|
||||
</td>
|
||||
@@ -132,20 +137,24 @@
|
||||
</button>
|
||||
|
||||
{{else}}
|
||||
{{!-- Non editable DiceList history (view for others players) --}}
|
||||
{{!-- Non editable DiceList history --}}
|
||||
<table>
|
||||
{{#each data.dicesList as |item idxStep|}}
|
||||
<tr>
|
||||
{{#each item as |dice idxDie|}}
|
||||
<td>
|
||||
<div class="dice {{this.choice}}">
|
||||
{{#if this.img}}
|
||||
<img src="{{this.img}}" alt="{{idxStep}}_{{idxDie}}" />
|
||||
{{/if}}
|
||||
</div>
|
||||
</td>
|
||||
{{/each}}
|
||||
</tr>
|
||||
<tr>
|
||||
{{#each item as |dice idxDie|}}
|
||||
<td>
|
||||
{{#if face}}
|
||||
<div class="dice {{choice}}">
|
||||
{{#if newFace}}
|
||||
<img src="{{getDiceFaceUrl type newFace}}" alt="{{idxStep}}_{{idxDie}}" />
|
||||
{{else}}
|
||||
<img src="{{getDiceFaceUrl type face}}" alt="{{idxStep}}_{{idxDie}}" />
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</td>
|
||||
{{/each}}
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/if}}
|
||||
|
||||
Reference in New Issue
Block a user