added DP on technique (wip)
This commit is contained in:
@@ -279,4 +279,37 @@ export class ActorL5r5e extends Actor {
|
||||
|
||||
return isPrepared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Status Rank of this actor
|
||||
* @return {number|null}
|
||||
*/
|
||||
get statusRank() {
|
||||
if (!["character", "npc"].includes(this.data.type)) {
|
||||
return null;
|
||||
}
|
||||
return Math.floor(this.data.data.social.status / 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Intrigue Rank of this actor
|
||||
* @return {number|null}
|
||||
*/
|
||||
get intrigueRank() {
|
||||
if (!["character", "npc"].includes(this.data.type)) {
|
||||
return null;
|
||||
}
|
||||
return this.data.type === "npc" ? this.data.data.conflict_rank.social : this.data.data.identity.school_rank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Martial Rank of this actor
|
||||
* @return {number|null}
|
||||
*/
|
||||
get martialRank() {
|
||||
if (!["character", "npc"].includes(this.data.type)) {
|
||||
return null;
|
||||
}
|
||||
return this.data.type === "npc" ? this.data.data.conflict_rank.martial : this.data.data.identity.school_rank;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,33 +277,14 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
|
||||
return;
|
||||
}
|
||||
|
||||
// *** Dice event on Skills clic ***
|
||||
html.find(".dice-picker").on("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const li = $(event.currentTarget);
|
||||
let skillId = li.data("skill") || null;
|
||||
// Dice event on Skills clic
|
||||
html.find(".dice-picker").on("click", this._openDicePickerForSkill.bind(this));
|
||||
|
||||
const weaponId = li.data("weapon-id") || null;
|
||||
if (weaponId) {
|
||||
skillId = this._getWeaponSkillId(weaponId);
|
||||
}
|
||||
|
||||
new game.l5r5e.DicePickerDialog({
|
||||
ringId: li.data("ring") || null,
|
||||
skillId: skillId,
|
||||
skillCatId: li.data("skillcat") || null,
|
||||
isInitiativeRoll: li.data("initiative") || false,
|
||||
actor: this.actor,
|
||||
}).render(true);
|
||||
});
|
||||
// Dice event on Technique clic
|
||||
html.find(".dice-picker-tech").on("click", this._openDicePickerForTechnique.bind(this));
|
||||
|
||||
// Prepared (Initiative)
|
||||
html.find(".prepared-control").on("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this._switchPrepared();
|
||||
});
|
||||
html.find(".prepared-control").on("click", this._switchPrepared.bind(this));
|
||||
|
||||
// Equipped / Readied
|
||||
html.find(".equip-readied-control").on("click", this._switchEquipReadied.bind(this));
|
||||
@@ -317,9 +298,13 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
|
||||
|
||||
/**
|
||||
* Switch the state "prepared" (initiative)
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_switchPrepared() {
|
||||
_switchPrepared(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.actor.data.data.prepared = !this.actor.data.data.prepared;
|
||||
this.actor.update({
|
||||
data: {
|
||||
@@ -594,4 +579,68 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the dice-picker for this skill
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_openDicePickerForSkill(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const li = $(event.currentTarget);
|
||||
let skillId = li.data("skill") || null;
|
||||
|
||||
const weaponId = li.data("weapon-id") || null;
|
||||
if (weaponId) {
|
||||
skillId = this._getWeaponSkillId(weaponId);
|
||||
}
|
||||
|
||||
new game.l5r5e.DicePickerDialog({
|
||||
ringId: li.data("ring") || null,
|
||||
skillId: skillId,
|
||||
skillCatId: li.data("skillcat") || null,
|
||||
isInitiativeRoll: li.data("initiative") || false,
|
||||
actor: this.actor,
|
||||
}).render(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the dice-picker for this technique
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_openDicePickerForTechnique(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const itemId = $(event.currentTarget).data("item-id") || null;
|
||||
const item = this.actor.items.get(itemId);
|
||||
if (!item || item.type !== "technique" || !item.data.data.skill) {
|
||||
return;
|
||||
}
|
||||
const itemData = item.data.data;
|
||||
|
||||
const difficulties = game.l5r5e.DicePickerDialog.parseDifficulty(this.actor, itemData.difficulty);
|
||||
if (!difficulties) {
|
||||
// do not block if no target or not found
|
||||
difficulties.difficulty = null;
|
||||
difficulties.difficultyHidden = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const skills = game.l5r5e.DicePickerDialog.parseSkills(itemData.skill);
|
||||
if (!skills) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(difficulties, skills); // todo tmp
|
||||
|
||||
new game.l5r5e.DicePickerDialog({
|
||||
actor: this.actor,
|
||||
ringId: itemData.ring || null,
|
||||
...difficulties,
|
||||
...skills,
|
||||
}).render(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ export class CharacterGeneratorDialog extends FormApplication {
|
||||
generate: {
|
||||
attributes: true,
|
||||
demeanor: true,
|
||||
identity: true,
|
||||
items: true,
|
||||
name: true,
|
||||
narrative: true,
|
||||
peculiarities: true,
|
||||
social: true,
|
||||
techniques: true,
|
||||
},
|
||||
};
|
||||
@@ -40,7 +40,7 @@ export class CharacterGeneratorDialog extends FormApplication {
|
||||
classes: ["l5r5e", "character-generator-dialog"],
|
||||
template: CONFIG.l5r5e.paths.templates + "actors/character-generator-dialog.html",
|
||||
title: game.i18n.localize("l5r5e.char_generator.title"),
|
||||
// width: 600,
|
||||
width: 450,
|
||||
// height: 360,
|
||||
resizable: false,
|
||||
closeOnSubmit: false,
|
||||
@@ -72,13 +72,9 @@ export class CharacterGeneratorDialog extends FormApplication {
|
||||
const actorDatas = this.actor.data.data;
|
||||
|
||||
// Identity
|
||||
//this.object.age = actorDatas.age;
|
||||
this.object.clan = actorDatas.identity.clan || "random";
|
||||
this.object.gender = actorDatas.identity.female
|
||||
? "female"
|
||||
: actorDatas.identity.female === false
|
||||
? "male"
|
||||
: "random";
|
||||
this.object.gender =
|
||||
actorDatas.identity.female === null ? "random" : actorDatas.identity.female ? "female" : "male";
|
||||
|
||||
// Rings
|
||||
this.object.avgRings = CharacterGenerator.sanitizeMinMax(
|
||||
@@ -106,8 +102,8 @@ export class CharacterGeneratorDialog extends FormApplication {
|
||||
clanList: [{ id: "random", label: game.i18n.localize("l5r5e.random") }, ...clans],
|
||||
genderList: [
|
||||
{ id: "random", label: game.i18n.localize("l5r5e.random") },
|
||||
{ id: "male", label: game.i18n.localize("l5r5e.gender.male") },
|
||||
{ id: "female", label: game.i18n.localize("l5r5e.gender.female") },
|
||||
{ id: "male", label: game.i18n.localize("l5r5e.social.gender.male") },
|
||||
{ id: "female", label: game.i18n.localize("l5r5e.social.gender.female") },
|
||||
],
|
||||
data: this.object,
|
||||
};
|
||||
|
||||
@@ -87,9 +87,6 @@ export class CharacterGenerator {
|
||||
family: "",
|
||||
gender: "male",
|
||||
age: 15,
|
||||
honor: 30,
|
||||
glory: 30,
|
||||
status: 30,
|
||||
maritalStatus: "",
|
||||
};
|
||||
|
||||
@@ -114,27 +111,24 @@ export class CharacterGenerator {
|
||||
this.data.clan = clanName;
|
||||
this.data.family = CharacterGenerator._getRandomFamily(clanName);
|
||||
this.data.gender = gender;
|
||||
this.data.age = CharacterGenerator._randomInt(15, this.data.avgRingsValue * 10 + 15);
|
||||
this.genSocialStanding();
|
||||
this.data.maritalStatus = this.genMaritalStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the gender is male
|
||||
* Return true if the gender is Female
|
||||
* @return {boolean}
|
||||
*/
|
||||
get isMale() {
|
||||
return this.data.gender === "male";
|
||||
get isFemale() {
|
||||
return this.data.gender === "female";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random value for this array
|
||||
* @param array
|
||||
* @return {*}
|
||||
* @return {String}
|
||||
* @private
|
||||
*/
|
||||
static _getRandomArrayValue(array) {
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
return array[Math.floor(Math.random() * array.length)] ?? "";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,30 +197,63 @@ export class CharacterGenerator {
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
async getRandomizedName() {
|
||||
let table = `Japanese names (${this.isMale ? "Male" : "Female"})`;
|
||||
// switch (this.data.clan) {
|
||||
// case "ivory_kingdoms": table = 'Ivory kingdoms names'; break;
|
||||
// case "qamarist": table = 'Qamarist names'; break;
|
||||
// case "ujik": table = 'Ujik names'; break;
|
||||
// }
|
||||
|
||||
let table = `Japanese names (${this.isFemale ? "Female" : "Male"})`;
|
||||
switch (this.data.clan) {
|
||||
case "ivory_kingdoms":
|
||||
table = "Ivory Kingdoms names";
|
||||
break;
|
||||
case "qamarist":
|
||||
table = "Qamarist names";
|
||||
break;
|
||||
case "ujik":
|
||||
table = "Ujik names";
|
||||
break;
|
||||
}
|
||||
const randomNames = await game.l5r5e.HelpersL5r5e.drawManyFromPack("l5r5e.core-name-tables", table, 1, {
|
||||
displayChat: false,
|
||||
});
|
||||
return this.data.family + " " + randomNames?.results[0]?.data.text || "";
|
||||
return this.data.family + " " + (randomNames?.results[0]?.data.text || "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the actor age
|
||||
* @param {number} avgRingsValue
|
||||
* @return {number}
|
||||
*/
|
||||
static genAge(avgRingsValue) {
|
||||
return CharacterGenerator._randomInt(15, avgRingsValue * 10 + 15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the marriage state
|
||||
* @param {number} age
|
||||
* @return {string} unmarried|betrothed|married|widowed
|
||||
*/
|
||||
static genMaritalStatus(age) {
|
||||
const rng = Math.random();
|
||||
if (age < 20) {
|
||||
return rng < 0.1 ? "married" : rng < 0.4 ? "betrothed" : "unmarried";
|
||||
}
|
||||
if (age < 30) {
|
||||
return rng < 0.4 ? "married" : rng < 0.7 ? "betrothed" : "unmarried";
|
||||
}
|
||||
return rng < 0.8 ? "married" : rng < 0.9 ? "widowed" : "unmarried";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Honor, Glory and Status values
|
||||
* @param {number} age
|
||||
* @param {string} clan
|
||||
* @return {{honor: number, glory: number, status: number}}
|
||||
*/
|
||||
genSocialStanding() {
|
||||
static genSocialStanding(age, clan) {
|
||||
const karma = Math.random() < 0.66 ? 1 : -1;
|
||||
const rng = (initial, variation) => {
|
||||
return initial + CharacterGenerator._randomInt(5, variation) * karma;
|
||||
};
|
||||
|
||||
let honor = rng(34, this.data.age / 2);
|
||||
switch (this.data.clan) {
|
||||
let honor = rng(34, age / 2);
|
||||
switch (clan) {
|
||||
case "lion":
|
||||
honor += 10;
|
||||
break;
|
||||
@@ -234,24 +261,11 @@ export class CharacterGenerator {
|
||||
honor -= 10;
|
||||
break;
|
||||
}
|
||||
this.data.honor = honor;
|
||||
this.data.glory = rng(40, this.data.age / 3);
|
||||
this.data.status = rng(30, this.data.age / 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the marriage state
|
||||
* @return {string} unmarried|betrothed|married|widowed
|
||||
*/
|
||||
genMaritalStatus() {
|
||||
const rng = Math.random();
|
||||
if (this.data.age < 20) {
|
||||
return rng < 0.1 ? "married" : rng < 0.4 ? "betrothed" : "unmarried";
|
||||
}
|
||||
if (this.data.age < 30) {
|
||||
return rng < 0.4 ? "married" : rng < 0.7 ? "betrothed" : "unmarried";
|
||||
}
|
||||
return rng < 0.8 ? "married" : rng < 0.9 ? "widowed" : "unmarried";
|
||||
return {
|
||||
honor,
|
||||
glory: rng(40, age / 3),
|
||||
status: rng(30, age / 4),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,8 +274,8 @@ export class CharacterGenerator {
|
||||
* @param {ActorL5r5e} actor Actor object
|
||||
* @param {Object} generate
|
||||
* @param {boolean} generate.name If true generate a new name
|
||||
* @param {boolean} generate.attributes If true generate rings, attributes, skills and confrontation ranks
|
||||
* @param {boolean} generate.social If true generate Social Standing
|
||||
* @param {boolean} generate.identity If true generate Clan, Gender, Age, Marital status
|
||||
* @param {boolean} generate.attributes If true generate Rings, attributes, skills and confrontation ranks
|
||||
* @param {boolean} generate.demeanor If true generate Demeanor and rings affinities
|
||||
* @param {boolean} generate.peculiarities If true generate Advantage and Disadvantage
|
||||
* @param {boolean} generate.items If true generate Armor, Weapons and Items
|
||||
@@ -274,7 +288,7 @@ export class CharacterGenerator {
|
||||
generate = {
|
||||
name: true,
|
||||
attributes: true,
|
||||
social: true,
|
||||
identity: true,
|
||||
demeanor: true,
|
||||
peculiarities: true,
|
||||
items: true,
|
||||
@@ -284,36 +298,45 @@ export class CharacterGenerator {
|
||||
) {
|
||||
const actorDatas = actor.data.data;
|
||||
|
||||
console.log(generate); // TODO tmp
|
||||
// Need to set some required values
|
||||
this.data.age = actorDatas.identity.age || CharacterGenerator.genAge(this.data.avgRingsValue);
|
||||
this.data.maritalStatus =
|
||||
actorDatas.identity.marital_status || CharacterGenerator.genMaritalStatus(this.data.age);
|
||||
|
||||
actorDatas.identity.clan = this.data.clan;
|
||||
actorDatas.identity.family = this.data.family;
|
||||
actorDatas.identity.female = this.isFemale;
|
||||
|
||||
// Name
|
||||
const newName = generate.name ? await this.getRandomizedName() : actor.data.name;
|
||||
|
||||
actorDatas.identity.age = this.data.age;
|
||||
actorDatas.identity.clan = this.data.clan;
|
||||
actorDatas.identity.family = this.data.family;
|
||||
actorDatas.identity.female = this.data.gender === "female";
|
||||
// Identity
|
||||
if (generate.identity) {
|
||||
actorDatas.identity.age = CharacterGenerator.genAge(this.data.avgRingsValue);
|
||||
actorDatas.identity.marital_status = CharacterGenerator.genMaritalStatus(this.data.age);
|
||||
this._generateNotes(actorDatas);
|
||||
}
|
||||
|
||||
// Img (only if default)
|
||||
// Img (only if system defaults)
|
||||
const folder = "systems/l5r5e/assets/icons/actors";
|
||||
const newImg = [
|
||||
`${folder}/npc.svg`,
|
||||
`${folder}/traditional-japanese-man.svg`,
|
||||
`${folder}/traditional-japanese-woman.svg`,
|
||||
].includes(actor.data.img)
|
||||
? `${folder}/traditional-japanese-${this.isMale ? "man" : "woman"}.svg`
|
||||
? `${folder}/traditional-japanese-${this.isFemale ? "woman" : "man"}.svg`
|
||||
: actor.data.img;
|
||||
|
||||
// Generate attributes (rings, attributes, skills, confrontation ranks)
|
||||
// Generate attributes & Social Standing
|
||||
if (generate.attributes) {
|
||||
// Generate attributes (rings, attributes, skills, confrontation ranks)
|
||||
this._generateAttributes(actorDatas);
|
||||
}
|
||||
|
||||
// Social Standing
|
||||
if (generate.social) {
|
||||
actorDatas.social.honor = this.data.honor;
|
||||
actorDatas.social.glory = this.data.glory;
|
||||
actorDatas.social.status = this.data.status;
|
||||
// Social Standing
|
||||
const social = CharacterGenerator.genSocialStanding(this.data.age, this.data.clan);
|
||||
actorDatas.social.honor = social.honor;
|
||||
actorDatas.social.glory = social.glory;
|
||||
actorDatas.social.status = social.status;
|
||||
}
|
||||
|
||||
// Demeanor (npc only)
|
||||
@@ -321,7 +344,7 @@ export class CharacterGenerator {
|
||||
this._generateDemeanor(actorDatas);
|
||||
}
|
||||
|
||||
// Items types
|
||||
// Item types
|
||||
if (generate.peculiarities || generate.items || generate.techniques) {
|
||||
const newItemsData = [];
|
||||
|
||||
@@ -346,7 +369,7 @@ export class CharacterGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
// Narrative
|
||||
// TODO Narrative
|
||||
if (generate.narrative) {
|
||||
this._generateNarrative(actorDatas);
|
||||
}
|
||||
@@ -358,8 +381,8 @@ export class CharacterGenerator {
|
||||
data: actorDatas,
|
||||
};
|
||||
}
|
||||
|
||||
//<editor-fold desc="toActor generators">
|
||||
|
||||
/**
|
||||
* Generate attributes (rings, attributes, skills, confrontation ranks)
|
||||
* @param {DocumentData.data} actorDatas
|
||||
@@ -386,9 +409,11 @@ export class CharacterGenerator {
|
||||
(skillName) => (actorDatas.skills[skillName] = Math.floor(Math.random() * stats.max))
|
||||
);
|
||||
|
||||
// Confrontation ranks
|
||||
actorDatas.conflict_rank.martial = this.data.avgRingsValue + actorDatas.skills.martial;
|
||||
actorDatas.conflict_rank.social = this.data.avgRingsValue + actorDatas.skills.social;
|
||||
// Confrontation ranks (npc only)
|
||||
if (actorDatas.conflict_rank) {
|
||||
actorDatas.conflict_rank.martial = this.data.avgRingsValue + actorDatas.skills.martial;
|
||||
actorDatas.conflict_rank.social = this.data.avgRingsValue + actorDatas.skills.social;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -594,27 +619,30 @@ export class CharacterGenerator {
|
||||
} // fr techCfg
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill notes with some values that don't appear in sheet
|
||||
* @param {DocumentData.data} actorDatas
|
||||
* @private
|
||||
*/
|
||||
_generateNotes(actorDatas) {
|
||||
actorDatas.notes =
|
||||
`<p>${game.i18n.localize("l5r5e.social.age")}: ${this.data.age}</p>` +
|
||||
`<p>${game.i18n.localize("l5r5e.social.gender.title")}: ${game.i18n.localize(
|
||||
"l5r5e.social.gender." + this.data.gender
|
||||
)}</p>` +
|
||||
`<p>${game.i18n.localize("l5r5e.clan")}: ${game.i18n.localize("l5r5e.clans." + this.data.clan)}</p>` +
|
||||
`<p>${game.i18n.localize("l5r5e.social.marital_status.title")}: ${game.i18n.localize(
|
||||
"l5r5e.social.marital_status." + this.data.maritalStatus
|
||||
)}</p>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Narrative fluff
|
||||
* @param {DocumentData.data} actorDatas
|
||||
* @private
|
||||
*/
|
||||
_generateNarrative(actorDatas) {
|
||||
// Fill notes with some values that don't appear in sheet
|
||||
actorDatas.notes =
|
||||
`<p>${game.i18n.localize("l5r5e.char_generator.age")}: ${this.data.age}</p>` +
|
||||
`<p>${game.i18n.localize("l5r5e.gender.title")}: ${game.i18n.localize(
|
||||
"l5r5e.gender." + this.data.gender
|
||||
)}</p>` +
|
||||
`<p>${game.i18n.localize("l5r5e.clan")}: ${game.i18n.localize("l5r5e.clans." + this.data.clan)}</p>` +
|
||||
`<p>${game.i18n.localize("l5r5e.char_generator.marital_status.title")}: ${game.i18n.localize(
|
||||
"l5r5e.char_generator.marital_status." + this.data.maritalStatus
|
||||
)}</p>`;
|
||||
|
||||
// data: {
|
||||
// "notes": "",
|
||||
// "description": "",
|
||||
// },
|
||||
// actorDatas.description = '';
|
||||
}
|
||||
//</editor-fold>
|
||||
}
|
||||
|
||||
@@ -140,3 +140,7 @@ L5R5E.families.set("oriole", ["Tsi"]);
|
||||
L5R5E.families.set("ox", ["Morito"]);
|
||||
L5R5E.families.set("sparrow", ["Suzume"]);
|
||||
L5R5E.families.set("tortoise", ["Kasuga"]);
|
||||
// External
|
||||
L5R5E.families.set("ivory_kingdoms", []);
|
||||
L5R5E.families.set("qamarist", []);
|
||||
L5R5E.families.set("ujik", []);
|
||||
|
||||
@@ -11,10 +11,14 @@ export class DicePickerDialog extends FormApplication {
|
||||
_actor = null;
|
||||
|
||||
/**
|
||||
* If GM as set to hidden, lock the player choice so he cannot look the TN
|
||||
* @type {boolean}
|
||||
* If GM or Constructor set to hidden, lock the player choice, so he cannot look the TN
|
||||
* @type {{gm: boolean, option: boolean}}
|
||||
* @private
|
||||
*/
|
||||
_difficultyHiddenIsLock = false;
|
||||
_difficultyHiddenIsLock = {
|
||||
gm: false,
|
||||
option: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Payload Object
|
||||
@@ -135,6 +139,9 @@ export class DicePickerDialog extends FormApplication {
|
||||
}
|
||||
|
||||
// DifficultyHidden
|
||||
if (options.difficultyHidden) {
|
||||
this._difficultyHiddenIsLock.option = true;
|
||||
}
|
||||
this.difficultyHidden = !!options.difficultyHidden;
|
||||
|
||||
// InitiativeRoll
|
||||
@@ -229,8 +236,8 @@ export class DicePickerDialog extends FormApplication {
|
||||
* @param difficulty
|
||||
*/
|
||||
set difficulty(difficulty) {
|
||||
difficulty = parseInt(difficulty) || null;
|
||||
if (difficulty < 0) {
|
||||
difficulty = parseInt(difficulty);
|
||||
if (isNaN(difficulty) || difficulty < 0) {
|
||||
difficulty = 2;
|
||||
}
|
||||
this.object.difficulty.value = difficulty;
|
||||
@@ -242,8 +249,8 @@ export class DicePickerDialog extends FormApplication {
|
||||
*/
|
||||
set difficultyHidden(isHidden) {
|
||||
// If GM hide, then player choice don't matter
|
||||
this._difficultyHiddenIsLock = game.settings.get("l5r5e", "initiative-difficulty-hidden");
|
||||
if (this._difficultyHiddenIsLock) {
|
||||
this._difficultyHiddenIsLock.gm = game.settings.get("l5r5e", "initiative-difficulty-hidden");
|
||||
if (this._difficultyHiddenIsLock.gm || this._difficultyHiddenIsLock.option) {
|
||||
isHidden = true;
|
||||
}
|
||||
this.object.difficulty.hidden = !!isHidden;
|
||||
@@ -274,7 +281,7 @@ export class DicePickerDialog extends FormApplication {
|
||||
canUseVoidPoint:
|
||||
this.object.difficulty.addVoidPoint || !this._actor || this._actor.data.data.void_points.value > 0,
|
||||
disableSubmit: this.object.skill.value < 1 && this.object.ring.value < 1,
|
||||
difficultyHiddenIsLock: this._difficultyHiddenIsLock,
|
||||
difficultyHiddenIsLock: this._difficultyHiddenIsLock.gm || this._difficultyHiddenIsLock.option,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -553,4 +560,150 @@ export class DicePickerDialog extends FormApplication {
|
||||
|
||||
return game.user.assignHotbarMacro(macro, "auto"); // 1st available
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the difficulty from technique
|
||||
*
|
||||
* Exemples :
|
||||
* "@T:vigilance"
|
||||
* "@T:vigilance|min"
|
||||
* "@T:vigilance|max"
|
||||
* "@T:vigilance|max(statusRank)"
|
||||
* "@T:intrigueRank"
|
||||
* "@T:martialRank"
|
||||
* "@T:statusRank|max"
|
||||
* "@T:strife.value|max"
|
||||
*
|
||||
* @param {string|number} difficulty
|
||||
* @return {{difficulty: number, difficultyHidden: boolean}|boolean}
|
||||
*/
|
||||
static parseDifficulty(actor, difficulty) {
|
||||
const out = {
|
||||
difficulty: null,
|
||||
difficultyHidden: null,
|
||||
};
|
||||
|
||||
// Macro style
|
||||
if (!Number.isNumeric(difficulty) && difficulty.startsWith("@")) {
|
||||
// 0: "@T:vigilance|max(statusRank)"
|
||||
// 1: "T" // Meaning : S(elf), T(arget)
|
||||
// 2: "vigilance"
|
||||
// 3: "max"
|
||||
// 4: "statusRank"
|
||||
const infos = difficulty.match(/^@([TS]):([^|]+?)(?:\|(min|max)(?:\(([^)]+?)\))?)?$/);
|
||||
|
||||
// Search for reference actor
|
||||
if (!infos || ((infos[1] === "T" || !!infos[3]) && game.user.targets.size < 1)) {
|
||||
// no target set, do manual TN
|
||||
return out;
|
||||
}
|
||||
|
||||
// Define which actor is needed for the difficulty
|
||||
let targetActor;
|
||||
if (infos[1] === "S") {
|
||||
targetActor = actor;
|
||||
} else {
|
||||
// Between the targets
|
||||
targetActor = DicePickerDialog._getTargetActorFromSelection(
|
||||
infos[4] || infos[2],
|
||||
!infos[3] ? null : infos[3] === "min"
|
||||
);
|
||||
}
|
||||
if (!targetActor) {
|
||||
console.log("L5R5E | Fail to get actor from target selection");
|
||||
return out;
|
||||
}
|
||||
|
||||
// Check in actor.<prop> or actor.data.data.<prop>
|
||||
difficulty = targetActor[infos[2]] || targetActor.data.data[infos[2]] || null;
|
||||
if (difficulty < 1) {
|
||||
console.log("L5R5E | Fail to parse difficulty from target");
|
||||
return out;
|
||||
}
|
||||
|
||||
// Hide npc stats on target
|
||||
if (infos[1] === "T") {
|
||||
out.difficultyHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
out.difficulty = parseInt(difficulty);
|
||||
if (isNaN(out.difficulty) || out.difficulty < 0) {
|
||||
out.difficulty = null;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Skills from technique
|
||||
* @param {string} skills
|
||||
* @return {{skillId: number, skillCatId: number}|boolean}
|
||||
*/
|
||||
static parseSkills(skills) {
|
||||
// Check category
|
||||
const categories = game.l5r5e.HelpersL5r5e.getCategoriesSkillsList();
|
||||
const categories2 = game.l5r5e.HelpersL5r5e.getSkillsList(true);
|
||||
|
||||
// Expand category (social) to it's skillname (command,courtesy...)
|
||||
// const skillList = [];
|
||||
// skills.split(',').forEach(e => {
|
||||
// if () {
|
||||
// table.push(e);
|
||||
// }
|
||||
// });
|
||||
|
||||
console.log(categories, categories2);
|
||||
|
||||
// Check skill
|
||||
|
||||
return {
|
||||
skillId: skills,
|
||||
// skillCatId,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the actor who have the min/max value for this property
|
||||
* @param {string} property Property name (vigilance, strife.value)
|
||||
* @param {boolean|null} isMin Null: single target, Min/Max: get the actor who have the max value
|
||||
* @return {null|*}
|
||||
* @private
|
||||
*/
|
||||
static _getTargetActorFromSelection(property, isMin = null) {
|
||||
if (game.user.targets.size < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let targetActor;
|
||||
if (isMin === null) {
|
||||
// only one target, get the first element
|
||||
targetActor = Array.from(game.user.targets).values().next()?.value.document.actor;
|
||||
} else {
|
||||
// Group (Min/Max)
|
||||
const targetGrp = Array.from(game.user.targets).reduce(
|
||||
(acc, tgt) => {
|
||||
const targetActor = tgt.document.actor;
|
||||
if (!["character", "npc"].includes(targetActor.type)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
const targetData = targetActor.data.data;
|
||||
const value = targetActor[property] || targetData[property] || null;
|
||||
if (!value) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if ((isMin && value < acc.value) || (!isMin && value > acc.value)) {
|
||||
acc.actor = targetActor;
|
||||
acc.value = value;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ actor: null, value: 0 }
|
||||
);
|
||||
targetActor = targetGrp.actor;
|
||||
}
|
||||
return targetActor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,42 +2,46 @@
|
||||
* Custom Handlebars for L5R5e
|
||||
*/
|
||||
export const RegisterHandlebars = function () {
|
||||
const sanitizeIfFail = (str) => {
|
||||
return str.indexOf("l5r5e.") !== -1 && str.indexOf("undefined") ? "" : str;
|
||||
};
|
||||
|
||||
/* ------------------------------------ */
|
||||
/* Localizations */
|
||||
/* ------------------------------------ */
|
||||
Handlebars.registerHelper("localizeSkill", function (categoryId, skillId) {
|
||||
const key = "l5r5e.skills." + categoryId.toLowerCase() + "." + skillId.toLowerCase();
|
||||
return game.i18n.localize(key);
|
||||
return sanitizeIfFail(game.i18n.localize(key));
|
||||
});
|
||||
|
||||
Handlebars.registerHelper("localizeSkillId", function (skillId) {
|
||||
const key = "l5r5e.skills." + CONFIG.l5r5e.skills.get(skillId.toLowerCase()) + "." + skillId.toLowerCase();
|
||||
return game.i18n.localize(key);
|
||||
return sanitizeIfFail(game.i18n.localize(key));
|
||||
});
|
||||
|
||||
Handlebars.registerHelper("localizeRing", function (ringId) {
|
||||
const key = "l5r5e.rings." + ringId.toLowerCase();
|
||||
return game.i18n.localize(key);
|
||||
return sanitizeIfFail(game.i18n.localize(key));
|
||||
});
|
||||
|
||||
Handlebars.registerHelper("localizeStanceTip", function (ringId) {
|
||||
const key = "l5r5e.conflict.stances." + ringId.toLowerCase() + "tip";
|
||||
return game.i18n.localize(key);
|
||||
return sanitizeIfFail(game.i18n.localize(key));
|
||||
});
|
||||
|
||||
Handlebars.registerHelper("localizeTechnique", function (techniqueName) {
|
||||
return game.i18n.localize("l5r5e.techniques." + techniqueName.toLowerCase());
|
||||
return sanitizeIfFail(game.i18n.localize("l5r5e.techniques." + techniqueName.toLowerCase()));
|
||||
});
|
||||
|
||||
Handlebars.registerHelper("localizeYesNo", function (isYes) {
|
||||
return game.i18n.localize(isYes ? "Yes" : "No");
|
||||
return sanitizeIfFail(game.i18n.localize(isYes ? "Yes" : "No"));
|
||||
});
|
||||
|
||||
/* ------------------------------------ */
|
||||
/* Dice */
|
||||
/* ------------------------------------ */
|
||||
Handlebars.registerHelper("getDiceFaceUrl", function (diceClass, faceId) {
|
||||
return game.l5r5e[diceClass].getResultSrc(faceId);
|
||||
return sanitizeIfFail(game.l5r5e[diceClass].getResultSrc(faceId));
|
||||
});
|
||||
|
||||
/* ------------------------------------ */
|
||||
|
||||
@@ -43,6 +43,21 @@ export class HelpersL5r5e {
|
||||
return skills;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Categories and Skill names in it
|
||||
* @return {Map}
|
||||
*/
|
||||
static getCategoriesSkillsList() {
|
||||
return Array.from(CONFIG.l5r5e.skills).reduce((acc, [id, cat]) => {
|
||||
if (acc.has(cat)) {
|
||||
acc.set(cat, [...acc.get(cat), id]);
|
||||
} else {
|
||||
acc.set(cat, [id]);
|
||||
}
|
||||
return acc;
|
||||
}, new Map());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Techniques for List / Select
|
||||
* @param types core|school|title|custom
|
||||
|
||||
Reference in New Issue
Block a user