Added Army/Cohort/Fortification raw sheet and js
This commit is contained in:
1
system/assets/icons/actors/army.svg
Normal file
1
system/assets/icons/actors/army.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 16 KiB |
1
system/assets/icons/items/army_cohort.svg
Normal file
1
system/assets/icons/items/army_cohort.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 16 KiB |
1
system/assets/icons/items/army_fortification.svg
Normal file
1
system/assets/icons/items/army_fortification.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.7 KiB |
@@ -21,7 +21,8 @@
|
||||
},
|
||||
"ACTOR": {
|
||||
"TypeCharacter": "Player Character",
|
||||
"TypeNpc": "Non-Player Character"
|
||||
"TypeNpc": "Non-Player Character",
|
||||
"TypeArmy": "Army"
|
||||
},
|
||||
"ITEM": {
|
||||
"TypeItem": "Item",
|
||||
@@ -34,7 +35,9 @@
|
||||
"TypeTitle": "Title",
|
||||
"TypeBond": "Bond",
|
||||
"TypeSignature_scroll": "Signature Scroll",
|
||||
"TypeItem_pattern": "Item Pattern"
|
||||
"TypeItem_pattern": "Item Pattern",
|
||||
"TypeArmy_fortification": "Fortification",
|
||||
"TypeArmy_cohort": "Cohort"
|
||||
},
|
||||
"l5r5e": {
|
||||
"global": {
|
||||
@@ -364,6 +367,36 @@
|
||||
"adversary": "Adversary",
|
||||
"minion": "Minion"
|
||||
},
|
||||
"army": {
|
||||
"warlord": "Warlord",
|
||||
"allies_backers": "Allies and Backers",
|
||||
"purpose_mustering": "Purpose for Mustering",
|
||||
"battle_readiness": {
|
||||
"title": "Battle Readiness",
|
||||
"strength": "Strength",
|
||||
"casualties": "Casualties",
|
||||
"discipline": "Discipline",
|
||||
"panic": "Panic"
|
||||
},
|
||||
"commander": "Commander",
|
||||
"commander_abilities": "Commander's relevant abilities",
|
||||
"army_abilities": "Army Abilities",
|
||||
"commander_standing": "Commander's Standing",
|
||||
"supplies_logistics": "Supplies and Logistics",
|
||||
"past_battles": "Past Battles",
|
||||
"cohort": {
|
||||
"tab": "Cohorts",
|
||||
"title": "Cohorts",
|
||||
"leader": "Battle Readiness",
|
||||
"abilities": "Abilities"
|
||||
},
|
||||
"fortification": {
|
||||
"tab": "Fortifications",
|
||||
"title": "Fortification Held",
|
||||
"difficulty": "Difficulty Value",
|
||||
"attrition_reduction": "Attrition Reduction"
|
||||
}
|
||||
},
|
||||
"twenty_questions": {
|
||||
"title": "Twenty questions",
|
||||
"bt_abrev": "20Q",
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
},
|
||||
"ACTOR": {
|
||||
"TypeCharacter": "Personaje jugador",
|
||||
"TypeNpc": "Personaje no jugador"
|
||||
"TypeNpc": "Personaje no jugador",
|
||||
"TypeArmy": "Army"
|
||||
},
|
||||
"ITEM": {
|
||||
"TypeItem": "Objeto",
|
||||
@@ -34,7 +35,9 @@
|
||||
"TypeTitle": "Title",
|
||||
"TypeBond": "Bond",
|
||||
"TypeSignatureScroll": "Signature Scroll",
|
||||
"TypeItemPattern": "Item Pattern"
|
||||
"TypeItemPattern": "Item Pattern",
|
||||
"TypeArmy_fortification": "Fortification",
|
||||
"TypeArmy_cohort": "Cohort"
|
||||
},
|
||||
"l5r5e": {
|
||||
"global": {
|
||||
@@ -364,6 +367,36 @@
|
||||
"adversary": "Adversario",
|
||||
"minion": "Esbirro"
|
||||
},
|
||||
"army": {
|
||||
"warlord": "Warlord",
|
||||
"allies_backers": "Allies and Backers",
|
||||
"purpose_mustering": "Purpose for Mustering",
|
||||
"battle_readiness": {
|
||||
"title": "Battle Readiness",
|
||||
"strength": "Strength",
|
||||
"casualties": "Casualties",
|
||||
"discipline": "Discipline",
|
||||
"panic": "Panic"
|
||||
},
|
||||
"commander": "Commander",
|
||||
"commander_abilities": "Commander's relevant abilities",
|
||||
"army_abilities": "Army Abilities",
|
||||
"commander_standing": "Commander's Standing",
|
||||
"supplies_logistics": "Supplies and Logistics",
|
||||
"past_battles": "Past Battles",
|
||||
"cohort": {
|
||||
"tab": "Cohorts",
|
||||
"title": "Cohorts",
|
||||
"leader": "Battle Readiness",
|
||||
"abilities": "Abilities"
|
||||
},
|
||||
"fortification": {
|
||||
"tab": "Fortifications",
|
||||
"title": "Fortification Held",
|
||||
"difficulty": "Difficulty Value",
|
||||
"attrition_reduction": "Attrition Reduction"
|
||||
}
|
||||
},
|
||||
"twenty_questions": {
|
||||
"title": "Veinte preguntas",
|
||||
"bt_abrev": "20P",
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
},
|
||||
"ACTOR": {
|
||||
"TypeCharacter": "Personnage Joueur",
|
||||
"TypeNpc": "Personnage non Joueur"
|
||||
"TypeNpc": "Personnage non Joueur",
|
||||
"TypeArmy": "Armée"
|
||||
},
|
||||
"ITEM": {
|
||||
"TypeItem": "Objet",
|
||||
@@ -34,7 +35,9 @@
|
||||
"TypeTitle": "Titre",
|
||||
"TypeBond": "Lien",
|
||||
"TypeSignature_scroll": "Rouleau de marque",
|
||||
"TypeItem_pattern": "Procédé de fabrication"
|
||||
"TypeItem_pattern": "Procédé de fabrication",
|
||||
"TypeArmy_fortification": "Fortification",
|
||||
"TypeArmy_cohort": "Régiment"
|
||||
},
|
||||
"l5r5e": {
|
||||
"global": {
|
||||
@@ -364,6 +367,36 @@
|
||||
"adversary": "Antagoniste",
|
||||
"minion": "Sous-fifre"
|
||||
},
|
||||
"army": {
|
||||
"warlord": "Seigneur de guerre",
|
||||
"allies_backers": "Alliés et Soutiens",
|
||||
"purpose_mustering": "Objectif du rassemblement",
|
||||
"battle_readiness": {
|
||||
"title": "Aptitudes au combat",
|
||||
"strength": "Force",
|
||||
"casualties": "Usure et pertes",
|
||||
"discipline": "Discipline",
|
||||
"panic": "Panique"
|
||||
},
|
||||
"commander": "Commandant",
|
||||
"commander_abilities": "Capacités du commandant",
|
||||
"army_abilities": "Capacités de l'armée",
|
||||
"commander_standing": "Position du commandant",
|
||||
"supplies_logistics": "Fournitures et logistique",
|
||||
"past_battles": "Batailles passées",
|
||||
"cohort": {
|
||||
"tab": "Régiments",
|
||||
"title": "Régiment",
|
||||
"leader": "Aptitude au combat",
|
||||
"abilities": "Capacités"
|
||||
},
|
||||
"fortification": {
|
||||
"tab": "Fortifications",
|
||||
"title": "Fortifications",
|
||||
"difficulty": "Difficulté",
|
||||
"attrition_reduction": "Attrition Reduction"
|
||||
}
|
||||
},
|
||||
"twenty_questions": {
|
||||
"title": "Le jeu des Vingt questions",
|
||||
"bt_abrev": "20Q",
|
||||
|
||||
@@ -55,6 +55,23 @@ export class ActorL5r5e extends Actor {
|
||||
{ overwrite: false }
|
||||
);
|
||||
break;
|
||||
|
||||
case "army":
|
||||
foundry.utils.mergeObject(
|
||||
data.token,
|
||||
{
|
||||
actorLink: true,
|
||||
disposition: 0, // neutral
|
||||
bar1: {
|
||||
attribute: "battle_readiness.casualties_strength",
|
||||
},
|
||||
bar2: {
|
||||
attribute: "battle_readiness.panic_discipline",
|
||||
},
|
||||
},
|
||||
{ overwrite: false }
|
||||
);
|
||||
break;
|
||||
}
|
||||
await super.create(data, options);
|
||||
}
|
||||
|
||||
78
system/scripts/actors/army-sheet.js
Normal file
78
system/scripts/actors/army-sheet.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import { BaseSheetL5r5e } from "./base-sheet.js";
|
||||
|
||||
/**
|
||||
* Sheet for Army "actor"
|
||||
*/
|
||||
export class ArmySheetL5r5e extends BaseSheetL5r5e {
|
||||
/**
|
||||
* Commons options
|
||||
*/
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "actor", "army"],
|
||||
template: CONFIG.l5r5e.paths.templates + "actors/army-sheet.html",
|
||||
width: 600,
|
||||
height: 800,
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "cohort" }],
|
||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
||||
});
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
getData(options = {}) {
|
||||
const sheetData = super.getData(options);
|
||||
|
||||
// Split Items by types
|
||||
sheetData.data.splitItemsList = this._splitItems(sheetData);
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split Items by types for better readability
|
||||
* @private
|
||||
*/
|
||||
_splitItems(sheetData) {
|
||||
const out = {
|
||||
army_cohort: [],
|
||||
army_fortification: [],
|
||||
};
|
||||
|
||||
sheetData.items.forEach((item) => {
|
||||
if (["army_cohort", "army_fortification"].includes(item.type)) {
|
||||
out[item.type].push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dropped data on the Actor sheet
|
||||
* @param {DragEvent} event
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
// *** Everything below here is only needed if the sheet is editable ***
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check item type and subtype
|
||||
const item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
|
||||
if (!item || item.documentName !== "Item" || !["army_cohort", "army_fortification"].includes(item.data.type)) {
|
||||
console.warn("L5R5E | Wrong type", item.data.type);
|
||||
return;
|
||||
}
|
||||
|
||||
// Can add the item - Foundry override cause props
|
||||
const allowed = Hooks.call("dropActorSheetData", this.actor, this, item);
|
||||
if (allowed === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
let itemData = item.data.toObject(true);
|
||||
|
||||
// Finally create the embed
|
||||
return this.actor.createEmbeddedDocuments("Item", [itemData]);
|
||||
}
|
||||
}
|
||||
547
system/scripts/actors/base-character-sheet.js
Normal file
547
system/scripts/actors/base-character-sheet.js
Normal file
@@ -0,0 +1,547 @@
|
||||
import { BaseSheetL5r5e } from "./base-sheet.js";
|
||||
|
||||
/**
|
||||
* Base Sheet for Character types (Character and Npc)
|
||||
*/
|
||||
export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
|
||||
/**
|
||||
* Commons options
|
||||
*/
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "actor"],
|
||||
// template: CONFIG.l5r5e.paths.templates + "actors/character-sheet.html",
|
||||
width: 600,
|
||||
height: 800,
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "skills" }],
|
||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
||||
});
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
getData(options = {}) {
|
||||
const sheetData = super.getData(options);
|
||||
|
||||
sheetData.data.stances = CONFIG.l5r5e.stances;
|
||||
sheetData.data.techniquesList = game.l5r5e.HelpersL5r5e.getTechniquesList({ displayInTypes: true });
|
||||
|
||||
// Split Techniques by types
|
||||
sheetData.data.splitTechniquesList = this._splitTechniques(sheetData);
|
||||
|
||||
// Split Items by types
|
||||
sheetData.data.splitItemsList = this._splitItems(sheetData);
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split Techniques by types for better readability
|
||||
* @private
|
||||
*/
|
||||
_splitTechniques(sheetData) {
|
||||
const out = {};
|
||||
const schoolTechniques = Array.from(CONFIG.l5r5e.techniques)
|
||||
.filter(([id, cfg]) => cfg.type === "school")
|
||||
.map(([id, cfg]) => id);
|
||||
|
||||
// Build the list order
|
||||
Array.from(CONFIG.l5r5e.techniques)
|
||||
.filter(([id, cfg]) => cfg.type !== "custom" || game.settings.get("l5r5e", "techniques-customs"))
|
||||
.forEach(([id, cfg]) => {
|
||||
out[id] = [];
|
||||
});
|
||||
|
||||
// Add tech the character knows
|
||||
sheetData.items.forEach((item) => {
|
||||
switch (item.type) {
|
||||
case "technique":
|
||||
if (!out[item.data.technique_type]) {
|
||||
console.warn(
|
||||
`L5R5E | Empty or unknown technique type[${item.data.technique_type}] forced to "kata" in item id[${item._id}], name[${item.name}]`
|
||||
);
|
||||
item.data.technique_type = "kata";
|
||||
}
|
||||
out[item.data.technique_type].push(item);
|
||||
break;
|
||||
|
||||
case "title":
|
||||
// Embed technique in titles
|
||||
Array.from(item.data.items).forEach(([id, embedItem]) => {
|
||||
if (embedItem.data.type === "technique") {
|
||||
if (!out[embedItem.data.data.technique_type]) {
|
||||
console.warn(
|
||||
`L5R5E | Empty or unknown technique type[${embedItem.data.data.technique_type}] forced to "kata" in item id[${id}], name[${embedItem.data.name}], parent: id[${item._id}], name[${item.name}]`
|
||||
);
|
||||
embedItem.data.data.technique_type = "kata";
|
||||
}
|
||||
out[embedItem.data.data.technique_type].push(embedItem.data);
|
||||
}
|
||||
});
|
||||
|
||||
// If unlocked, add the "title_ability" as technique (or always displayed for npc)
|
||||
if (item.data.xp_used >= item.data.xp_cost || this.document.type === "npc") {
|
||||
out["title_ability"].push(item);
|
||||
}
|
||||
break;
|
||||
} //swi
|
||||
});
|
||||
|
||||
// Remove unused techs
|
||||
Object.keys(out).forEach((tech) => {
|
||||
if (out[tech].length < 1 && !sheetData.data.data.techniques[tech] && !schoolTechniques.includes(tech)) {
|
||||
delete out[tech];
|
||||
}
|
||||
});
|
||||
|
||||
// Manage school add button
|
||||
sheetData.data.data.techniques["school_ability"] = out["school_ability"].length === 0;
|
||||
sheetData.data.data.techniques["mastery_ability"] = out["mastery_ability"].length === 0;
|
||||
|
||||
// Always display "school_ability", but display a empty "mastery_ability" field only if rank >= 5
|
||||
if (sheetData.data.data.identity?.school_rank < 5 && out["mastery_ability"].length === 0) {
|
||||
delete out["mastery_ability"];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split Items by types for better readability
|
||||
* @private
|
||||
*/
|
||||
_splitItems(sheetData) {
|
||||
const out = {
|
||||
weapon: [],
|
||||
armor: [],
|
||||
item: [],
|
||||
};
|
||||
|
||||
sheetData.items.forEach((item) => {
|
||||
if (["item", "armor", "weapon"].includes(item.type)) {
|
||||
out[item.type].push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dropped data on the Actor sheet
|
||||
* @param {DragEvent} event
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
// *** Everything below here is only needed if the sheet is editable ***
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check item type and subtype
|
||||
const item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
|
||||
if (!item || !["Item", "JournalEntry"].includes(item.documentName) || item.data.type === "property") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Specific curriculum journal drop
|
||||
if (item.documentName === "JournalEntry") {
|
||||
// npc does not have this
|
||||
if (!this.actor.data.data.identity?.school_curriculum_journal) {
|
||||
return;
|
||||
}
|
||||
this.actor.data.data.identity.school_curriculum_journal = {
|
||||
id: item.data._id,
|
||||
name: item.data.name,
|
||||
pack: item.pack || null,
|
||||
};
|
||||
await this.actor.update({
|
||||
data: {
|
||||
identity: {
|
||||
school_curriculum_journal: this.actor.data.data.identity.school_curriculum_journal,
|
||||
},
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Dropped a item with same "id" as one owned
|
||||
if (this.actor.data.items) {
|
||||
// Exit if we already owned exactly this id (drag a personal item on our own sheet)
|
||||
if (
|
||||
this.actor.data.items.some((embedItem) => {
|
||||
// Search in children
|
||||
if (embedItem.items instanceof Map && embedItem.items.has(item.data._id)) {
|
||||
return true;
|
||||
}
|
||||
return embedItem.data._id === item.data._id;
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add quantity instead if they have (id is different so use type and name)
|
||||
if (item.data.data.quantity) {
|
||||
const tmpItem = this.actor.data.items.find(
|
||||
(embedItem) => embedItem.name === item.data.name && embedItem.type === item.data.type
|
||||
);
|
||||
if (tmpItem && this._modifyQuantity(tmpItem.id, 1)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Can add the item - Foundry override cause props
|
||||
const allowed = Hooks.call("dropActorSheetData", this.actor, this, item);
|
||||
if (allowed === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
let itemData = item.data.toObject(true);
|
||||
|
||||
// Item subtype specific
|
||||
switch (itemData.type) {
|
||||
case "advancement":
|
||||
// Specific advancements, remove 1 to selected ring/skill
|
||||
await this.actor.addBonus(item);
|
||||
break;
|
||||
|
||||
case "title":
|
||||
// Generate new Ids for the embed items
|
||||
await item.generateNewIdsForAllEmbedItems();
|
||||
|
||||
// Add embed advancements bonus
|
||||
for (let [embedId, embedItem] of item.data.data.items) {
|
||||
if (embedItem.data.type === "advancement") {
|
||||
await this.actor.addBonus(embedItem);
|
||||
}
|
||||
}
|
||||
|
||||
// refresh data
|
||||
itemData = item.data.toObject(true);
|
||||
break;
|
||||
|
||||
case "technique":
|
||||
// School_ability and mastery_ability, allow only 1 per type
|
||||
if (CONFIG.l5r5e.techniques.get(itemData.data.technique_type)?.type === "school") {
|
||||
if (
|
||||
Array.from(this.actor.items).some((e) => {
|
||||
return (
|
||||
e.type === "technique" && e.data.data.technique_type === itemData.data.technique_type
|
||||
);
|
||||
})
|
||||
) {
|
||||
ui.notifications.info(game.i18n.localize("l5r5e.techniques.only_one"));
|
||||
return;
|
||||
}
|
||||
|
||||
// No cost for schools
|
||||
itemData.data.xp_cost = 0;
|
||||
itemData.data.xp_used = 0;
|
||||
itemData.data.in_curriculum = true;
|
||||
} else {
|
||||
// Check if technique is allowed for this character
|
||||
if (!game.user.isGM && !this.actor.data.data.techniques[itemData.data.technique_type]) {
|
||||
ui.notifications.info(game.i18n.localize("l5r5e.techniques.not_allowed"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify cost
|
||||
itemData.data.xp_cost =
|
||||
itemData.data.xp_cost > 0 ? itemData.data.xp_cost : CONFIG.l5r5e.xp.techniqueCost;
|
||||
itemData.data.xp_used = itemData.data.xp_cost;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Modify the bought at rank to the current actor rank
|
||||
if (itemData.data.bought_at_rank !== undefined && this.actor.data.data.identity?.school_rank) {
|
||||
itemData.data.bought_at_rank = this.actor.data.data.identity.school_rank;
|
||||
}
|
||||
|
||||
// Finally create the embed
|
||||
return this.actor.createEmbeddedDocuments("Item", [itemData]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events from the sheet.
|
||||
* @param {jQuery} html HTML content of the sheet.
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
// *** Everything below here is only needed if the sheet is editable ***
|
||||
if (!this.isEditable) {
|
||||
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;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
// Prepared (Initiative)
|
||||
html.find(".prepared-control").on("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this._switchPrepared();
|
||||
});
|
||||
|
||||
// Equipped / Readied
|
||||
html.find(".equip-readied-control").on("click", this._switchEquipReadied.bind(this));
|
||||
|
||||
// Others Advancements
|
||||
html.find(".item-advancement-choose").on("click", this._showDialogAddSubItem.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the state "prepared" (initiative)
|
||||
* @private
|
||||
*/
|
||||
_switchPrepared() {
|
||||
this.actor.data.data.prepared = !this.actor.data.data.prepared;
|
||||
this.actor.update({
|
||||
data: {
|
||||
prepared: this.actor.data.data.prepared,
|
||||
},
|
||||
});
|
||||
this.render(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a generic item with sub type
|
||||
* @param {string} type Item sub type (armor, weapon, bond...)
|
||||
* @param {boolean} isEquipped For item with prop "Equipped" set the value
|
||||
* @param {string|null} techniqueType Technique subtype (kata, shuji...)
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _createSubItem({ type, isEquipped = false, techniqueType = null }) {
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
|
||||
const created = await this.actor.createEmbeddedDocuments("Item", [
|
||||
{
|
||||
name: game.i18n.localize(`ITEM.Type${type.capitalize()}`),
|
||||
type: type,
|
||||
img: `${CONFIG.l5r5e.paths.assets}icons/items/${type}.svg`,
|
||||
},
|
||||
]);
|
||||
if (created?.length < 1) {
|
||||
return;
|
||||
}
|
||||
const item = this.actor.items.get(created[0].id);
|
||||
|
||||
// Assign current school rank to the new adv/tech
|
||||
if (this.actor.data.data.identity?.school_rank) {
|
||||
item.data.data.bought_at_rank = this.actor.data.data.identity.school_rank;
|
||||
if (["advancement", "technique"].includes(item.data.type)) {
|
||||
item.data.data.rank = this.actor.data.data.identity.school_rank;
|
||||
}
|
||||
}
|
||||
|
||||
switch (item.data.type) {
|
||||
case "item": // no break
|
||||
case "armor": // no break
|
||||
case "weapon":
|
||||
item.data.data.equipped = isEquipped;
|
||||
break;
|
||||
|
||||
case "technique": {
|
||||
// If technique, select the current sub-type
|
||||
if (CONFIG.l5r5e.techniques.get(techniqueType)) {
|
||||
item.data.name = game.i18n.localize(`l5r5e.techniques.${techniqueType}`);
|
||||
item.data.img = `${CONFIG.l5r5e.paths.assets}icons/techs/${techniqueType}.svg`;
|
||||
item.data.data.technique_type = techniqueType;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
item.sheet.render(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a dialog to choose what Item to add
|
||||
* @param {Event} event
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _showDialogAddSubItem(event) {
|
||||
game.l5r5e.HelpersL5r5e.showSubItemDialog(["bond", "title", "signature_scroll", "item_pattern"]).then(
|
||||
(selectedType) => {
|
||||
this._createSubItem({ type: selectedType });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a generic item with sub type
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
async _addSubItem(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const type = $(event.currentTarget).data("item-type");
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isEquipped = $(event.currentTarget).data("item-equipped") || false;
|
||||
const techniqueType = $(event.currentTarget).data("tech-type") || null;
|
||||
|
||||
return this._createSubItem({ type, isEquipped, techniqueType });
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a generic item with sub type
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_deleteSubItem(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
if (!itemId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tmpItem = this.actor.items.get(itemId);
|
||||
if (!tmpItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove 1 qty if possible
|
||||
if (tmpItem.data.data.quantity > 1 && this._modifyQuantity(tmpItem.id, -1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const callback = async () => {
|
||||
switch (tmpItem.type) {
|
||||
case "advancement":
|
||||
// Remove advancements bonus (1 to selected ring/skill)
|
||||
await this.actor.removeBonus(tmpItem);
|
||||
break;
|
||||
|
||||
case "title":
|
||||
// Remove embed advancements bonus
|
||||
for (let [embedId, embedItem] of tmpItem.data.data.items) {
|
||||
if (embedItem.data.type === "advancement") {
|
||||
await this.actor.removeBonus(embedItem);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return this.actor.deleteEmbeddedDocuments("Item", [itemId]);
|
||||
};
|
||||
|
||||
// Holing Ctrl = without confirm
|
||||
if (event.ctrlKey) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
game.l5r5e.HelpersL5r5e.confirmDeleteDialog(
|
||||
game.i18n.format("l5r5e.global.delete_confirm", { name: tmpItem.name }),
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch "in_curriculum"
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_switchSubItemCurriculum(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
const item = this.actor.items.get(itemId);
|
||||
if (item.type !== "item") {
|
||||
item.update({
|
||||
data: {
|
||||
in_curriculum: !item.data.data.in_curriculum,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or subtract a quantity to a owned item
|
||||
* @private
|
||||
*/
|
||||
_modifyQuantity(itemId, add) {
|
||||
const tmpItem = this.actor.items.get(itemId);
|
||||
if (tmpItem) {
|
||||
tmpItem.data.data.quantity = Math.max(1, tmpItem.data.data.quantity + add);
|
||||
tmpItem.update({
|
||||
data: {
|
||||
quantity: tmpItem.data.data.quantity,
|
||||
},
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch Readied state on a weapon
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_switchEquipReadied(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const type = $(event.currentTarget).data("type");
|
||||
if (!["equipped", "readied"].includes(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
const tmpItem = this.actor.items.get(itemId);
|
||||
if (!tmpItem || tmpItem.data.data[type] === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
tmpItem.data.data[type] = !tmpItem.data.data[type];
|
||||
const data = {
|
||||
equipped: tmpItem.data.data.equipped,
|
||||
};
|
||||
// Only weapons
|
||||
if (tmpItem.data.data.readied !== undefined) {
|
||||
data.readied = tmpItem.data.data.readied;
|
||||
}
|
||||
|
||||
tmpItem.update({ data });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the skillId for this weaponId
|
||||
* @private
|
||||
*/
|
||||
_getWeaponSkillId(weaponId) {
|
||||
const item = this.actor.items.get(weaponId);
|
||||
if (!!item && item.type === "weapon") {
|
||||
return item.data.data.skill;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ export class BaseSheetL5r5e extends ActorSheet {
|
||||
* Commons options
|
||||
*/
|
||||
static get defaultOptions() {
|
||||
return mergeObject(super.defaultOptions, {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "actor"],
|
||||
// template: CONFIG.l5r5e.paths.templates + "actors/character-sheet.html",
|
||||
width: 600,
|
||||
@@ -45,114 +45,15 @@ export class BaseSheetL5r5e extends ActorSheet {
|
||||
const sheetData = super.getData(options);
|
||||
|
||||
sheetData.data.dtypes = ["String", "Number", "Boolean"];
|
||||
sheetData.data.stances = CONFIG.l5r5e.stances;
|
||||
sheetData.data.techniquesList = game.l5r5e.HelpersL5r5e.getTechniquesList({ displayInTypes: true });
|
||||
|
||||
// Sort Items by name
|
||||
sheetData.items.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
// Split Techniques by types
|
||||
sheetData.data.splitTechniquesList = this._splitTechniques(sheetData);
|
||||
|
||||
// Split Items by types
|
||||
sheetData.data.splitItemsList = this._splitItems(sheetData);
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split Techniques by types for better readability
|
||||
* @private
|
||||
*/
|
||||
_splitTechniques(sheetData) {
|
||||
const out = {};
|
||||
const schoolTechniques = Array.from(CONFIG.l5r5e.techniques)
|
||||
.filter(([id, cfg]) => cfg.type === "school")
|
||||
.map(([id, cfg]) => id);
|
||||
|
||||
// Build the list order
|
||||
Array.from(CONFIG.l5r5e.techniques)
|
||||
.filter(([id, cfg]) => cfg.type !== "custom" || game.settings.get("l5r5e", "techniques-customs"))
|
||||
.forEach(([id, cfg]) => {
|
||||
out[id] = [];
|
||||
});
|
||||
|
||||
// Add tech the character knows
|
||||
sheetData.items.forEach((item) => {
|
||||
switch (item.type) {
|
||||
case "technique":
|
||||
if (!out[item.data.technique_type]) {
|
||||
console.warn(
|
||||
`L5R5E | Empty or unknown technique type[${item.data.technique_type}] forced to "kata" in item id[${item._id}], name[${item.name}]`
|
||||
);
|
||||
item.data.technique_type = "kata";
|
||||
}
|
||||
out[item.data.technique_type].push(item);
|
||||
break;
|
||||
|
||||
case "title":
|
||||
// Embed technique in titles
|
||||
Array.from(item.data.items).forEach(([id, embedItem]) => {
|
||||
if (embedItem.data.type === "technique") {
|
||||
if (!out[embedItem.data.data.technique_type]) {
|
||||
console.warn(
|
||||
`L5R5E | Empty or unknown technique type[${embedItem.data.data.technique_type}] forced to "kata" in item id[${id}], name[${embedItem.data.name}], parent: id[${item._id}], name[${item.name}]`
|
||||
);
|
||||
embedItem.data.data.technique_type = "kata";
|
||||
}
|
||||
out[embedItem.data.data.technique_type].push(embedItem.data);
|
||||
}
|
||||
});
|
||||
|
||||
// If unlocked, add the "title_ability" as technique (or always displayed for npc)
|
||||
if (item.data.xp_used >= item.data.xp_cost || this.document.type === "npc") {
|
||||
out["title_ability"].push(item);
|
||||
}
|
||||
break;
|
||||
} //swi
|
||||
});
|
||||
|
||||
// Remove unused techs
|
||||
Object.keys(out).forEach((tech) => {
|
||||
if (out[tech].length < 1 && !sheetData.data.data.techniques[tech] && !schoolTechniques.includes(tech)) {
|
||||
delete out[tech];
|
||||
}
|
||||
});
|
||||
|
||||
// Manage school add button
|
||||
sheetData.data.data.techniques["school_ability"] = out["school_ability"].length === 0;
|
||||
sheetData.data.data.techniques["mastery_ability"] = out["mastery_ability"].length === 0;
|
||||
|
||||
// Always display "school_ability", but display a empty "mastery_ability" field only if rank >= 5
|
||||
if (sheetData.data.data.identity?.school_rank < 5 && out["mastery_ability"].length === 0) {
|
||||
delete out["mastery_ability"];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split Items by types for better readability
|
||||
* @private
|
||||
*/
|
||||
_splitItems(sheetData) {
|
||||
const out = {
|
||||
weapon: [],
|
||||
armor: [],
|
||||
item: [],
|
||||
};
|
||||
|
||||
sheetData.items.forEach((item) => {
|
||||
if (["item", "armor", "weapon"].includes(item.type)) {
|
||||
out[item.type].push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a light sheet if in "limited" state
|
||||
* @override
|
||||
@@ -196,141 +97,6 @@ export class BaseSheetL5r5e extends ActorSheet {
|
||||
return super._updateObject(event, formData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dropped data on the Actor sheet
|
||||
* @param {DragEvent} event
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
// *** Everything below here is only needed if the sheet is editable ***
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check item type and subtype
|
||||
const item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
|
||||
if (!item || !["Item", "JournalEntry"].includes(item.documentName) || item.data.type === "property") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Specific curriculum journal drop
|
||||
if (item.documentName === "JournalEntry") {
|
||||
// npc does not have this
|
||||
if (!this.actor.data.data.identity?.school_curriculum_journal) {
|
||||
return;
|
||||
}
|
||||
this.actor.data.data.identity.school_curriculum_journal = {
|
||||
id: item.data._id,
|
||||
name: item.data.name,
|
||||
pack: item.pack || null,
|
||||
};
|
||||
await this.actor.update({
|
||||
data: {
|
||||
identity: {
|
||||
school_curriculum_journal: this.actor.data.data.identity.school_curriculum_journal,
|
||||
},
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Dropped a item with same "id" as one owned
|
||||
if (this.actor.data.items) {
|
||||
// Exit if we already owned exactly this id (drag a personal item on our own sheet)
|
||||
if (
|
||||
this.actor.data.items.some((embedItem) => {
|
||||
// Search in children
|
||||
if (embedItem.items instanceof Map && embedItem.items.has(item.data._id)) {
|
||||
return true;
|
||||
}
|
||||
return embedItem.data._id === item.data._id;
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add quantity instead if they have (id is different so use type and name)
|
||||
if (item.data.data.quantity) {
|
||||
const tmpItem = this.actor.data.items.find(
|
||||
(embedItem) => embedItem.name === item.data.name && embedItem.type === item.data.type
|
||||
);
|
||||
if (tmpItem && this._modifyQuantity(tmpItem.id, 1)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Can add the item - Foundry override cause props
|
||||
const allowed = Hooks.call("dropActorSheetData", this.actor, this, item);
|
||||
if (allowed === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
let itemData = item.data.toObject(true);
|
||||
|
||||
// Item subtype specific
|
||||
switch (itemData.type) {
|
||||
case "advancement":
|
||||
// Specific advancements, remove 1 to selected ring/skill
|
||||
await this.actor.addBonus(item);
|
||||
break;
|
||||
|
||||
case "title":
|
||||
// Generate new Ids for the embed items
|
||||
await item.generateNewIdsForAllEmbedItems();
|
||||
|
||||
// Add embed advancements bonus
|
||||
for (let [embedId, embedItem] of item.data.data.items) {
|
||||
if (embedItem.data.type === "advancement") {
|
||||
await this.actor.addBonus(embedItem);
|
||||
}
|
||||
}
|
||||
|
||||
// refresh data
|
||||
itemData = item.data.toObject(true);
|
||||
break;
|
||||
|
||||
case "technique":
|
||||
// School_ability and mastery_ability, allow only 1 per type
|
||||
if (CONFIG.l5r5e.techniques.get(itemData.data.technique_type)?.type === "school") {
|
||||
if (
|
||||
Array.from(this.actor.items).some((e) => {
|
||||
return (
|
||||
e.type === "technique" && e.data.data.technique_type === itemData.data.technique_type
|
||||
);
|
||||
})
|
||||
) {
|
||||
ui.notifications.info(game.i18n.localize("l5r5e.techniques.only_one"));
|
||||
return;
|
||||
}
|
||||
|
||||
// No cost for schools
|
||||
itemData.data.xp_cost = 0;
|
||||
itemData.data.xp_used = 0;
|
||||
itemData.data.in_curriculum = true;
|
||||
} else {
|
||||
// Check if technique is allowed for this character
|
||||
if (!game.user.isGM && !this.actor.data.data.techniques[itemData.data.technique_type]) {
|
||||
ui.notifications.info(game.i18n.localize("l5r5e.techniques.not_allowed"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify cost
|
||||
itemData.data.xp_cost =
|
||||
itemData.data.xp_cost > 0 ? itemData.data.xp_cost : CONFIG.l5r5e.xp.techniqueCost;
|
||||
itemData.data.xp_used = itemData.data.xp_cost;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Modify the bought at rank to the current actor rank
|
||||
if (itemData.data.bought_at_rank !== undefined && this.actor.data.data.identity?.school_rank) {
|
||||
itemData.data.bought_at_rank = this.actor.data.data.identity.school_rank;
|
||||
}
|
||||
|
||||
// Finally create the embed
|
||||
return this.actor.createEmbeddedDocuments("Item", [itemData]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events from the sheet.
|
||||
* @param {jQuery} html HTML content of the sheet.
|
||||
@@ -346,27 +112,6 @@ export class BaseSheetL5r5e extends ActorSheet {
|
||||
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;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
// On focus on one numeric element, select all text for better experience
|
||||
html.find(".select-on-focus").on("focus", (event) => {
|
||||
event.preventDefault();
|
||||
@@ -374,48 +119,19 @@ export class BaseSheetL5r5e extends ActorSheet {
|
||||
event.target.select();
|
||||
});
|
||||
|
||||
// Prepared (Initiative)
|
||||
html.find(".prepared-control").on("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this._switchPrepared();
|
||||
});
|
||||
|
||||
// Equipped / Readied
|
||||
html.find(".equip-readied-control").on("click", this._switchEquipReadied.bind(this));
|
||||
|
||||
// *** Items : add, edit, delete ***
|
||||
html.find(".item-add").on("click", this._addSubItem.bind(this));
|
||||
html.find(`.item-edit`).on("click", this._editSubItem.bind(this));
|
||||
html.find(`.item-delete`).on("click", this._deleteSubItem.bind(this));
|
||||
|
||||
// Others Advancements
|
||||
html.find(".item-advancement-choose").on("click", this._showDialogAddSubItem.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the state "prepared" (initiative)
|
||||
* @private
|
||||
*/
|
||||
_switchPrepared() {
|
||||
this.actor.data.data.prepared = !this.actor.data.data.prepared;
|
||||
this.actor.update({
|
||||
data: {
|
||||
prepared: this.actor.data.data.prepared,
|
||||
},
|
||||
});
|
||||
this.render(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a generic item with sub type
|
||||
* @param {string} type Item sub type (armor, weapon, bond...)
|
||||
* @param {boolean} isEquipped For item with prop "Equipped" set the value
|
||||
* @param {string|null} techniqueType Technique subtype (kata, shuji...)
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _createSubItem({ type, isEquipped = false, techniqueType = null }) {
|
||||
async _createSubItem({ type }) {
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
@@ -432,49 +148,9 @@ export class BaseSheetL5r5e extends ActorSheet {
|
||||
}
|
||||
const item = this.actor.items.get(created[0].id);
|
||||
|
||||
// Assign current school rank to the new adv/tech
|
||||
if (this.actor.data.data.identity?.school_rank) {
|
||||
item.data.data.bought_at_rank = this.actor.data.data.identity.school_rank;
|
||||
if (["advancement", "technique"].includes(item.data.type)) {
|
||||
item.data.data.rank = this.actor.data.data.identity.school_rank;
|
||||
}
|
||||
}
|
||||
|
||||
switch (item.data.type) {
|
||||
case "item": // no break
|
||||
case "armor": // no break
|
||||
case "weapon":
|
||||
item.data.data.equipped = isEquipped;
|
||||
break;
|
||||
|
||||
case "technique": {
|
||||
// If technique, select the current sub-type
|
||||
if (CONFIG.l5r5e.techniques.get(techniqueType)) {
|
||||
item.data.name = game.i18n.localize(`l5r5e.techniques.${techniqueType}`);
|
||||
item.data.img = `${CONFIG.l5r5e.paths.assets}icons/techs/${techniqueType}.svg`;
|
||||
item.data.data.technique_type = techniqueType;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
item.sheet.render(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a dialog to choose what Item to add
|
||||
* @param {Event} event
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _showDialogAddSubItem(event) {
|
||||
game.l5r5e.HelpersL5r5e.showSubItemDialog(["bond", "title", "signature_scroll", "item_pattern"]).then(
|
||||
(selectedType) => {
|
||||
this._createSubItem({ type: selectedType });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a generic item with sub type
|
||||
* @param {Event} event
|
||||
@@ -489,10 +165,7 @@ export class BaseSheetL5r5e extends ActorSheet {
|
||||
return;
|
||||
}
|
||||
|
||||
const isEquipped = $(event.currentTarget).data("item-equipped") || false;
|
||||
const techniqueType = $(event.currentTarget).data("tech-type") || null;
|
||||
|
||||
return this._createSubItem({ type, isEquipped, techniqueType });
|
||||
return this._createSubItem({ type });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,27 +203,7 @@ export class BaseSheetL5r5e extends ActorSheet {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove 1 qty if possible
|
||||
if (tmpItem.data.data.quantity > 1 && this._modifyQuantity(tmpItem.id, -1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const callback = async () => {
|
||||
switch (tmpItem.type) {
|
||||
case "advancement":
|
||||
// Remove advancements bonus (1 to selected ring/skill)
|
||||
await this.actor.removeBonus(tmpItem);
|
||||
break;
|
||||
|
||||
case "title":
|
||||
// Remove embed advancements bonus
|
||||
for (let [embedId, embedItem] of tmpItem.data.data.items) {
|
||||
if (embedItem.data.type === "advancement") {
|
||||
await this.actor.removeBonus(embedItem);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return this.actor.deleteEmbeddedDocuments("Item", [itemId]);
|
||||
};
|
||||
|
||||
@@ -564,86 +217,4 @@ export class BaseSheetL5r5e extends ActorSheet {
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch "in_curriculum"
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_switchSubItemCurriculum(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
const item = this.actor.items.get(itemId);
|
||||
if (item.type !== "item") {
|
||||
item.update({
|
||||
data: {
|
||||
in_curriculum: !item.data.data.in_curriculum,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or subtract a quantity to a owned item
|
||||
* @private
|
||||
*/
|
||||
_modifyQuantity(itemId, add) {
|
||||
const tmpItem = this.actor.items.get(itemId);
|
||||
if (tmpItem) {
|
||||
tmpItem.data.data.quantity = Math.max(1, tmpItem.data.data.quantity + add);
|
||||
tmpItem.update({
|
||||
data: {
|
||||
quantity: tmpItem.data.data.quantity,
|
||||
},
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch Readied state on a weapon
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_switchEquipReadied(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const type = $(event.currentTarget).data("type");
|
||||
if (!["equipped", "readied"].includes(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
const tmpItem = this.actor.items.get(itemId);
|
||||
if (!tmpItem || tmpItem.data.data[type] === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
tmpItem.data.data[type] = !tmpItem.data.data[type];
|
||||
const data = {
|
||||
equipped: tmpItem.data.data.equipped,
|
||||
};
|
||||
// Only weapons
|
||||
if (tmpItem.data.data.readied !== undefined) {
|
||||
data.readied = tmpItem.data.data.readied;
|
||||
}
|
||||
|
||||
tmpItem.update({ data });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the skillId for this weaponId
|
||||
* @private
|
||||
*/
|
||||
_getWeaponSkillId(weaponId) {
|
||||
const item = this.actor.items.get(weaponId);
|
||||
if (!!item && item.type === "weapon") {
|
||||
return item.data.data.skill;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { BaseSheetL5r5e } from "./base-sheet.js";
|
||||
import { BaseCharacterSheetL5r5e } from "./base-character-sheet.js";
|
||||
import { TwentyQuestionsDialog } from "./twenty-questions-dialog.js";
|
||||
|
||||
/**
|
||||
* Actor / Character Sheet
|
||||
*/
|
||||
export class CharacterSheetL5r5e extends BaseSheetL5r5e {
|
||||
export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "actor"],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { BaseSheetL5r5e } from "./base-sheet.js";
|
||||
import { BaseCharacterSheetL5r5e } from "./base-character-sheet.js";
|
||||
|
||||
/**
|
||||
* NPC Sheet
|
||||
*/
|
||||
export class NpcSheetL5r5e extends BaseSheetL5r5e {
|
||||
export class NpcSheetL5r5e extends BaseCharacterSheetL5r5e {
|
||||
/**
|
||||
* Sub Types
|
||||
*/
|
||||
|
||||
@@ -478,7 +478,7 @@ export class HelpersL5r5e {
|
||||
* Get a Item from a Actor Sheet
|
||||
* @param {Event} event HTML Event
|
||||
* @param {ActorL5r5e} actor
|
||||
* @return {ItemL5r5e}
|
||||
* @return {Promise<ItemL5r5e>}
|
||||
*/
|
||||
static async getEmbedItemByEvent(event, actor) {
|
||||
const current = $(event.currentTarget);
|
||||
|
||||
17
system/scripts/items/army-cohort-sheet.js
Normal file
17
system/scripts/items/army-cohort-sheet.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { ItemSheetL5r5e } from "./item-sheet.js";
|
||||
|
||||
/**
|
||||
* @extends {ItemSheetL5r5e}
|
||||
*/
|
||||
export class ArmyCohortSheetL5r5e extends ItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "army-cohort"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/army-cohort/army-cohort-sheet.html",
|
||||
width: 520,
|
||||
height: 480,
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
|
||||
});
|
||||
}
|
||||
}
|
||||
17
system/scripts/items/army-fortification-sheet.js
Normal file
17
system/scripts/items/army-fortification-sheet.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { ItemSheetL5r5e } from "./item-sheet.js";
|
||||
|
||||
/**
|
||||
* @extends {ItemSheetL5r5e}
|
||||
*/
|
||||
export class ArmyFortificationSheetL5r5e extends ItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "army-fortification"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/army-fortification/army-fortification-sheet.html",
|
||||
width: 520,
|
||||
height: 480,
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
|
||||
});
|
||||
}
|
||||
}
|
||||
166
system/scripts/items/base-item-sheet.js
Normal file
166
system/scripts/items/base-item-sheet.js
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* Extend the basic ItemSheet with some very simple modifications
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class BaseItemSheetL5r5e extends ItemSheet {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "item"],
|
||||
//template: CONFIG.l5r5e.paths.templates + "items/item/item-sheet.html",
|
||||
width: 520,
|
||||
height: 480,
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the SendToChat button on top of sheet
|
||||
* @override
|
||||
*/
|
||||
_getHeaderButtons() {
|
||||
let buttons = super._getHeaderButtons();
|
||||
|
||||
// Send To Chat
|
||||
buttons.unshift({
|
||||
label: game.i18n.localize("l5r5e.global.send_to_chat"),
|
||||
class: "send-to-chat",
|
||||
icon: "fas fa-comment-dots",
|
||||
onclick: () =>
|
||||
game.l5r5e.HelpersL5r5e.debounce(
|
||||
"send2chat-" + this.object.id,
|
||||
() => game.l5r5e.HelpersL5r5e.sendToChat(this.object),
|
||||
2000,
|
||||
true
|
||||
)(),
|
||||
});
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object|Promise}
|
||||
*/
|
||||
async getData(options = {}) {
|
||||
const sheetData = await super.getData(options);
|
||||
|
||||
sheetData.data.dtypes = ["String", "Number", "Boolean"];
|
||||
|
||||
// Fix editable
|
||||
sheetData.editable = this.isEditable;
|
||||
sheetData.options.editable = sheetData.editable;
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a named TinyMCE text editor
|
||||
* @param {string} name The named data field which the editor modifies.
|
||||
* @param {object} options TinyMCE initialization options passed to TextEditor.create
|
||||
* @param {string} initialContent Initial text content for the editor area.
|
||||
* @override
|
||||
*/
|
||||
activateEditor(name, options = {}, initialContent = "") {
|
||||
if (name === "data.description" && initialContent) {
|
||||
initialContent = game.l5r5e.HelpersL5r5e.convertSymbols(initialContent, false);
|
||||
}
|
||||
super.activateEditor(name, options, initialContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called upon form submission after form data is validated
|
||||
* @param event {Event} The initial triggering submission event
|
||||
* @param formData {Object} The object of validated form data with which to update the object
|
||||
* @returns {Promise} A Promise which resolves once the update operation has completed
|
||||
* @override
|
||||
*/
|
||||
async _updateObject(event, formData) {
|
||||
if (formData["data.description"]) {
|
||||
// Base links (Journal, compendiums...)
|
||||
formData["data.description"] = TextEditor.enrichHTML(formData["data.description"]);
|
||||
// L5R Symbols
|
||||
formData["data.description"] = game.l5r5e.HelpersL5r5e.convertSymbols(formData["data.description"], true);
|
||||
}
|
||||
return super._updateObject(event, formData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events from the sheet.
|
||||
* @param {jQuery} html HTML content of the sheet.
|
||||
* @override
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
// Commons
|
||||
game.l5r5e.HelpersL5r5e.commonListeners(html, this.actor);
|
||||
|
||||
// Everything below here is only needed if the sheet is editable
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// On focus on one numeric element, select all text for better experience
|
||||
html.find(".select-on-focus").on("focus", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.target.select();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a embed item
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_addSubItem(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
console.warn("L5R5E | TODO ItemSheetL5r5e._addSubItem()", itemId); // TODO _addSubItem Currently not used, title override it
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a embed item
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_editSubItem(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
const item = this.document.items.get(itemId);
|
||||
if (item) {
|
||||
item.sheet.render(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a embed item
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_deleteSubItem(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
const item = this.document.getEmbedItem(itemId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
const callback = async () => {
|
||||
this.document.deleteEmbedItem(itemId);
|
||||
};
|
||||
|
||||
// Holing Ctrl = without confirm
|
||||
if (event.ctrlKey) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
game.l5r5e.HelpersL5r5e.confirmDeleteDialog(
|
||||
game.i18n.format("l5r5e.global.delete_confirm", { name: item.name }),
|
||||
callback
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import { BaseItemSheetL5r5e } from "./base-item-sheet.js";
|
||||
|
||||
/**
|
||||
* Extend the basic ItemSheet with some very simple modifications
|
||||
* Extend BaseItemSheetL5r5e with modifications for objects
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class ItemSheetL5r5e extends ItemSheet {
|
||||
export class ItemSheetL5r5e extends BaseItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
@@ -14,46 +16,17 @@ export class ItemSheetL5r5e extends ItemSheet {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the SendToChat button on top of sheet
|
||||
* @override
|
||||
*/
|
||||
_getHeaderButtons() {
|
||||
let buttons = super._getHeaderButtons();
|
||||
|
||||
// Send To Chat
|
||||
buttons.unshift({
|
||||
label: game.i18n.localize("l5r5e.global.send_to_chat"),
|
||||
class: "send-to-chat",
|
||||
icon: "fas fa-comment-dots",
|
||||
onclick: () =>
|
||||
game.l5r5e.HelpersL5r5e.debounce(
|
||||
"send2chat-" + this.object.id,
|
||||
() => game.l5r5e.HelpersL5r5e.sendToChat(this.object),
|
||||
2000,
|
||||
true
|
||||
)(),
|
||||
});
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object|Promise}
|
||||
*/
|
||||
async getData(options = {}) {
|
||||
const sheetData = await super.getData(options);
|
||||
|
||||
sheetData.data.dtypes = ["String", "Number", "Boolean"];
|
||||
sheetData.data.ringsList = game.l5r5e.HelpersL5r5e.getRingsList();
|
||||
|
||||
// Prepare Properties (id/name => object)
|
||||
await this._prepareProperties(sheetData);
|
||||
|
||||
// Fix editable
|
||||
sheetData.editable = this.isEditable;
|
||||
sheetData.options.editable = sheetData.editable;
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
@@ -79,37 +52,6 @@ export class ItemSheetL5r5e extends ItemSheet {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a named TinyMCE text editor
|
||||
* @param {string} name The named data field which the editor modifies.
|
||||
* @param {object} options TinyMCE initialization options passed to TextEditor.create
|
||||
* @param {string} initialContent Initial text content for the editor area.
|
||||
* @override
|
||||
*/
|
||||
activateEditor(name, options = {}, initialContent = "") {
|
||||
if (name === "data.description" && initialContent) {
|
||||
initialContent = game.l5r5e.HelpersL5r5e.convertSymbols(initialContent, false);
|
||||
}
|
||||
super.activateEditor(name, options, initialContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called upon form submission after form data is validated
|
||||
* @param event {Event} The initial triggering submission event
|
||||
* @param formData {Object} The object of validated form data with which to update the object
|
||||
* @returns {Promise} A Promise which resolves once the update operation has completed
|
||||
* @override
|
||||
*/
|
||||
async _updateObject(event, formData) {
|
||||
if (formData["data.description"]) {
|
||||
// Base links (Journal, compendiums...)
|
||||
formData["data.description"] = TextEditor.enrichHTML(formData["data.description"]);
|
||||
// L5R Symbols
|
||||
formData["data.description"] = game.l5r5e.HelpersL5r5e.convertSymbols(formData["data.description"], true);
|
||||
}
|
||||
return super._updateObject(event, formData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events from the sheet.
|
||||
* @param {jQuery} html HTML content of the sheet.
|
||||
@@ -118,21 +60,11 @@ export class ItemSheetL5r5e extends ItemSheet {
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
// Commons
|
||||
game.l5r5e.HelpersL5r5e.commonListeners(html, this.actor);
|
||||
|
||||
// Everything below here is only needed if the sheet is editable
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// On focus on one numeric element, select all text for better experience
|
||||
html.find(".select-on-focus").on("focus", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.target.select();
|
||||
});
|
||||
|
||||
// Delete a property
|
||||
html.find(`.property-delete`).on("click", this._deleteProperty.bind(this));
|
||||
}
|
||||
@@ -260,60 +192,4 @@ export class ItemSheetL5r5e extends ItemSheet {
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a embed item
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_addSubItem(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
console.warn("L5R5E | TODO ItemSheetL5r5e._addSubItem()", itemId); // TODO _addSubItem Currently not used, title override it
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a embed item
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_editSubItem(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
const item = this.document.items.get(itemId);
|
||||
if (item) {
|
||||
item.sheet.render(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a embed item
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_deleteSubItem(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
const item = this.document.getEmbedItem(itemId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
const callback = async () => {
|
||||
this.document.deleteEmbedItem(itemId);
|
||||
};
|
||||
|
||||
// Holing Ctrl = without confirm
|
||||
if (event.ctrlKey) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
game.l5r5e.HelpersL5r5e.confirmDeleteDialog(
|
||||
game.i18n.format("l5r5e.global.delete_confirm", { name: item.name }),
|
||||
callback
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import HooksL5r5e from "./hooks.js";
|
||||
import { ActorL5r5e } from "./actor.js";
|
||||
import { CharacterSheetL5r5e } from "./actors/character-sheet.js";
|
||||
import { NpcSheetL5r5e } from "./actors/npc-sheet.js";
|
||||
import { ArmySheetL5r5e } from "./actors/army-sheet.js";
|
||||
// Dice and rolls
|
||||
import { L5rBaseDie } from "./dice/dietype/l5r-base-die.js";
|
||||
import { AbilityDie } from "./dice/dietype/ability-die.js";
|
||||
@@ -32,6 +33,8 @@ import { TitleSheetL5r5e } from "./items/title-sheet.js";
|
||||
import { BondSheetL5r5e } from "./items/bond-sheet.js";
|
||||
import { SignatureScrollSheetL5r5e } from "./items/signature-scroll-sheet.js";
|
||||
import { ItemPatternSheetL5r5e } from "./items/item-pattern-sheet.js";
|
||||
import { ArmyCohortSheetL5r5e } from "./items/army-cohort-sheet.js";
|
||||
import { ArmyFortificationSheetL5r5e } from "./items/army-fortification-sheet.js";
|
||||
// JournalEntry
|
||||
import { JournalL5r5e } from "./journal.js";
|
||||
import { BaseJournalSheetL5r5e } from "./journals/base-journal-sheet.js";
|
||||
@@ -106,6 +109,7 @@ Hooks.once("init", async () => {
|
||||
Actors.unregisterSheet("core", ActorSheet);
|
||||
Actors.registerSheet("l5r5e", CharacterSheetL5r5e, { types: ["character"], makeDefault: true });
|
||||
Actors.registerSheet("l5r5e", NpcSheetL5r5e, { types: ["npc"], makeDefault: true });
|
||||
Actors.registerSheet("l5r5e", ArmySheetL5r5e, { types: ["army"], makeDefault: true });
|
||||
|
||||
// Items
|
||||
Items.unregisterSheet("core", ItemSheet);
|
||||
@@ -120,6 +124,8 @@ Hooks.once("init", async () => {
|
||||
Items.registerSheet("l5r5e", BondSheetL5r5e, { types: ["bond"], makeDefault: true });
|
||||
Items.registerSheet("l5r5e", SignatureScrollSheetL5r5e, { types: ["signature_scroll"], makeDefault: true });
|
||||
Items.registerSheet("l5r5e", ItemPatternSheetL5r5e, { types: ["item_pattern"], makeDefault: true });
|
||||
Items.registerSheet("l5r5e", ArmyCohortSheetL5r5e, { types: ["army_cohort"], makeDefault: true });
|
||||
Items.registerSheet("l5r5e", ArmyFortificationSheetL5r5e, { types: ["army_fortification"], makeDefault: true });
|
||||
|
||||
// Journal
|
||||
Items.unregisterSheet("core", JournalSheet);
|
||||
|
||||
@@ -28,6 +28,10 @@ export const PreloadTemplates = async function () {
|
||||
`${tpl}actors/npc/social.html`,
|
||||
`${tpl}actors/npc/skill.html`,
|
||||
`${tpl}actors/npc/techniques.html`,
|
||||
// *** Actors : Army ***
|
||||
`${tpl}actors/army/cohort.html`,
|
||||
`${tpl}actors/army/fortification.html`,
|
||||
`${tpl}actors/army/others.html`,
|
||||
// *** Items ***
|
||||
`${tpl}items/advancement/advancement-entry.html`,
|
||||
`${tpl}items/advancement/advancement-sheet.html`,
|
||||
@@ -57,5 +61,7 @@ export const PreloadTemplates = async function () {
|
||||
`${tpl}items/weapon/weapons.html`,
|
||||
`${tpl}items/weapon/weapon-entry.html`,
|
||||
`${tpl}items/weapon/weapon-sheet.html`,
|
||||
`${tpl}items/army-cohort/army-cohort-entry.html`,
|
||||
`${tpl}items/army-fortification/army-fortification-entry.html`,
|
||||
]);
|
||||
};
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"description": "This is an authorised multilingual game system En|Fr|Es, for Legend of the Five Rings (5th Edition) by <a href='https://edge-studio.net/'>Edge Studio</a> <p> - Join the official Discord server: <a href='https://discord.gg/foundryvtt'> Official Discord</a></p><p> - Rejoignez la communauté Francophone: <a href='https://discord.gg/pPSDNJk'>Francophone Discord</a></p>",
|
||||
"url": "https://gitlab.com/teaml5r/l5r5e",
|
||||
"manifest": "https://gitlab.com/teaml5r/l5r5e/-/raw/master/system/system.json",
|
||||
"download": "https://gitlab.com/teaml5r/l5r5e/-/jobs/artifacts/v1.3.5/raw/l5r5e.zip?job=build",
|
||||
"version": "1.3.5",
|
||||
"download": "https://gitlab.com/teaml5r/l5r5e/-/jobs/artifacts/v1.4.0/raw/l5r5e.zip?job=build",
|
||||
"version": "1.4.0",
|
||||
"minimumCoreVersion": "0.8.5",
|
||||
"compatibleCoreVersion": "0.8.9",
|
||||
"manifestPlusVersion": "1.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"Actor": {
|
||||
"types": ["character", "npc"],
|
||||
"types": ["character", "npc", "army"],
|
||||
"templates": {
|
||||
"identity": {
|
||||
"identity": {
|
||||
@@ -141,6 +141,33 @@
|
||||
"social": 0,
|
||||
"trade": 0
|
||||
}
|
||||
},
|
||||
"army": {
|
||||
"warlord": "",
|
||||
"allies_backers": "",
|
||||
"purpose_mustering": "",
|
||||
"battle_readiness": {
|
||||
"casualties_strength": {
|
||||
"max": 0,
|
||||
"value": 0
|
||||
},
|
||||
"panic_discipline": {
|
||||
"max": 0,
|
||||
"value": 0
|
||||
}
|
||||
},
|
||||
"commander": "",
|
||||
"commander_abilities": "",
|
||||
"army_abilities": "",
|
||||
"commander_standing": {
|
||||
"honor": 0,
|
||||
"glory": 0,
|
||||
"status": 0
|
||||
},
|
||||
"supplies_logistics": "",
|
||||
"notes": "",
|
||||
"description": "",
|
||||
"past_battles": ""
|
||||
}
|
||||
},
|
||||
"Item": {
|
||||
@@ -155,7 +182,9 @@
|
||||
"title",
|
||||
"bond",
|
||||
"signature_scroll",
|
||||
"item_pattern"
|
||||
"item_pattern",
|
||||
"army_cohort",
|
||||
"army_fortification"
|
||||
],
|
||||
"templates": {
|
||||
"basics": {
|
||||
@@ -236,6 +265,28 @@
|
||||
},
|
||||
"signature_scroll": {
|
||||
"templates": ["basics", "advancement"]
|
||||
},
|
||||
"army_cohort": {
|
||||
"templates": ["basics"],
|
||||
"leader": "",
|
||||
"equipment": "",
|
||||
"abilities": "",
|
||||
"battle_readiness": {
|
||||
"casualties_strength": {
|
||||
"max": 0,
|
||||
"value": 0
|
||||
},
|
||||
"panic_discipline": {
|
||||
"max": 0,
|
||||
"value": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"army_fortification": {
|
||||
"templates": ["basics"],
|
||||
"difficulty": "",
|
||||
"attrition_reduction": "",
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
99
system/templates/actors/army-sheet.html
Normal file
99
system/templates/actors/army-sheet.html
Normal file
@@ -0,0 +1,99 @@
|
||||
<form class="{{cssClass}}" data-lang="{{localize 'I18N.Language'}}" autocomplete="off">
|
||||
{{!-- Sheet Header --}}
|
||||
<header class="sheet-header">
|
||||
<img class="profile-img" src="{{data.img}}" data-edit="img" title="{{data.name}}"/>
|
||||
<div class="header-fields identity-wrapper">
|
||||
<h1 class="charname"><input name="name" type="text" value="{{data.name}}" placeholder="Name"/></h1>
|
||||
</div>
|
||||
<div class="header-fields">
|
||||
<div class="warlord">
|
||||
<p>
|
||||
{{localize 'l5r5e.army.warlord'}}
|
||||
<input name="data.warlord" type="text" value="{{data.data.warlord}}" />
|
||||
</p>
|
||||
<p>
|
||||
{{localize 'l5r5e.army.allies_backers'}}
|
||||
<textarea type="text" name="data.allies_backers">{{data.data.allies_backers}}</textarea>
|
||||
</p>
|
||||
<p>
|
||||
{{localize 'l5r5e.army.purpose_mustering'}}
|
||||
<textarea type="text" name="data.purpose_mustering">{{data.data.purpose_mustering}}</textarea>
|
||||
</p>
|
||||
|
||||
<div class="attributes-wrapper">
|
||||
{{localize 'l5r5e.army.battle_readiness.title'}}
|
||||
<p>
|
||||
{{localize 'l5r5e.army.battle_readiness.strength'}}
|
||||
<input name="data.battle_readiness.casualties_strength.max" type="text" value="{{data.data.battle_readiness.casualties_strength.max}}" />
|
||||
</p>
|
||||
<p>
|
||||
{{localize 'l5r5e.army.battle_readiness.casualties'}}
|
||||
<input name="data.battle_readiness.casualties_strength.value" type="text" value="{{data.data.battle_readiness.casualties_strength.value}}" />
|
||||
</p>
|
||||
<p>
|
||||
{{localize 'l5r5e.army.battle_readiness.discipline'}}
|
||||
<input name="data.battle_readiness.panic_discipline.max" type="text" value="{{data.data.battle_readiness.panic_discipline.max}}" />
|
||||
</p>
|
||||
<p>
|
||||
{{localize 'l5r5e.army.battle_readiness.panic'}}
|
||||
<input name="data.battle_readiness.panic_discipline.value" type="text" value="{{data.data.battle_readiness.panic_discipline.value}}" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="commander">
|
||||
<p>
|
||||
{{localize 'l5r5e.army.commander'}}
|
||||
<input name="data.commander" type="text" value="{{data.data.commander}}" />
|
||||
</p>
|
||||
<p>
|
||||
{{localize 'l5r5e.army.commander_abilities'}}
|
||||
<textarea type="text" name="data.commander_abilities">{{data.data.commander_abilities}}</textarea>
|
||||
</p>
|
||||
<p>
|
||||
{{localize 'l5r5e.army.army_abilities'}}
|
||||
<textarea type="text" name="data.army_abilities">{{data.data.army_abilities}}</textarea>
|
||||
</p>
|
||||
|
||||
<div class="attributes-wrapper">
|
||||
{{localize 'l5r5e.army.commander_standing'}}
|
||||
<p>
|
||||
{{localize 'l5r5e.social.honor'}}
|
||||
<input name="data.commander_standing.honor" type="text" value="{{data.data.commander_standing.honor}}" />
|
||||
</p>
|
||||
<p>
|
||||
{{localize 'l5r5e.social.glory'}}
|
||||
<input name="data.commander_standing.glory" type="text" value="{{data.data.commander_standing.glory}}" />
|
||||
</p>
|
||||
<p>
|
||||
{{localize 'l5r5e.social.status'}}
|
||||
<input name="data.commander_standing.status" type="text" value="{{data.data.commander_standing.status}}" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{{!-- Sheet Body --}}
|
||||
<section class="sheet-body">
|
||||
{{!-- Sheet Tab Navigation --}}
|
||||
<nav class="sheet-tabs tabs" data-group="primary">
|
||||
<a class="item" data-tab="cohort">{{localize 'l5r5e.army.cohort.title'}}</a>
|
||||
<a class="item" data-tab="fortification">{{localize 'l5r5e.army.fortification.title'}}</a>
|
||||
<a class="item" data-tab="others">{{localize 'l5r5e.notes'}}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- Cohort Tab --}}
|
||||
<article class="tab cohort" data-group="primary" data-tab="cohort">
|
||||
{{> 'systems/l5r5e/templates/actors/army/cohort.html'}}
|
||||
</article>
|
||||
|
||||
{{!-- Fortification Tab --}}
|
||||
<article class="tab fortification" data-group="primary" data-tab="fortification">
|
||||
{{> 'systems/l5r5e/templates/actors/army/fortification.html'}}
|
||||
</article>
|
||||
|
||||
{{!-- Others Tab --}}
|
||||
<article class="tab others" data-group="primary" data-tab="others">
|
||||
{{> 'systems/l5r5e/templates/actors/army/others.html'}}
|
||||
</article>
|
||||
</section>
|
||||
</form>
|
||||
13
system/templates/actors/army/cohort.html
Normal file
13
system/templates/actors/army/cohort.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<fieldset class="cohort-content">
|
||||
<legend class="section-header">
|
||||
{{localize 'l5r5e.army.cohort.title'}}
|
||||
{{#if options.editable}}
|
||||
<a data-item-type="army_cohort" class="army-cohort-control item-add" title="{{localize 'l5r5e.global.add'}}"><i class="fas fa-plus"></i></a>
|
||||
{{/if}}
|
||||
</legend>
|
||||
<ul class="item-list">
|
||||
{{#each data.splitItemsList.army_cohort as |item|}}
|
||||
{{> 'systems/l5r5e/templates/items/army-cohort/army-cohort-entry.html' cohort=item editable=../options.editable}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
</fieldset>
|
||||
13
system/templates/actors/army/fortification.html
Normal file
13
system/templates/actors/army/fortification.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<fieldset class="fortification-content">
|
||||
<legend class="section-header">
|
||||
{{localize 'l5r5e.army.fortification.title'}}
|
||||
{{#if options.editable}}
|
||||
<a data-item-type="army_fortification" class="army-fortification-control item-add" title="{{localize 'l5r5e.global.add'}}"><i class="fas fa-plus"></i></a>
|
||||
{{/if}}
|
||||
</legend>
|
||||
<ul class="item-list">
|
||||
{{#each data.splitItemsList.army_fortification as |item|}}
|
||||
{{> 'systems/l5r5e/templates/items/army-fortification/army-fortification-entry.html' fortification=item editable=../options.editable}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
</fieldset>
|
||||
25
system/templates/actors/army/others.html
Normal file
25
system/templates/actors/army/others.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<div>
|
||||
{{!-- Supplies and Logistics --}}
|
||||
<fieldset class="supplies_logistics">
|
||||
<legend class="text-block-header">{{localize 'l5r5e.army.supplies_logistics'}}</legend>
|
||||
{{editor content=data.data.supplies_logistics target="data.supplies_logistics" button=true editable=options.editable}}
|
||||
</fieldset>
|
||||
|
||||
{{!-- Past Battles --}}
|
||||
<fieldset class="past_battles">
|
||||
<legend class="text-block-header">{{localize 'l5r5e.army.past_battles'}}</legend>
|
||||
{{editor content=data.data.past_battles target="data.past_battles" button=true editable=options.editable}}
|
||||
</fieldset>
|
||||
|
||||
{{!-- Description (public) --}}
|
||||
<fieldset class="description">
|
||||
<legend class="text-block-header">{{localize 'l5r5e.description'}}</legend>
|
||||
{{editor content=data.data.description target="data.description" button=true editable=options.editable}}
|
||||
</fieldset>
|
||||
|
||||
{{!-- Notes (private) --}}
|
||||
<fieldset class="note">
|
||||
<legend class="text-block-header">{{localize 'l5r5e.notes'}}</legend>
|
||||
{{editor content=data.data.notes target="data.notes" button=true editable=options.editable}}
|
||||
</fieldset>
|
||||
</div>
|
||||
22
system/templates/items/army-cohort/army-cohort-entry.html
Normal file
22
system/templates/items/army-cohort/army-cohort-entry.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<li class="item cohort flexcol">
|
||||
<ul class="item-header item-control">
|
||||
<li class="item-img"><img src="{{cohort.img}}" title="{{cohort.name}}" width="32px" height="32px"/></li>
|
||||
<li class="item-name">{{cohort.name}}</li>
|
||||
<li class="icon-stat-container">
|
||||
<i class="fas fa-skull" title="{{localize 'l5r5e.army.battle_readiness.casualties'}}"> {{cohort.data.battle_readiness.casualties_strength.value}}</i>
|
||||
<i class="fas fa-tint" title="{{localize 'l5r5e.army.battle_readiness.strength'}}"> {{cohort.data.battle_readiness.casualties_strength.max}}</i>
|
||||
<i class="fas fa-skull" title="{{localize 'l5r5e.army.battle_readiness.panic'}}"> {{cohort.data.battle_readiness.panic_discipline.value}}</i>
|
||||
<i class="fas fa-tint" title="{{localize 'l5r5e.army.battle_readiness.discipline'}}"> {{cohort.data.battle_readiness.panic_discipline.max}}</i>
|
||||
</li>
|
||||
{{#if editable}}
|
||||
<li data-item-id="{{cohort._id}}" class="item-edit" title="{{localize 'l5r5e.global.edit'}}"><i class="fas fa-edit"></i></li>
|
||||
<li data-item-id="{{cohort._id}}" class="item-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
<ul class="item-properties">
|
||||
<li>{{localize 'l5r5e.army.cohort.leader'}} : {{cohort.data.leader}}</li>
|
||||
<li>{{localize 'l5r5e.equipment'}} : {{cohort.data.equipment}}</li>
|
||||
<li>{{localize 'l5r5e.army.cohort.abilities'}} : {{cohort.data.abilities}}</li>
|
||||
<li>{{localize 'l5r5e.description'}} : {{cohort.data.description}}</li>
|
||||
</ul>
|
||||
</li>
|
||||
43
system/templates/items/army-cohort/army-cohort-sheet.html
Normal file
43
system/templates/items/army-cohort/army-cohort-sheet.html
Normal file
@@ -0,0 +1,43 @@
|
||||
<form class="{{cssClass}}" autocomplete="off">
|
||||
<header class="sheet-header">
|
||||
<img class="profile-img" src="{{data.img}}" data-edit="img" title="{{data.name}}"/>
|
||||
<h1 class="charname"><input name="name" type="text" value="{{data.name}}" placeholder="Name"/></h1>
|
||||
</header>
|
||||
{{!-- Sheet Body --}}
|
||||
<section class="sheet-body">
|
||||
{{!-- properties Tab --}}
|
||||
<article class="attributes" data-group="primary" data-tab="description">
|
||||
<label class="attribute army-cohort-types">
|
||||
{{localize 'l5r5e.army.cohort.leader'}}
|
||||
<input class="select-on-focus" type="number" name="data.leader" value="{{data.data.leader}}" data-dtype="Number"/>
|
||||
</label>
|
||||
<label class="attribute army-cohort-types">
|
||||
{{localize 'l5r5e.equipment'}}
|
||||
<input class="select-on-focus" type="number" name="data.equipment" value="{{data.data.equipment}}" data-dtype="Number"/>
|
||||
</label>
|
||||
<label class="attribute army-cohort-types">
|
||||
{{localize 'l5r5e.army.cohort.abilities'}}
|
||||
<input class="select-on-focus" type="number" name="data.abilities" value="{{data.data.abilities}}" data-dtype="Number"/>
|
||||
</label>
|
||||
|
||||
{{!-- battle readiness --}}
|
||||
<label class="attribute army-cohort-types">
|
||||
{{localize 'l5r5e.army.battle_readiness.strength'}}
|
||||
<input class="select-on-focus" type="number" name="data.battle_readiness.casualties_strength.max" value="{{data.data.battle_readiness.casualties_strength.max}}" data-dtype="Number"/>
|
||||
</label>
|
||||
<label class="attribute army-cohort-types">
|
||||
{{localize 'l5r5e.army.battle_readiness.casualties'}}
|
||||
<input class="select-on-focus" type="number" name="data.battle_readiness.casualties_strength.value" value="{{data.data.battle_readiness.casualties_strength.value}}" data-dtype="Number"/>
|
||||
</label>
|
||||
<label class="attribute army-cohort-types">
|
||||
{{localize 'l5r5e.army.battle_readiness.discipline'}}
|
||||
<input class="select-on-focus" type="number" name="data.battle_readiness.panic_discipline.max" value="{{data.data.battle_readiness.panic_discipline.max}}" data-dtype="Number"/>
|
||||
</label>
|
||||
<label class="attribute army-cohort-types">
|
||||
{{localize 'l5r5e.army.battle_readiness.panic'}}
|
||||
<input class="select-on-focus" type="number" name="data.battle_readiness.panic_discipline.value" value="{{data.data.battle_readiness.panic_discipline.value}}" data-dtype="Number"/>
|
||||
</label>
|
||||
</article>
|
||||
{{> 'systems/l5r5e/templates/items/item/item-infos.html'}}
|
||||
</section>
|
||||
</form>
|
||||
@@ -0,0 +1,17 @@
|
||||
<li class="item fortification flexcol">
|
||||
<ul class="item-header item-control">
|
||||
<li class="item-img"><img src="{{fortification.img}}" title="{{fortification.name}}" width="32px" height="32px"/></li>
|
||||
<li class="item-name">{{fortification.name}}</li>
|
||||
<li class="icon-stat-container">
|
||||
<i class="fas fa-skull" title="{{localize 'l5r5e.army.fortification.difficulty'}}"> {{fortification.data.difficulty}}</i>
|
||||
<i class="fas fa-tint" title="{{localize 'l5r5e.army.fortification.attrition_reduction'}}"> {{fortification.data.attrition_reduction}}</i>
|
||||
</li>
|
||||
{{#if editable}}
|
||||
<li data-item-id="{{fortification._id}}" class="item-edit" title="{{localize 'l5r5e.global.edit'}}"><i class="fas fa-edit"></i></li>
|
||||
<li data-item-id="{{fortification._id}}" class="item-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
<ul class="item-properties">
|
||||
<li>{{localize 'l5r5e.description'}} : {{fortification.data.description}}</li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -0,0 +1,22 @@
|
||||
<form class="{{cssClass}}" autocomplete="off">
|
||||
<header class="sheet-header">
|
||||
<img class="profile-img" src="{{data.img}}" data-edit="img" title="{{data.name}}"/>
|
||||
<h1 class="charname"><input name="name" type="text" value="{{data.name}}" placeholder="Name"/></h1>
|
||||
</header>
|
||||
{{!-- Sheet Body --}}
|
||||
<section class="sheet-body">
|
||||
{{!-- properties Tab --}}
|
||||
<article class="attributes" data-group="primary" data-tab="description">
|
||||
<label class="attribute army-fortification-types">
|
||||
{{localize 'l5r5e.army.fortification.difficulty'}}
|
||||
<input class="select-on-focus" type="number" name="data.difficulty" value="{{data.data.difficulty}}" data-dtype="Number"/>
|
||||
</label>
|
||||
|
||||
<label class="attribute army-fortification-types">
|
||||
{{localize 'l5r5e.army.fortification.attrition_reduction'}}
|
||||
<input class="select-on-focus" type="number" name="data.attrition_reduction" value="{{data.data.attrition_reduction}}" data-dtype="Number"/>
|
||||
</label>
|
||||
</article>
|
||||
{{> 'systems/l5r5e/templates/items/item/item-infos.html'}}
|
||||
</section>
|
||||
</form>
|
||||
Reference in New Issue
Block a user