some autocomplete clan & family testing
This commit is contained in:
@@ -542,7 +542,7 @@
|
|||||||
"demeanor": "Demeanor & rings affinities",
|
"demeanor": "Demeanor & rings affinities",
|
||||||
"peculiarities": "Advantages and Disadvantages",
|
"peculiarities": "Advantages and Disadvantages",
|
||||||
"items": "Armure, Armes, et Objets",
|
"items": "Armure, Armes, et Objets",
|
||||||
"narrative": "Narrative (fluff)",
|
"narrative": "Narrative (Notes)",
|
||||||
"age": "Age",
|
"age": "Age",
|
||||||
"marital_status": {
|
"marital_status": {
|
||||||
"title": "Marital Status",
|
"title": "Marital Status",
|
||||||
|
|||||||
@@ -542,7 +542,7 @@
|
|||||||
"demeanor": "Demeanor & rings affinities",
|
"demeanor": "Demeanor & rings affinities",
|
||||||
"peculiarities": "Advantages and Disadvantages",
|
"peculiarities": "Advantages and Disadvantages",
|
||||||
"items": "Armure, Armes, et Objets",
|
"items": "Armure, Armes, et Objets",
|
||||||
"narrative": "Narrative (fluff)",
|
"narrative": "Narrative (Notes)",
|
||||||
"age": "Age",
|
"age": "Age",
|
||||||
"marital_status": {
|
"marital_status": {
|
||||||
"title": "Marital Status",
|
"title": "Marital Status",
|
||||||
|
|||||||
@@ -542,7 +542,7 @@
|
|||||||
"demeanor": "Comportement et affinités avec les anneaux",
|
"demeanor": "Comportement et affinités avec les anneaux",
|
||||||
"peculiarities": "Avantages et désavantages",
|
"peculiarities": "Avantages et désavantages",
|
||||||
"items": "Armure, Armes, et Objets",
|
"items": "Armure, Armes, et Objets",
|
||||||
"narrative": "Narration (fluff)",
|
"narrative": "Narration (Notes)",
|
||||||
"age": "Age",
|
"age": "Age",
|
||||||
"marital_status": {
|
"marital_status": {
|
||||||
"title": "État civil",
|
"title": "État civil",
|
||||||
|
|||||||
@@ -15,17 +15,19 @@ export class CharacterGeneratorDialog extends FormApplication {
|
|||||||
* Payload Object
|
* Payload Object
|
||||||
*/
|
*/
|
||||||
object = {
|
object = {
|
||||||
avg_rings: 3,
|
avgRings: 3,
|
||||||
clan: "random",
|
clan: "random",
|
||||||
gender: "random",
|
gender: "random",
|
||||||
generateAttributes: true,
|
generate: {
|
||||||
generateDemeanor: true,
|
attributes: true,
|
||||||
generateName: true,
|
demeanor: true,
|
||||||
generateNarrative: true,
|
items: true,
|
||||||
generatePeculiarities: true,
|
name: true,
|
||||||
generateItems: true,
|
narrative: true,
|
||||||
generateTechniques: true,
|
peculiarities: true,
|
||||||
generateSocial: true,
|
social: true,
|
||||||
|
techniques: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,6 +62,32 @@ export class CharacterGeneratorDialog extends FormApplication {
|
|||||||
constructor(actor = null, options = {}) {
|
constructor(actor = null, options = {}) {
|
||||||
super({}, options);
|
super({}, options);
|
||||||
this.actor = actor;
|
this.actor = actor;
|
||||||
|
this.initializeFromActor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to get values from actor to initialize the generator
|
||||||
|
*/
|
||||||
|
initializeFromActor() {
|
||||||
|
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";
|
||||||
|
|
||||||
|
// Rings
|
||||||
|
this.object.avgRings = CharacterGenerator.sanitizeMinMax(
|
||||||
|
Math.round(
|
||||||
|
Object.values(actorDatas.rings).reduce((acc, ringValue) => {
|
||||||
|
return acc + ringValue;
|
||||||
|
}, 0) / 5
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,7 +96,7 @@ export class CharacterGeneratorDialog extends FormApplication {
|
|||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
async getData(options = null) {
|
async getData(options = null) {
|
||||||
const clans = Object.keys(CharacterGenerator.clansAndFamilies).map((e) => ({
|
const clans = Array.from(CONFIG.l5r5e.families.keys()).map((e) => ({
|
||||||
id: e,
|
id: e,
|
||||||
label: game.i18n.localize("l5r5e.clans." + e),
|
label: game.i18n.localize("l5r5e.clans." + e),
|
||||||
}));
|
}));
|
||||||
@@ -93,23 +121,22 @@ export class CharacterGeneratorDialog extends FormApplication {
|
|||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
|
formData = foundry.utils.expandObject(formData);
|
||||||
|
|
||||||
// Generate datas
|
// Generate datas
|
||||||
const generator = new CharacterGenerator({
|
const generator = new CharacterGenerator({
|
||||||
avgRingsValue: formData.avg_rings,
|
avgRingsValue: formData.avgRings,
|
||||||
clanName: formData.clan,
|
clanName: formData.clan,
|
||||||
gender: formData.gender,
|
gender: formData.gender,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update current Object with new data to keep selection
|
// Update current Object with new data to keep selection
|
||||||
// Get selected value from generator for random values
|
|
||||||
this.object = {
|
this.object = {
|
||||||
...formData,
|
...formData,
|
||||||
clan: generator.data.clan,
|
|
||||||
gender: generator.data.gender,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update actor with selection
|
// Update actor with selection
|
||||||
const updatedDatas = await generator.toActor(this.actor, formData);
|
const updatedDatas = await generator.toActor(this.actor, formData.generate);
|
||||||
await this.actor.update(updatedDatas);
|
await this.actor.update(updatedDatas);
|
||||||
|
|
||||||
this.render(false);
|
this.render(false);
|
||||||
|
|||||||
@@ -3,36 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export class CharacterGenerator {
|
export class CharacterGenerator {
|
||||||
//<editor-fold desc="Config Datas">
|
//<editor-fold desc="Config Datas">
|
||||||
static clansAndFamilies = {
|
|
||||||
// Majors
|
|
||||||
imperial: ["Miya", "Otomo", "Seppun"],
|
|
||||||
crab: ["Hida", "Kaiu", "Hiruma", "Yasuki", "Kuni"],
|
|
||||||
crane: ["Asahina", "Daidoji", "Doji", "Kakita"],
|
|
||||||
dragon: ["Kitsuki", "Mirumoto", "Togashi"],
|
|
||||||
lion: ["Akodo", "Ikoma", "Kitsu", "Matsu"],
|
|
||||||
phoenix: ["Agasha", "Asako", "Isawa", "Shiba"],
|
|
||||||
scorpion: ["Bayushi", "Shosuro", "Soshi", "Yogo"],
|
|
||||||
unicorn: ["Ide", "Iuchi", "Moto", "Shinjo", "Utaku"],
|
|
||||||
mantis: ["(mantis)"], // no family name, boat name
|
|
||||||
|
|
||||||
// Minors
|
|
||||||
ronin: ["(ronin)"], // can be anything
|
|
||||||
badger: ["Ichiro"],
|
|
||||||
bat: ["Komori"],
|
|
||||||
boar: ["Heichi"],
|
|
||||||
dragonfly: ["Tonbo"],
|
|
||||||
firefly: ["Hotaru"],
|
|
||||||
hare: ["Ujina", "Usagi"],
|
|
||||||
monkey: ["Toku", "Fuzake"],
|
|
||||||
oriole: ["Tsi"],
|
|
||||||
ox: ["Morito"],
|
|
||||||
sparrow: ["Suzume"],
|
|
||||||
tortoise: ["Kasuga"],
|
|
||||||
// ivory_kingdoms
|
|
||||||
// qamarist
|
|
||||||
// ujik
|
|
||||||
};
|
|
||||||
|
|
||||||
static demeanorList = [
|
static demeanorList = [
|
||||||
{ id: "adaptable", mod: { fire: 2, earth: -2 } },
|
{ id: "adaptable", mod: { fire: 2, earth: -2 } },
|
||||||
{ id: "adaptable", mod: { water: 2, earth: -2 } },
|
{ id: "adaptable", mod: { water: 2, earth: -2 } },
|
||||||
@@ -130,17 +100,17 @@ export class CharacterGenerator {
|
|||||||
* @param {string} gender random|male|female
|
* @param {string} gender random|male|female
|
||||||
*/
|
*/
|
||||||
constructor({ avgRingsValue = 3, clanName = "random", gender = "random" }) {
|
constructor({ avgRingsValue = 3, clanName = "random", gender = "random" }) {
|
||||||
if (!CharacterGenerator.clansAndFamilies[clanName]) {
|
if (!CONFIG.l5r5e.families.has(clanName)) {
|
||||||
clanName = "random";
|
clanName = "random";
|
||||||
}
|
}
|
||||||
if (clanName === "random") {
|
if (clanName === "random") {
|
||||||
clanName = CharacterGenerator._getRandomArrayValue(Object.keys(CharacterGenerator.clansAndFamilies));
|
clanName = CharacterGenerator._getRandomArrayValue(Array.from(CONFIG.l5r5e.families.keys()));
|
||||||
}
|
}
|
||||||
if (gender === "random" || !["male", "female"].includes(gender)) {
|
if (gender === "random" || !["male", "female"].includes(gender)) {
|
||||||
gender = Math.random() > 0.5 ? "male" : "female";
|
gender = Math.random() > 0.5 ? "male" : "female";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data.avgRingsValue = CharacterGenerator._sanitizeMinMax(avgRingsValue);
|
this.data.avgRingsValue = CharacterGenerator.sanitizeMinMax(avgRingsValue);
|
||||||
this.data.clan = clanName;
|
this.data.clan = clanName;
|
||||||
this.data.family = CharacterGenerator._getRandomFamily(clanName);
|
this.data.family = CharacterGenerator._getRandomFamily(clanName);
|
||||||
this.data.gender = gender;
|
this.data.gender = gender;
|
||||||
@@ -182,9 +152,8 @@ export class CharacterGenerator {
|
|||||||
* Always return a number between 1 and 5
|
* Always return a number between 1 and 5
|
||||||
* @param {number} number
|
* @param {number} number
|
||||||
* @return {number}
|
* @return {number}
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
static _sanitizeMinMax(number) {
|
static sanitizeMinMax(number) {
|
||||||
return Math.min(5, Math.max(1, number));
|
return Math.min(5, Math.max(1, number));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,10 +191,11 @@ export class CharacterGenerator {
|
|||||||
// Ronin specific, can be any other family name
|
// Ronin specific, can be any other family name
|
||||||
if (clanName === "ronin") {
|
if (clanName === "ronin") {
|
||||||
originClan = CharacterGenerator._getRandomArrayValue(
|
originClan = CharacterGenerator._getRandomArrayValue(
|
||||||
Object.keys(CharacterGenerator.clansAndFamilies).filter((e) => e !== "ronin")
|
Array.from(CONFIG.l5r5e.families.keys()).filter((e) => e !== "ronin")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return CharacterGenerator._getRandomArrayValue(CharacterGenerator.clansAndFamilies[originClan]);
|
|
||||||
|
return CharacterGenerator._getRandomArrayValue(CONFIG.l5r5e.families.get(originClan));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -288,33 +258,41 @@ export class CharacterGenerator {
|
|||||||
* Modify the current actor datas with selected options
|
* Modify the current actor datas with selected options
|
||||||
*
|
*
|
||||||
* @param {ActorL5r5e} actor Actor object
|
* @param {ActorL5r5e} actor Actor object
|
||||||
* @param {boolean} generateName If true generate a new name
|
* @param {Object} generate
|
||||||
* @param {boolean} generateAttributes If true generate rings, attributes, skills and confrontation ranks
|
* @param {boolean} generate.name If true generate a new name
|
||||||
* @param {boolean} generateSocial If true generate Social Standing
|
* @param {boolean} generate.attributes If true generate rings, attributes, skills and confrontation ranks
|
||||||
* @param {boolean} generateDemeanor If true generate Demeanor and rings affinities
|
* @param {boolean} generate.social If true generate Social Standing
|
||||||
* @param {boolean} generatePeculiarities If true generate Advantage and Disadvantage
|
* @param {boolean} generate.demeanor If true generate Demeanor and rings affinities
|
||||||
* @param {boolean} generateItems If true generate Armor, Weapons and Items
|
* @param {boolean} generate.peculiarities If true generate Advantage and Disadvantage
|
||||||
* @param {boolean} generateTechniques If true generate Shuji, Katas...
|
* @param {boolean} generate.items If true generate Armor, Weapons and Items
|
||||||
* @param {boolean} generateNarrative If true generate Narrative and fluff
|
* @param {boolean} generate.techniques If true generate Shuji, Katas...
|
||||||
|
* @param {boolean} generate.narrative If true generate Narrative and fluff
|
||||||
* @return {Promise<Object>}
|
* @return {Promise<Object>}
|
||||||
*/
|
*/
|
||||||
async toActor(
|
async toActor(
|
||||||
actor,
|
actor,
|
||||||
{
|
generate = {
|
||||||
generateName = true,
|
name: true,
|
||||||
generateAttributes = true,
|
attributes: true,
|
||||||
generateSocial = true,
|
social: true,
|
||||||
generateDemeanor = true,
|
demeanor: true,
|
||||||
generatePeculiarities = true,
|
peculiarities: true,
|
||||||
generateItems = true,
|
items: true,
|
||||||
generateTechniques = true,
|
techniques: true,
|
||||||
generateNarrative = true,
|
narrative: true,
|
||||||
} = {}
|
}
|
||||||
) {
|
) {
|
||||||
const actorDatas = actor.data.data;
|
const actorDatas = actor.data.data;
|
||||||
|
|
||||||
|
console.log(generate); // TODO tmp
|
||||||
|
|
||||||
// Name
|
// Name
|
||||||
const newName = generateName ? await this.getRandomizedName() : actor.data.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";
|
||||||
|
|
||||||
// Img (only if default)
|
// Img (only if default)
|
||||||
const folder = "systems/l5r5e/assets/icons/actors";
|
const folder = "systems/l5r5e/assets/icons/actors";
|
||||||
@@ -327,38 +305,38 @@ export class CharacterGenerator {
|
|||||||
: actor.data.img;
|
: actor.data.img;
|
||||||
|
|
||||||
// Generate attributes (rings, attributes, skills, confrontation ranks)
|
// Generate attributes (rings, attributes, skills, confrontation ranks)
|
||||||
if (generateAttributes) {
|
if (generate.attributes) {
|
||||||
this._generateAttributes(actorDatas);
|
this._generateAttributes(actorDatas);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Social Standing
|
// Social Standing
|
||||||
if (generateSocial) {
|
if (generate.social) {
|
||||||
actorDatas.social.honor = this.data.honor;
|
actorDatas.social.honor = this.data.honor;
|
||||||
actorDatas.social.glory = this.data.glory;
|
actorDatas.social.glory = this.data.glory;
|
||||||
actorDatas.social.status = this.data.status;
|
actorDatas.social.status = this.data.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Demeanor (npc only)
|
// Demeanor (npc only)
|
||||||
if (generateDemeanor && actor.type === "npc") {
|
if (generate.demeanor && actor.type === "npc") {
|
||||||
this._generateDemeanor(actorDatas);
|
this._generateDemeanor(actorDatas);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Items types
|
// Items types
|
||||||
if (generatePeculiarities || generateItems || generateTechniques) {
|
if (generate.peculiarities || generate.items || generate.techniques) {
|
||||||
const newItemsData = [];
|
const newItemsData = [];
|
||||||
|
|
||||||
// Advantage / Disadvantage
|
// Advantage / Disadvantage
|
||||||
if (generatePeculiarities) {
|
if (generate.peculiarities) {
|
||||||
await this._generatePeculiarities(actor, newItemsData);
|
await this._generatePeculiarities(actor, newItemsData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
if (generateItems) {
|
if (generate.items) {
|
||||||
await this._generateItems(actor, newItemsData);
|
await this._generateItems(actor, newItemsData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Techniques
|
// Techniques
|
||||||
if (generateTechniques) {
|
if (generate.techniques) {
|
||||||
await this._generateTechniques(actor, newItemsData);
|
await this._generateTechniques(actor, newItemsData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +347,7 @@ export class CharacterGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Narrative
|
// Narrative
|
||||||
if (generateNarrative) {
|
if (generate.narrative) {
|
||||||
this._generateNarrative(actorDatas);
|
this._generateNarrative(actorDatas);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +371,7 @@ export class CharacterGenerator {
|
|||||||
// Rings
|
// Rings
|
||||||
CONFIG.l5r5e.stances.forEach((ringName) => {
|
CONFIG.l5r5e.stances.forEach((ringName) => {
|
||||||
// avgRingsValue + (-1|0|1)
|
// avgRingsValue + (-1|0|1)
|
||||||
actorDatas.rings[ringName] = CharacterGenerator._sanitizeMinMax(
|
actorDatas.rings[ringName] = CharacterGenerator.sanitizeMinMax(
|
||||||
this.data.avgRingsValue - 1 + Math.floor(Math.random() * 3)
|
this.data.avgRingsValue - 1 + Math.floor(Math.random() * 3)
|
||||||
);
|
);
|
||||||
stats.min = Math.min(stats.min, actorDatas.rings[ringName]);
|
stats.min = Math.min(stats.min, actorDatas.rings[ringName]);
|
||||||
|
|||||||
@@ -76,6 +76,24 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Autocomplete
|
||||||
|
game.l5r5e.HelpersL5r5e.autocomplete(
|
||||||
|
html,
|
||||||
|
"data.identity.clan",
|
||||||
|
Object.entries(game.i18n.translations.l5r5e.clans)
|
||||||
|
.filter(([k, v]) => k !== "title")
|
||||||
|
.map(([k, v]) => v)
|
||||||
|
);
|
||||||
|
game.l5r5e.HelpersL5r5e.autocomplete(
|
||||||
|
html,
|
||||||
|
"data.identity.family",
|
||||||
|
CONFIG.l5r5e.families.get(
|
||||||
|
Object.entries(game.i18n.translations.l5r5e.clans).find(
|
||||||
|
([k, v]) => v === this.actor.data.data.identity.clan
|
||||||
|
)?.[0]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Open linked school curriculum journal
|
// Open linked school curriculum journal
|
||||||
html.find(".school-journal-link").on("click", this._openLinkedJournal.bind(this));
|
html.find(".school-journal-link").on("click", this._openLinkedJournal.bind(this));
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,13 @@ export class NpcSheetL5r5e extends BaseCharacterSheetL5r5e {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the NpcGenerator button on top of sheet
|
* Add the CharacterGenerator button in L5R specific bar
|
||||||
* @override
|
* @override
|
||||||
|
* @return {{label: string, class: string, icon: string, onclick: Function|null}[]}
|
||||||
*/
|
*/
|
||||||
_getHeaderButtons() {
|
_getL5rHeaderButtons() {
|
||||||
let buttons = super._getHeaderButtons();
|
const buttons = super._getL5rHeaderButtons();
|
||||||
if (!this.isEditable || this.actor.limited) {
|
if (!this.isEditable || this.actor.limited || this.actor.data.data.soft_locked) {
|
||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ L5R5E.techniques.set("title_ability", { type: "title", displayInTypes: false });
|
|||||||
// Custom
|
// Custom
|
||||||
L5R5E.techniques.set("specificity", { type: "custom", displayInTypes: false });
|
L5R5E.techniques.set("specificity", { type: "custom", displayInTypes: false });
|
||||||
|
|
||||||
// Map SkillId - CategoryId
|
// *** SkillId - CategoryId ***
|
||||||
L5R5E.skills = new Map();
|
L5R5E.skills = new Map();
|
||||||
L5R5E.skills.set("aesthetics", "artisan");
|
L5R5E.skills.set("aesthetics", "artisan");
|
||||||
L5R5E.skills.set("composition", "artisan");
|
L5R5E.skills.set("composition", "artisan");
|
||||||
@@ -74,7 +74,7 @@ L5R5E.skills.set("seafaring", "trade");
|
|||||||
L5R5E.skills.set("skulduggery", "trade");
|
L5R5E.skills.set("skulduggery", "trade");
|
||||||
L5R5E.skills.set("survival", "trade");
|
L5R5E.skills.set("survival", "trade");
|
||||||
|
|
||||||
// Symbols Map
|
// *** Symbols ***
|
||||||
L5R5E.symbols = new Map();
|
L5R5E.symbols = new Map();
|
||||||
L5R5E.symbols.set("(op)", { class: "i_opportunity", label: "l5r5e.chatdices.opportunities" });
|
L5R5E.symbols.set("(op)", { class: "i_opportunity", label: "l5r5e.chatdices.opportunities" });
|
||||||
L5R5E.symbols.set("(su)", { class: "i_success", label: "l5r5e.chatdices.successes" });
|
L5R5E.symbols.set("(su)", { class: "i_success", label: "l5r5e.chatdices.successes" });
|
||||||
@@ -114,3 +114,29 @@ L5R5E.symbols.set("(unicorn)", { class: "i_unicorn", label: "" });
|
|||||||
L5R5E.symbols.set("(bushi)", { class: "i_bushi", label: "" });
|
L5R5E.symbols.set("(bushi)", { class: "i_bushi", label: "" });
|
||||||
L5R5E.symbols.set("(courtier)", { class: "i_courtier", label: "" });
|
L5R5E.symbols.set("(courtier)", { class: "i_courtier", label: "" });
|
||||||
L5R5E.symbols.set("(shugenja)", { class: "i_shugenja", label: "" });
|
L5R5E.symbols.set("(shugenja)", { class: "i_shugenja", label: "" });
|
||||||
|
|
||||||
|
// *** Clans and Families ***
|
||||||
|
L5R5E.families = new Map();
|
||||||
|
// Majors
|
||||||
|
L5R5E.families.set("imperial", ["Miya", "Otomo", "Seppun"]);
|
||||||
|
L5R5E.families.set("crab", ["Hida", "Kaiu", "Hiruma", "Yasuki", "Kuni"]);
|
||||||
|
L5R5E.families.set("crane", ["Asahina", "Daidoji", "Doji", "Kakita"]);
|
||||||
|
L5R5E.families.set("dragon", ["Kitsuki", "Mirumoto", "Togashi"]);
|
||||||
|
L5R5E.families.set("lion", ["Akodo", "Ikoma", "Kitsu", "Matsu"]);
|
||||||
|
L5R5E.families.set("phoenix", ["Agasha", "Asako", "Isawa", "Shiba"]);
|
||||||
|
L5R5E.families.set("scorpion", ["Bayushi", "Shosuro", "Soshi", "Yogo"]);
|
||||||
|
L5R5E.families.set("unicorn", ["Ide", "Iuchi", "Moto", "Shinjo", "Utaku"]);
|
||||||
|
// Minors
|
||||||
|
L5R5E.families.set("mantis", ["(boat name)"]); // no family name, boat name
|
||||||
|
L5R5E.families.set("ronin", ["(ronin)"]); // can be anything
|
||||||
|
L5R5E.families.set("badger", ["Ichiro"]);
|
||||||
|
L5R5E.families.set("bat", ["Komori"]);
|
||||||
|
L5R5E.families.set("boar", ["Heichi"]);
|
||||||
|
L5R5E.families.set("dragonfly", ["Tonbo"]);
|
||||||
|
L5R5E.families.set("firefly", ["Hotaru"]);
|
||||||
|
L5R5E.families.set("hare", ["Ujina", "Usagi"]);
|
||||||
|
L5R5E.families.set("monkey", ["Toku", "Fuzake"]);
|
||||||
|
L5R5E.families.set("oriole", ["Tsi"]);
|
||||||
|
L5R5E.families.set("ox", ["Morito"]);
|
||||||
|
L5R5E.families.set("sparrow", ["Suzume"]);
|
||||||
|
L5R5E.families.set("tortoise", ["Kasuga"]);
|
||||||
|
|||||||
@@ -690,4 +690,123 @@ export class HelpersL5r5e {
|
|||||||
}
|
}
|
||||||
return await table.drawMany(retrieve, opt);
|
return await table.drawMany(retrieve, opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the string without accents
|
||||||
|
* @param {string} str
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
static noAccents(str) {
|
||||||
|
return str.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autocomplete method
|
||||||
|
* @param {jQuery} html HTML content of the sheet.
|
||||||
|
* @param {string} name Html name of the input
|
||||||
|
* @param {string[]} list Array of string to display
|
||||||
|
*/
|
||||||
|
static autocomplete(html, name, list = []) {
|
||||||
|
const inp = document.getElementsByName(name)?.[0];
|
||||||
|
if (list.length < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let currentFocus;
|
||||||
|
|
||||||
|
const closeAllLists = (elmnt = null) => {
|
||||||
|
const collection = document.getElementsByClassName("autocomplete-items");
|
||||||
|
for (let item of collection) {
|
||||||
|
if (!elmnt || (elmnt !== item && elmnt !== inp)) {
|
||||||
|
item.parentNode.removeChild(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// execute a function when someone writes in the text field
|
||||||
|
inp.addEventListener("input", (inputEvent) => {
|
||||||
|
closeAllLists();
|
||||||
|
|
||||||
|
const val = inputEvent.target.value;
|
||||||
|
if (!val) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFocus = -1;
|
||||||
|
|
||||||
|
// create a DIV element that will contain the items (values)
|
||||||
|
const listDiv = document.createElement("DIV");
|
||||||
|
listDiv.setAttribute("id", inputEvent.target.id + "autocomplete-list");
|
||||||
|
listDiv.setAttribute("class", "autocomplete-items");
|
||||||
|
|
||||||
|
// append the DIV element as a child of the autocomplete container
|
||||||
|
inputEvent.target.parentNode.appendChild(listDiv);
|
||||||
|
|
||||||
|
list.forEach((value, index) => {
|
||||||
|
if (
|
||||||
|
HelpersL5r5e.noAccents(value.substring(0, val.length).toLowerCase()) ===
|
||||||
|
HelpersL5r5e.noAccents(val.toLowerCase())
|
||||||
|
) {
|
||||||
|
const choiceDiv = document.createElement("DIV");
|
||||||
|
choiceDiv.setAttribute("data-id", index);
|
||||||
|
choiceDiv.innerHTML = `<strong>${value.substring(0, val.length)}</strong>${value.substring(
|
||||||
|
val.length
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
choiceDiv.addEventListener("click", (clickEvent) => {
|
||||||
|
const selectedIndex = clickEvent.target.attributes["data-id"].value;
|
||||||
|
if (!list[selectedIndex]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inp.value = list[selectedIndex];
|
||||||
|
closeAllLists();
|
||||||
|
});
|
||||||
|
listDiv.appendChild(choiceDiv);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// execute a function presses a key on the keyboard
|
||||||
|
inp.addEventListener("keydown", function (e) {
|
||||||
|
const collection = document.getElementById(this.id + "autocomplete-list")?.getElementsByTagName("div");
|
||||||
|
if (!collection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (e.code) {
|
||||||
|
case "ArrowUp":
|
||||||
|
case "ArrowDown":
|
||||||
|
// focus index
|
||||||
|
currentFocus += e.code === "ArrowUp" ? -1 : 1;
|
||||||
|
if (currentFocus >= collection.length) {
|
||||||
|
currentFocus = 0;
|
||||||
|
}
|
||||||
|
if (currentFocus < 0) {
|
||||||
|
currentFocus = collection.length - 1;
|
||||||
|
}
|
||||||
|
// css classes
|
||||||
|
for (let item of collection) {
|
||||||
|
item.classList.remove("autocomplete-active");
|
||||||
|
}
|
||||||
|
collection[currentFocus]?.classList.add("autocomplete-active");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Tab":
|
||||||
|
case "Enter":
|
||||||
|
e.preventDefault();
|
||||||
|
if (currentFocus > -1 && !!collection[currentFocus]) {
|
||||||
|
collection[currentFocus].click();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Escape":
|
||||||
|
closeAllLists();
|
||||||
|
break;
|
||||||
|
} //swi
|
||||||
|
});
|
||||||
|
|
||||||
|
// execute a function when someone clicks in the document
|
||||||
|
html.on("focusout", (e) => {
|
||||||
|
console.log("aaaaaaaaaa");
|
||||||
|
closeAllLists(e.target);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -182,4 +182,16 @@
|
|||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
form .form-group {
|
||||||
|
border-bottom: solid #8080803d 1px;
|
||||||
|
|
||||||
|
&.smaller {
|
||||||
|
> label {
|
||||||
|
flex: 10;
|
||||||
|
}
|
||||||
|
.form-fields {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -917,3 +917,36 @@ button {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.autocomplete {
|
||||||
|
/*the container must be positioned relative:*/
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
.autocomplete-items {
|
||||||
|
position: absolute;
|
||||||
|
border: 1px solid #d4d4d4;
|
||||||
|
border-bottom: none;
|
||||||
|
border-top: none;
|
||||||
|
z-index: 99;
|
||||||
|
/*position the autocomplete items to be the same width as the container:*/
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.autocomplete-items div {
|
||||||
|
padding: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 1px solid #d4d4d4;
|
||||||
|
}
|
||||||
|
.autocomplete-items div:hover {
|
||||||
|
/*when hovering an item:*/
|
||||||
|
background-color: #e9e9e9;
|
||||||
|
}
|
||||||
|
.autocomplete-active {
|
||||||
|
/*when navigating through the items using the arrow keys:*/
|
||||||
|
background-color: DodgerBlue !important;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,12 +7,15 @@
|
|||||||
},
|
},
|
||||||
"identity": {
|
"identity": {
|
||||||
"identity": {
|
"identity": {
|
||||||
|
"age": "",
|
||||||
"clan": "",
|
"clan": "",
|
||||||
"family": "",
|
"family": "",
|
||||||
|
"female": false,
|
||||||
|
"marital_status": "unmarried",
|
||||||
|
"roles": "",
|
||||||
"school": "",
|
"school": "",
|
||||||
"school_rank": 1,
|
"school_rank": 1,
|
||||||
"school_curriculum_journal": {},
|
"school_curriculum_journal": {}
|
||||||
"roles": ""
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rings": {
|
"rings": {
|
||||||
@@ -124,7 +127,7 @@
|
|||||||
"zeni": 0
|
"zeni": 0
|
||||||
},
|
},
|
||||||
"npc": {
|
"npc": {
|
||||||
"templates": ["softlock", "rings", "social", "techniques", "conflict"],
|
"templates": ["softlock", "identity", "rings", "social", "techniques", "conflict"],
|
||||||
"type": "adversary",
|
"type": "adversary",
|
||||||
"attitude": "",
|
"attitude": "",
|
||||||
"conflict_rank": {
|
"conflict_rank": {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<form class="l5r5e character-generator-dialog" autocomplete="off">
|
<form class="l5r5e character-generator-dialog" autocomplete="off">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="form-group body">
|
<div class="form-group">
|
||||||
<label>
|
<label>{{localize 'l5r5e.clan'}}</label>
|
||||||
{{localize 'l5r5e.clan'}}
|
<div class="form-fields">
|
||||||
<select class="attribute-dtype" name="clan">
|
<select class="attribute-dtype" name="clan">
|
||||||
{{#select data.clan}}
|
{{#select data.clan}}
|
||||||
{{#each clanList as |value|}}
|
{{#each clanList as |value|}}
|
||||||
@@ -10,8 +10,11 @@
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
{{/select}}
|
{{/select}}
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
{{localize 'l5r5e.gender.title'}}
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize 'l5r5e.gender.title'}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
<select class="attribute-dtype" name="gender">
|
<select class="attribute-dtype" name="gender">
|
||||||
{{#select data.gender}}
|
{{#select data.gender}}
|
||||||
{{#each genderList as |value|}}
|
{{#each genderList as |value|}}
|
||||||
@@ -19,47 +22,68 @@
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
{{/select}}
|
{{/select}}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
{{localize 'l5r5e.char_generator.average_value'}} <input class="centered-input" type="number" name="avg_rings" value="{{data.avg_rings}}" data-dtype="Number" min="1" max="5" />
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
{{localize 'l5r5e.char_generator.generate'}}:
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="generateName" {{checked data.generateName}} />
|
|
||||||
{{localize 'l5r5e.name'}}
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="generateAttributes" {{checked data.generateAttributes}} />
|
|
||||||
{{localize 'l5r5e.char_generator.attributes'}}
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="generateSocial" {{checked data.generateSocial}} />
|
|
||||||
{{localize 'l5r5e.char_generator.social'}}
|
|
||||||
</label>
|
|
||||||
{{#if isNpc}}
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="generateDemeanor" {{checked data.generateDemeanor}} />
|
|
||||||
{{localize 'l5r5e.char_generator.demeanor'}}
|
|
||||||
</label>
|
|
||||||
{{/if}}
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="generatePeculiarities" {{checked data.generatePeculiarities}} />
|
|
||||||
{{localize 'l5r5e.char_generator.peculiarities'}}
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="generateItems" {{checked data.generateItems}} />
|
|
||||||
{{localize 'l5r5e.char_generator.items'}}
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="generateTechniques" {{checked data.generateTechniques}} />
|
|
||||||
{{localize 'l5r5e.techniques.title'}}
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="generateNarrative" {{checked data.generateNarrative}} />
|
|
||||||
{{localize 'l5r5e.char_generator.narrative'}}
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize 'l5r5e.char_generator.average_value'}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input type="range" name="avgRings" data-dtype="Number" value="{{data.avgRings}}" min="1" max="5" step="1">
|
||||||
|
<span class="range-value">{{data.avgRings}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="separator">{{localize 'l5r5e.char_generator.generate'}}</h2>
|
||||||
|
<div class="form-group smaller">
|
||||||
|
<label>{{localize 'l5r5e.name'}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input type="checkbox" name="generate.name" data-dtype="Boolean" {{checked data.generate.name}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group smaller">
|
||||||
|
<label>{{localize 'l5r5e.char_generator.attributes'}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input type="checkbox" name="generate.attributes" data-dtype="Boolean" {{checked data.generate.attributes}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group smaller">
|
||||||
|
<label>{{localize 'l5r5e.char_generator.social'}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input type="checkbox" name="generate.social" data-dtype="Boolean" {{checked data.generate.social}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#if isNpc}}
|
||||||
|
<div class="form-group smaller">
|
||||||
|
<label>{{localize 'l5r5e.char_generator.demeanor'}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input type="checkbox" name="generate.demeanor" data-dtype="Boolean" {{checked data.generate.demeanor}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<div class="form-group smaller">
|
||||||
|
<label>{{localize 'l5r5e.char_generator.peculiarities'}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input type="checkbox" name="generate.peculiarities" data-dtype="Boolean" {{checked data.generate.peculiarities}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group smaller">
|
||||||
|
<label>{{localize 'l5r5e.char_generator.items'}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input type="checkbox" name="generate.items" data-dtype="Boolean" {{checked data.generate.items}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group smaller">
|
||||||
|
<label>{{localize 'l5r5e.techniques.title'}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input type="checkbox" name="generate.techniques" data-dtype="Boolean" {{checked data.generate.techniques}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group smaller">
|
||||||
|
<label>{{localize 'l5r5e.char_generator.narrative'}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input type="checkbox" name="generate.narrative" data-dtype="Boolean" {{checked data.generate.narrative}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group footer">
|
<div class="form-group footer">
|
||||||
<button name="generate" id="generate" type="submit">
|
<button name="generate" id="generate" type="submit">
|
||||||
{{localize 'l5r5e.char_generator.generate'}} <i class='fas fa-arrow-circle-right'></i>
|
{{localize 'l5r5e.char_generator.generate'}} <i class='fas fa-arrow-circle-right'></i>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<ul class="identity-content">
|
<ul class="identity-content">
|
||||||
<li>
|
<li>
|
||||||
<label class="attribute-label">
|
<label class="attribute-label autocomplete">
|
||||||
{{#ifCond data.data.template '==' 'pow'}}
|
{{#ifCond data.data.template '==' 'pow'}}
|
||||||
{{localize 'l5r5e.region'}}
|
{{localize 'l5r5e.region'}}
|
||||||
{{else}}
|
{{else}}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label class="attribute-label">
|
<label class="attribute-label autocomplete">
|
||||||
{{#ifCond data.data.template '==' 'pow'}}
|
{{#ifCond data.data.template '==' 'pow'}}
|
||||||
{{localize 'l5r5e.upbringing'}}
|
{{localize 'l5r5e.upbringing'}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|||||||
Reference in New Issue
Block a user