427 lines
12 KiB
JavaScript
427 lines
12 KiB
JavaScript
/**
|
|
* L5R Dice Roll n Keep dialog
|
|
* @extends {FormApplication}
|
|
*/
|
|
export class RollnKeepDialog extends FormApplication {
|
|
/**
|
|
* Player choice list
|
|
*/
|
|
static CHOICES = {
|
|
discard: "discard",
|
|
keep: "keep",
|
|
nothing: null,
|
|
reroll: "reroll",
|
|
// reserve: "reserve",
|
|
swap: "swap",
|
|
};
|
|
|
|
/**
|
|
* The current ChatMessage where we come from
|
|
* @param {ChatMessage} message
|
|
*/
|
|
message = null;
|
|
|
|
/**
|
|
* The current Roll
|
|
* @param {Roll} roll
|
|
*/
|
|
roll = null;
|
|
|
|
/**
|
|
* Payload Object
|
|
*/
|
|
object = {
|
|
currentStep: 0,
|
|
submitDisabled: true,
|
|
swapDiceFaces: {
|
|
rings: [],
|
|
skills: [],
|
|
},
|
|
dicesList: [[]],
|
|
};
|
|
|
|
/**
|
|
* Assign the default options
|
|
* @override
|
|
*/
|
|
static get defaultOptions() {
|
|
return mergeObject(super.defaultOptions, {
|
|
id: "l5r5e-roll-n-keep-dialog",
|
|
classes: ["l5r5e", "roll-n-keep-dialog"],
|
|
template: CONFIG.l5r5e.paths.templates + "dice/roll-n-keep-dialog.html",
|
|
title: game.i18n.localize("l5r5e.roll_n_keep.title"),
|
|
width: 660,
|
|
height: 454,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Define a unique and dynamic element ID for the rendered ActorSheet application
|
|
*/
|
|
get id() {
|
|
return `l5r5e-roll-n-keep-dialog-${this.message._id}`;
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
|
|
console.log(this.object); // TODO TMP
|
|
}
|
|
|
|
/**
|
|
* Refresh data (used from socket)
|
|
*/
|
|
async refresh() {
|
|
if (!this.message) {
|
|
return;
|
|
}
|
|
this._initialize();
|
|
this.render(false);
|
|
}
|
|
|
|
/**
|
|
* Render
|
|
* @param {boolean} force
|
|
* @param {RenderOptions} options
|
|
* @returns {Application}
|
|
* @override
|
|
*/
|
|
render(force = null, options = {}) {
|
|
if (!this.message) {
|
|
return;
|
|
}
|
|
return super.render(force, options);
|
|
}
|
|
|
|
/**
|
|
* Initialize the dialog with the message
|
|
* @private
|
|
*/
|
|
_initialize() {
|
|
if (!this.message) {
|
|
return;
|
|
}
|
|
|
|
// Get the roll
|
|
this.roll = game.l5r5e.RollL5r5e.fromData(this.message.roll);
|
|
|
|
// Already history
|
|
if (Array.isArray(this.roll.l5r5e.history)) {
|
|
this.object.currentStep = this.roll.l5r5e.history.length;
|
|
this.object.dicesList = this.roll.l5r5e.history;
|
|
return;
|
|
}
|
|
|
|
// New
|
|
this.roll.terms.forEach((term) => {
|
|
if (typeof term !== "object") {
|
|
return;
|
|
}
|
|
term.results.forEach((res) => {
|
|
this.object.dicesList[0].push({
|
|
type: term.constructor.name,
|
|
face: res.result,
|
|
img: term.constructor.getResultSrc(res.result),
|
|
choice: RollnKeepDialog.CHOICES.nothing,
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Fill the dices faces
|
|
* @private
|
|
*/
|
|
_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) };
|
|
});
|
|
|
|
// 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) };
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create drag-and-drop workflow handlers for this Application
|
|
* @return An array of DragDrop handlers
|
|
*/
|
|
_createDragDropHandlers() {
|
|
return [
|
|
new DragDrop({
|
|
dragSelector: ".dice.draggable",
|
|
dropSelector: ".dropbox",
|
|
permissions: { dragstart: this._canDragStart.bind(this), drop: this._canDragDrop.bind(this) },
|
|
callbacks: { dragstart: this._onDragStart.bind(this), drop: this._onDropItem.bind(this) },
|
|
}),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Callback actions which occur at the beginning of a drag start workflow.
|
|
* @param {DragEvent} event The originating DragEvent
|
|
*/
|
|
_onDragStart(event) {
|
|
const target = $(event.currentTarget);
|
|
event.dataTransfer.setData(
|
|
"text/plain",
|
|
JSON.stringify({
|
|
step: target.data("step"),
|
|
die: target.data("die"),
|
|
})
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Construct and return the data object used to render the HTML template for this form application.
|
|
* @param options
|
|
* @return {Object}
|
|
*/
|
|
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;
|
|
|
|
return {
|
|
...super.getData(options),
|
|
data: this.object,
|
|
l5r5e: this.message._roll.l5r5e,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Listen to html elements
|
|
* @override
|
|
*/
|
|
activateListeners(html) {
|
|
super.activateListeners(html);
|
|
|
|
// *** Everything below here is only needed if the sheet is editable ***
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
|
|
// Finalize Button
|
|
html.find("#finalize").on("click", (event) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
if (!this.object.submitDisabled) {
|
|
this.submit();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Handle dropped items
|
|
*/
|
|
async _onDropItem(event) {
|
|
// *** Everything below here is only needed if the sheet is editable ***
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
|
|
const type = $(event.currentTarget).data("type");
|
|
const json = event.dataTransfer.getData("text/plain");
|
|
if (!json || !Object.values(RollnKeepDialog.CHOICES).some((e) => !!e && e === type)) {
|
|
return;
|
|
}
|
|
|
|
const data = JSON.parse(json);
|
|
if (!data) {
|
|
return;
|
|
}
|
|
|
|
const current = this.object.dicesList[data.step][data.die];
|
|
delete current.newFace;
|
|
|
|
// FaceSwap
|
|
if (type === RollnKeepDialog.CHOICES.swap) {
|
|
// Dice Type Ring/Skill
|
|
const diceType = $(event.currentTarget).data("die");
|
|
const diceNewFace = $(event.currentTarget).data("face");
|
|
|
|
if (current.type !== diceType || current.face === diceNewFace) {
|
|
current.choice = RollnKeepDialog.CHOICES.nothing;
|
|
this.render(false);
|
|
return false;
|
|
}
|
|
|
|
current.newFace = diceNewFace;
|
|
}
|
|
|
|
current.choice = type;
|
|
current.img = game.l5r5e[current.type].getResultSrc(current.newFace ?? current.face);
|
|
|
|
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
|
|
*/
|
|
_getKeepCount() {
|
|
return this.object.dicesList.reduce((acc, step) => {
|
|
return (
|
|
acc +
|
|
step.reduce((acc2, die) => {
|
|
if (
|
|
!!die &&
|
|
[
|
|
RollnKeepDialog.CHOICES.keep,
|
|
RollnKeepDialog.CHOICES.reroll,
|
|
RollnKeepDialog.CHOICES.swap,
|
|
].includes(die.choice)
|
|
) {
|
|
acc2 = acc2 + 1;
|
|
}
|
|
return acc2;
|
|
}, 0)
|
|
);
|
|
}, 0);
|
|
}
|
|
|
|
/**
|
|
* This method is called upon form submission after form data is validated
|
|
* @param event The initial triggering submission event
|
|
* @param formData The object of validated form data with which to update the object
|
|
* @returns A Promise which resolves once the update operation has completed
|
|
* @override
|
|
*/
|
|
async _updateObject(event, formData) {
|
|
// *** Everything below here is only needed if the sheet is editable ***
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
|
|
console.log("**** _updateObject");
|
|
|
|
console.log(this.object.dicesList);
|
|
|
|
// 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);
|
|
// }
|
|
|
|
// 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 {}
|
|
|
|
// Notify the change to other players
|
|
// game.l5r5e.sockets.refreshAppId(this.id);
|
|
|
|
// 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();
|
|
}
|
|
|
|
/**
|
|
* Handle execution of a chat card action via a click event on the RnK button
|
|
* @param {Event} event The originating click event
|
|
* @returns {Promise} A promise which resolves once the handler workflow is complete
|
|
*/
|
|
static async onChatAction(event) {
|
|
event.preventDefault();
|
|
|
|
// Extract card data
|
|
const button = $(event.currentTarget);
|
|
button.attr("disabled", true);
|
|
const card = button.parents(".l5r5e.item-display.dices-l5r");
|
|
const messageId = card.parents(".chat-message").data("message-id");
|
|
|
|
new RollnKeepDialog(messageId).render(true);
|
|
|
|
// Re-enable the button
|
|
button.attr("disabled", false);
|
|
}
|
|
}
|