Init
This commit is contained in:
parent
3d8054c632
commit
5c6f9bc9a5
280
modules/mournblade-actor-sheet.js
Normal file
280
modules/mournblade-actor-sheet.js
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
/**
|
||||||
|
* Extend the basic ActorSheet with some very simple modifications
|
||||||
|
* @extends {ActorSheet}
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { MournbladeUtility } from "./mournblade-utility.js";
|
||||||
|
import { MournbladeRollDialog } from "./Mournblade-roll-dialog.js";
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
export class MournbladeActorSheet extends ActorSheet {
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static get defaultOptions() {
|
||||||
|
|
||||||
|
return mergeObject(super.defaultOptions, {
|
||||||
|
classes: ["fvtt-mournblade", "sheet", "actor"],
|
||||||
|
template: "systems/fvtt-mournblade/templates/actor-sheet.html",
|
||||||
|
width: 640,
|
||||||
|
height: 720,
|
||||||
|
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }],
|
||||||
|
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
||||||
|
editScore: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async getData() {
|
||||||
|
const objectData = MournbladeUtility.data(this.object);
|
||||||
|
|
||||||
|
let actorData = duplicate(MournbladeUtility.templateData(this.object));
|
||||||
|
|
||||||
|
let formData = {
|
||||||
|
title: this.title,
|
||||||
|
id: objectData.id,
|
||||||
|
type: objectData.type,
|
||||||
|
img: objectData.img,
|
||||||
|
name: objectData.name,
|
||||||
|
editable: this.isEditable,
|
||||||
|
cssClass: this.isEditable ? "editable" : "locked",
|
||||||
|
data: actorData,
|
||||||
|
effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)),
|
||||||
|
limited: this.object.limited,
|
||||||
|
weapons: this.actor.checkAndPrepareWeapons( duplicate(this.actor.getWeapons()) ),
|
||||||
|
armors: this.actor.checkAndPrepareArmors( duplicate(this.actor.getArmors())),
|
||||||
|
shields: duplicate(this.actor.getShields()),
|
||||||
|
equipments: duplicate(this.actor.getEquipments()),
|
||||||
|
options: this.options,
|
||||||
|
owner: this.document.isOwner,
|
||||||
|
editScore: this.options.editScore,
|
||||||
|
isGM: game.user.isGM
|
||||||
|
}
|
||||||
|
this.formData = formData;
|
||||||
|
|
||||||
|
console.log("PC : ", formData, this.object);
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/** @override */
|
||||||
|
activateListeners(html) {
|
||||||
|
super.activateListeners(html);
|
||||||
|
|
||||||
|
// Everything below here is only needed if the sheet is editable
|
||||||
|
if (!this.options.editable) return;
|
||||||
|
|
||||||
|
html.bind("keydown", function(e) { // Ignore Enter in actores sheet
|
||||||
|
if (e.keyCode === 13) return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update Inventory Item
|
||||||
|
html.find('.item-edit').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
let itemId = li.data("item-id");
|
||||||
|
const item = this.actor.items.get( itemId );
|
||||||
|
item.sheet.render(true);
|
||||||
|
});
|
||||||
|
// Delete Inventory Item
|
||||||
|
html.find('.item-delete').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
MournbladeUtility.confirmDelete(this, li);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.spec-group-activate').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
let itemId = li.data("item-id");
|
||||||
|
this.actor.specPowerActivate( itemId)
|
||||||
|
});
|
||||||
|
html.find('.spec-group-deactivate').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
let itemId = li.data("item-id");
|
||||||
|
this.actor.specPowerDeactivate( itemId)
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.equip-activate').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item")
|
||||||
|
let itemId = li.data("item-id")
|
||||||
|
this.actor.equipActivate( itemId)
|
||||||
|
});
|
||||||
|
html.find('.equip-deactivate').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item")
|
||||||
|
let itemId = li.data("item-id")
|
||||||
|
this.actor.equipDeactivate( itemId)
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.effect-used').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
let itemId = li.data("item-id");
|
||||||
|
this.actor.perkEffectUsed( itemId)
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.perk-status').change(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
let itemId = li.data("item-id");
|
||||||
|
this.actor.updatePerkStatus( itemId, ev.currentTarget.value)
|
||||||
|
});
|
||||||
|
html.find('.power-cost-spent').change(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
let itemId = li.data("item-id");
|
||||||
|
this.actor.updatePowerSpentCost( itemId, ev.currentTarget.value)
|
||||||
|
});
|
||||||
|
html.find('.perk-used').change(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item")
|
||||||
|
let itemId = li.data("item-id")
|
||||||
|
let index = Number($(ev.currentTarget).data("use-index") )
|
||||||
|
this.actor.updatePerkUsed( itemId, index, ev.currentTarget.checked )
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.subactor-edit').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
let actorId = li.data("actor-id");
|
||||||
|
let actor = game.actors.get( actorId );
|
||||||
|
actor.sheet.render(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.subactor-delete').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
let actorId = li.data("actor-id");
|
||||||
|
this.actor.delSubActor(actorId);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.quantity-minus').click(event => {
|
||||||
|
const li = $(event.currentTarget).parents(".item");
|
||||||
|
this.actor.incDecQuantity( li.data("item-id"), -1 );
|
||||||
|
} );
|
||||||
|
html.find('.quantity-plus').click(event => {
|
||||||
|
const li = $(event.currentTarget).parents(".item");
|
||||||
|
this.actor.incDecQuantity( li.data("item-id"), +1 );
|
||||||
|
} );
|
||||||
|
|
||||||
|
html.find('.unarmed-attack').click((event) => {
|
||||||
|
this.actor.rollUnarmedAttack();
|
||||||
|
});
|
||||||
|
html.find('.generic-pool-roll').click((event) => {
|
||||||
|
this.openGenericRoll()
|
||||||
|
} );
|
||||||
|
html.find('.attack-melee').click((event) => {
|
||||||
|
this.actor.rollPool( 'com');
|
||||||
|
});
|
||||||
|
html.find('.attack-ranged').click((event) => {
|
||||||
|
this.actor.rollPool( 'agi');
|
||||||
|
});
|
||||||
|
html.find('.defense-roll').click((event) => {
|
||||||
|
this.actor.rollPool( 'def', true);
|
||||||
|
});
|
||||||
|
html.find('.damage-melee').click((event) => {
|
||||||
|
this.actor.rollPool( 'str');
|
||||||
|
});
|
||||||
|
html.find('.damage-ranged').click((event) => {
|
||||||
|
this.actor.rollPool( 'per');
|
||||||
|
});
|
||||||
|
html.find('.damage-resistance').click((event) => {
|
||||||
|
this.actor.rollPool( 'phy');
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.roll-stat').click((event) => {
|
||||||
|
const statId = $(event.currentTarget).data("stat-key");
|
||||||
|
this.actor.rollStat(statId);
|
||||||
|
});
|
||||||
|
html.find('.roll-mr').click((event) => {
|
||||||
|
this.actor.rollMR();
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.roll-spec').click((event) => {
|
||||||
|
const li = $(event.currentTarget).parents(".item");
|
||||||
|
const specId = li.data("item-id");
|
||||||
|
this.actor.rollSpec(specId);
|
||||||
|
});
|
||||||
|
html.find('.power-roll').click((event) => {
|
||||||
|
const li = $(event.currentTarget).parents(".item");
|
||||||
|
const powerId = li.data("item-id");
|
||||||
|
this.actor.rollPower(powerId);
|
||||||
|
});
|
||||||
|
html.find('.weapon-roll').click((event) => {
|
||||||
|
const li = $(event.currentTarget).parents(".item");
|
||||||
|
const weaponId = li.data("item-id");
|
||||||
|
this.actor.rollWeapon(weaponId);
|
||||||
|
});
|
||||||
|
html.find('.armor-roll').click((event) => {
|
||||||
|
const li = $(event.currentTarget).parents(".item");
|
||||||
|
const armorId = li.data("item-id");
|
||||||
|
this.actor.rollArmor(armorId);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.weapon-damage-roll').click((event) => {
|
||||||
|
const li = $(event.currentTarget).parents(".item");
|
||||||
|
const weaponId = li.data("item-id");
|
||||||
|
this.actor.rollWeapon(weaponId, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.weapon-damage').click((event) => {
|
||||||
|
const li = $(event.currentTarget).parents(".item");
|
||||||
|
const weapon = this.actor.getOwnedItem(li.data("item-id"));
|
||||||
|
this.actor.rollDamage(weapon, 'damage');
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.lock-unlock-sheet').click((event) => {
|
||||||
|
this.options.editScore = !this.options.editScore;
|
||||||
|
this.render(true);
|
||||||
|
});
|
||||||
|
html.find('.item-link a').click((event) => {
|
||||||
|
const itemId = $(event.currentTarget).data("item-id");
|
||||||
|
const item = this.actor.getOwnedItem(itemId);
|
||||||
|
item.sheet.render(true);
|
||||||
|
});
|
||||||
|
html.find('.item-equip').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
this.actor.equipItem( li.data("item-id") );
|
||||||
|
this.render(true);
|
||||||
|
});
|
||||||
|
html.find('.power-activate').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
this.actor.activatePower( li.data("item-id") );
|
||||||
|
this.render(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.change-worstfear').change(ev => {
|
||||||
|
this.actor.manageWorstFear( ev.currentTarget.checked )
|
||||||
|
});
|
||||||
|
html.find('.change-desires').change(ev => {
|
||||||
|
this.actor.manageDesires( ev.currentTarget.checked )
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.update-field').change(ev => {
|
||||||
|
const fieldName = $(ev.currentTarget).data("field-name");
|
||||||
|
let value = Number(ev.currentTarget.value);
|
||||||
|
this.actor.update( { [`${fieldName}`]: value } );
|
||||||
|
});
|
||||||
|
html.find('.perk-active').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
this.actor.activatePerk( li.data("item-id") );
|
||||||
|
this.render(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/** @override */
|
||||||
|
setPosition(options = {}) {
|
||||||
|
const position = super.setPosition(options);
|
||||||
|
const sheetBody = this.element.find(".sheet-body");
|
||||||
|
const bodyHeight = position.height - 192;
|
||||||
|
sheetBody.css("height", bodyHeight);
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async _onDropItem(event, dragData) {
|
||||||
|
let item = await MournbladeUtility.searchItem( dragData)
|
||||||
|
this.actor.preprocessItem( event, item, true )
|
||||||
|
super._onDropItem(event, dragData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/** @override */
|
||||||
|
_updateObject(event, formData) {
|
||||||
|
// Update the Actor
|
||||||
|
return this.object.update(formData);
|
||||||
|
}
|
||||||
|
}
|
189
modules/mournblade-actor.js
Normal file
189
modules/mournblade-actor.js
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
/* -------------------------------------------- */
|
||||||
|
import { MournbladeUtility } from "./mournblade-utility.js";
|
||||||
|
import { MournbladeRollDialog } from "./mournblade-roll-dialog.js";
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
|
||||||
|
* @extends {Actor}
|
||||||
|
*/
|
||||||
|
export class MournbladeActor extends Actor {
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Override the create() function to provide additional SoS functionality.
|
||||||
|
*
|
||||||
|
* This overrided create() function adds initial items
|
||||||
|
* Namely: Basic skills, money,
|
||||||
|
*
|
||||||
|
* @param {Object} data Barebones actor data which this function adds onto.
|
||||||
|
* @param {Object} options (Unused) Additional options which customize the creation workflow.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
|
||||||
|
// Case of compendium global import
|
||||||
|
if (data instanceof Array) {
|
||||||
|
return super.create(data, options);
|
||||||
|
}
|
||||||
|
// If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic
|
||||||
|
if (data.items) {
|
||||||
|
let actor = super.create(data, options);
|
||||||
|
return actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.type == 'character') {
|
||||||
|
const skills = await MournbladeUtility.loadCompendium("fvtt-mournblade.skills")
|
||||||
|
data.items = skills.map(i => i.toObject());
|
||||||
|
}
|
||||||
|
if (data.type == 'npc') {
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.create(data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
prepareBaseData() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async prepareData() {
|
||||||
|
super.prepareData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
prepareDerivedData() {
|
||||||
|
|
||||||
|
if (this.type == 'character') {
|
||||||
|
}
|
||||||
|
|
||||||
|
super.prepareDerivedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
_preUpdate(changed, options, user) {
|
||||||
|
|
||||||
|
super._preUpdate(changed, options, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
getActivePerks() {
|
||||||
|
let perks = this.data.items.filter(item => item.type == 'perk' && item.data.data.active);
|
||||||
|
return perks;
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
getItemById(id) {
|
||||||
|
let item = this.data.items.find(item => item.id == id);
|
||||||
|
if (item) {
|
||||||
|
item = duplicate(item)
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async equipItem(itemId) {
|
||||||
|
let item = this.data.items.find(item => item.id == itemId);
|
||||||
|
if (item && item.data.data) {
|
||||||
|
let update = { _id: item.id, "data.equipped": !item.data.data.equipped };
|
||||||
|
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
compareName(a, b) {
|
||||||
|
if (a.name < b.name) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.name > b.name) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
getAttribute(attrKey) {
|
||||||
|
return this.data.data.attributes[attrKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async equipGear(equipmentId) {
|
||||||
|
let item = this.data.items.find(item => item.id == equipmentId);
|
||||||
|
if (item && item.data.data) {
|
||||||
|
let update = { _id: item.id, "data.equipped": !item.data.data.equipped };
|
||||||
|
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
getSubActors() {
|
||||||
|
let subActors = [];
|
||||||
|
for (let id of this.data.data.subactors) {
|
||||||
|
subActors.push(duplicate(game.actors.get(id)));
|
||||||
|
}
|
||||||
|
return subActors;
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addSubActor(subActorId) {
|
||||||
|
let subActors = duplicate(this.data.data.subactors);
|
||||||
|
subActors.push(subActorId);
|
||||||
|
await this.update({ 'data.subactors': subActors });
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async delSubActor(subActorId) {
|
||||||
|
let newArray = [];
|
||||||
|
for (let id of this.data.data.subactors) {
|
||||||
|
if (id != subActorId) {
|
||||||
|
newArray.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.update({ 'data.subactors': newArray });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async incDecQuantity(objetId, incDec = 0) {
|
||||||
|
let objetQ = this.data.items.get(objetId)
|
||||||
|
if (objetQ) {
|
||||||
|
let newQ = objetQ.data.data.quantity + incDec;
|
||||||
|
const updated = await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'data.quantity': newQ }]); // pdates one EmbeddedEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
getCommonRollData(statKey = undefined, useShield = false) {
|
||||||
|
let rollData = MournbladeUtility.getBasicRollData()
|
||||||
|
rollData.alias = this.name
|
||||||
|
rollData.actorImg = this.img
|
||||||
|
rollData.actorId = this.id
|
||||||
|
rollData.img = this.img
|
||||||
|
rollData.activePerks = duplicate(this.getActivePerks())
|
||||||
|
|
||||||
|
if (statKey) {
|
||||||
|
rollData.statKey = statKey
|
||||||
|
rollData.stat = this.getStat(statKey)
|
||||||
|
rollData.statDicesLevel = rollData.stat.value
|
||||||
|
rollData.statMod = rollData.stat.mod
|
||||||
|
rollData.specList = this.getRelevantSpec(statKey)
|
||||||
|
rollData.selectedSpec = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addEffects(rollData)
|
||||||
|
this.addArmorsShields(rollData, statKey, useShield)
|
||||||
|
this.addWeapons(rollData, statKey, useShield)
|
||||||
|
this.addEquipments(rollData, statKey)
|
||||||
|
|
||||||
|
return rollData
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async startRoll(rollData) {
|
||||||
|
this.syncRoll(rollData);
|
||||||
|
//console.log("ROLL DATA", rollData)
|
||||||
|
let rollDialog = await MournbladeRollDialog.create(this, rollData);
|
||||||
|
console.log(rollDialog);
|
||||||
|
rollDialog.render(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
modules/mournblade-combat.js
Normal file
24
modules/mournblade-combat.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { MournbladeUtility } from "./mournblade-utility.js";
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
export class MournbladeCombat extends Combat {
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async rollInitiative(ids, formula = undefined, messageOptions = {} ) {
|
||||||
|
ids = typeof ids === "string" ? [ids] : ids;
|
||||||
|
for (let cId = 0; cId < ids.length; cId++) {
|
||||||
|
const c = this.combatants.get(ids[cId]);
|
||||||
|
let id = c._id || c.id;
|
||||||
|
let initBonus = c.actor ? c.actor.getInitiativeScore( this.id, id ) : -1;
|
||||||
|
await this.updateEmbeddedDocuments("Combatant", [ { _id: id, initiative: initBonus } ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
_onUpdate(changed, options, userId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
124
modules/mournblade-commands.js
Normal file
124
modules/mournblade-commands.js
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
import { MournbladeActorCreate } from "./mournblade-create-char.js";
|
||||||
|
import { MournbladeUtility } from "./mournblade-utility.js";
|
||||||
|
import { MournbladeRollDialog } from "./mournblade-roll-dialog.js";
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
export class MournbladeCommands {
|
||||||
|
|
||||||
|
static init() {
|
||||||
|
if (!game.system.Mournblade.commands) {
|
||||||
|
const MournbladeCommands = new MournbladeCommands();
|
||||||
|
MournbladeCommands.registerCommand({ path: ["/char"], func: (content, msg, params) => MournbladeCommands.createChar(msg), descr: "Create a new character" });
|
||||||
|
MournbladeCommands.registerCommand({ path: ["/pool"], func: (content, msg, params) => MournbladeCommands.poolRoll(msg), descr: "Generic Roll Window" });
|
||||||
|
game.system.Mournblade.commands = MournbladeCommands;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constructor() {
|
||||||
|
this.commandsTable = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
registerCommand(command) {
|
||||||
|
this._addCommand(this.commandsTable, command.path, '', command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
_addCommand(targetTable, path, fullPath, command) {
|
||||||
|
if (!this._validateCommand(targetTable, path, command)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const term = path[0];
|
||||||
|
fullPath = fullPath + term + ' '
|
||||||
|
if (path.length == 1) {
|
||||||
|
command.descr = `<strong>${fullPath}</strong>: ${command.descr}`;
|
||||||
|
targetTable[term] = command;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!targetTable[term]) {
|
||||||
|
targetTable[term] = { subTable: {} };
|
||||||
|
}
|
||||||
|
this._addCommand(targetTable[term].subTable, path.slice(1), fullPath, command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
_validateCommand(targetTable, path, command) {
|
||||||
|
if (path.length > 0 && path[0] && command.descr && (path.length != 1 || targetTable[path[0]] == undefined)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
console.warn("MournbladeCommands._validateCommand failed ", targetTable, path, command);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Manage chat commands */
|
||||||
|
processChatCommand(commandLine, content = '', msg = {}) {
|
||||||
|
// Setup new message's visibility
|
||||||
|
let rollMode = game.settings.get("core", "rollMode");
|
||||||
|
if (["gmroll", "blindroll"].includes(rollMode)) msg["whisper"] = ChatMessage.getWhisperRecipients("GM");
|
||||||
|
if (rollMode === "blindroll") msg["blind"] = true;
|
||||||
|
msg["type"] = 0;
|
||||||
|
|
||||||
|
let command = commandLine[0].toLowerCase();
|
||||||
|
let params = commandLine.slice(1);
|
||||||
|
|
||||||
|
return this.process(command, params, content, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
process(command, params, content, msg) {
|
||||||
|
return this._processCommand(this.commandsTable, command, params, content, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
_processCommand(commandsTable, name, params, content = '', msg = {}, path = "") {
|
||||||
|
console.log("===> Processing command")
|
||||||
|
let command = commandsTable[name];
|
||||||
|
path = path + name + " ";
|
||||||
|
if (command && command.subTable) {
|
||||||
|
if (params[0]) {
|
||||||
|
return this._processCommand(command.subTable, params[0], params.slice(1), content, msg, path)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.help(msg, command.subTable);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (command && command.func) {
|
||||||
|
const result = command.func(content, msg, params);
|
||||||
|
if (result == false) {
|
||||||
|
RdDCommands._chatAnswer(msg, command.descr);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async createChar(msg) {
|
||||||
|
game.system.Mournblade.creator = new MournbladeActorCreate();
|
||||||
|
game.system.Mournblade.creator.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static _chatAnswer(msg, content) {
|
||||||
|
msg.whisper = [game.user.id];
|
||||||
|
msg.content = content;
|
||||||
|
ChatMessage.create(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async poolRoll( msg) {
|
||||||
|
let rollData = MournbladeUtility.getBasicRollData()
|
||||||
|
rollData.alias = "Dice Pool Roll",
|
||||||
|
rollData.mode = "generic"
|
||||||
|
rollData.title = `Dice Pool Roll`;
|
||||||
|
|
||||||
|
let rollDialog = await MournbladeRollDialog.create( this, rollData);
|
||||||
|
rollDialog.render( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
508
modules/mournblade-item-sheet.js
Normal file
508
modules/mournblade-item-sheet.js
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
import { MournbladeUtility } from "./Mournblade-utility.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend the basic ItemSheet with some very simple modifications
|
||||||
|
* @extends {ItemSheet}
|
||||||
|
*/
|
||||||
|
export class MournbladeItemSheet extends ItemSheet {
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static get defaultOptions() {
|
||||||
|
|
||||||
|
return mergeObject(super.defaultOptions, {
|
||||||
|
classes: ["fvtt-mournblade", "sheet", "item"],
|
||||||
|
template: "systems/fvtt-mournblade/templates/item-sheet.html",
|
||||||
|
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
||||||
|
width: 620,
|
||||||
|
height: 550
|
||||||
|
//tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description"}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
_getHeaderButtons() {
|
||||||
|
let buttons = super._getHeaderButtons();
|
||||||
|
// Add "Post to chat" button
|
||||||
|
// We previously restricted this to GM and editable items only. If you ever find this comment because it broke something: eh, sorry!
|
||||||
|
buttons.unshift(
|
||||||
|
{
|
||||||
|
class: "post",
|
||||||
|
icon: "fas fa-comment",
|
||||||
|
onclick: ev => { }
|
||||||
|
})
|
||||||
|
return buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/** @override */
|
||||||
|
setPosition(options = {}) {
|
||||||
|
const position = super.setPosition(options);
|
||||||
|
const sheetBody = this.element.find(".sheet-body");
|
||||||
|
const bodyHeight = position.height - 192;
|
||||||
|
sheetBody.css("height", bodyHeight);
|
||||||
|
if (this.item.type.includes('weapon')) {
|
||||||
|
position.width = 640;
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async getData() {
|
||||||
|
const objectData = MournbladeUtility.data(this.object);
|
||||||
|
|
||||||
|
let itemData = foundry.utils.deepClone(MournbladeUtility.templateData(this.object));
|
||||||
|
let formData = {
|
||||||
|
title: this.title,
|
||||||
|
id: this.id,
|
||||||
|
type: objectData.type,
|
||||||
|
img: objectData.img,
|
||||||
|
name: objectData.name,
|
||||||
|
editable: this.isEditable,
|
||||||
|
cssClass: this.isEditable ? "editable" : "locked",
|
||||||
|
optionsDiceList: MournbladeUtility.getOptionsDiceList(),
|
||||||
|
optionsStatusList: MournbladeUtility.getOptionsStatusList(),
|
||||||
|
data: itemData,
|
||||||
|
limited: this.object.limited,
|
||||||
|
options: this.options,
|
||||||
|
owner: this.document.isOwner,
|
||||||
|
mr: (this.object.type == 'specialisation'),
|
||||||
|
isGM: game.user.isGM
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options.editable = !(this.object.data.origin == "embeddedItem");
|
||||||
|
console.log("ITEM DATA", formData, this);
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
_getHeaderButtons() {
|
||||||
|
let buttons = super._getHeaderButtons();
|
||||||
|
buttons.unshift({
|
||||||
|
class: "post",
|
||||||
|
icon: "fas fa-comment",
|
||||||
|
onclick: ev => this.postItem()
|
||||||
|
});
|
||||||
|
return buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
postItem() {
|
||||||
|
let chatData = duplicate(MournbladeUtility.data(this.item));
|
||||||
|
if (this.actor) {
|
||||||
|
chatData.actor = { id: this.actor.id };
|
||||||
|
}
|
||||||
|
// Don't post any image for the item (which would leave a large gap) if the default image is used
|
||||||
|
if (chatData.img.includes("/blank.png")) {
|
||||||
|
chatData.img = null;
|
||||||
|
}
|
||||||
|
// JSON object for easy creation
|
||||||
|
chatData.jsondata = JSON.stringify(
|
||||||
|
{
|
||||||
|
compendium: "postedItem",
|
||||||
|
payload: chatData,
|
||||||
|
});
|
||||||
|
|
||||||
|
renderTemplate('systems/fvtt-Mournblade-rpg/templates/post-item.html', chatData).then(html => {
|
||||||
|
let chatOptions = MournbladeUtility.chatDataSetup(html);
|
||||||
|
ChatMessage.create(chatOptions)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async viewSubitem(ev) {
|
||||||
|
let field = $(ev.currentTarget).data('type');
|
||||||
|
let idx = Number($(ev.currentTarget).data('index'));
|
||||||
|
let itemData = this.object.data.data[field][idx];
|
||||||
|
if (itemData.name != 'None') {
|
||||||
|
let spec = await Item.create(itemData, { temporary: true });
|
||||||
|
spec.data.origin = "embeddedItem";
|
||||||
|
new MournbladeItemSheet(spec).render(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async deleteSubitem(ev) {
|
||||||
|
let field = $(ev.currentTarget).data('type');
|
||||||
|
let idx = Number($(ev.currentTarget).data('index'));
|
||||||
|
let oldArray = this.object.data.data[field];
|
||||||
|
let itemData = this.object.data.data[field][idx];
|
||||||
|
if (itemData.name != 'None') {
|
||||||
|
let newArray = [];
|
||||||
|
for (var i = 0; i < oldArray.length; i++) {
|
||||||
|
if (i != idx) {
|
||||||
|
newArray.push(oldArray[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.object.update({ [`data.${field}`]: newArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async manageSpec() {
|
||||||
|
let itemData = this.object.data.data.specialisation[0];
|
||||||
|
if (itemData.name != 'None') {
|
||||||
|
let spec = await Item.create(itemData, { temporary: true });
|
||||||
|
spec.data.origin = "embeddedItem";
|
||||||
|
new MournbladeItemSheet(spec).render(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/** @override */
|
||||||
|
activateListeners(html) {
|
||||||
|
super.activateListeners(html);
|
||||||
|
|
||||||
|
// Everything below here is only needed if the sheet is editable
|
||||||
|
if (!this.options.editable) return;
|
||||||
|
|
||||||
|
|
||||||
|
// Update Inventory Item
|
||||||
|
html.find('.item-edit').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
const item = this.object.options.actor.getOwnedItem(li.data("item-id"));
|
||||||
|
item.sheet.render(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.delete-spec').click(ev => {
|
||||||
|
this.object.update({ "data.specialisation": [{ name: 'None' }] });
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.delete-subitem').click(ev => {
|
||||||
|
this.deleteSubitem(ev);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.stat-choice-flag').click(ev => {
|
||||||
|
let idx = $(ev.currentTarget).data("stat-idx");
|
||||||
|
let array = duplicate(this.object.data.data.statincreasechoice);
|
||||||
|
array[Number(idx)].flag = !array[Number(idx)].flag;
|
||||||
|
this.object.update({ "data.statincreasechoice": array });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update Inventory Item
|
||||||
|
html.find('.item-delete').click(ev => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
let itemId = li.data("item-id");
|
||||||
|
let itemType = li.data("item-type");
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.view-subitem').click(ev => {
|
||||||
|
this.viewSubitem(ev);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('.view-spec').click(ev => {
|
||||||
|
this.manageSpec();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addAbility(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
newItem._id = randomID(dataItem.id.length);
|
||||||
|
console.log("ABB", event, item, dataItem)
|
||||||
|
if (event.toElement.className == 'drop-abilities') {
|
||||||
|
let abilityArray = duplicate(this.object.data.data.abilities);
|
||||||
|
abilityArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.abilities': abilityArray });
|
||||||
|
}
|
||||||
|
if (event.toElement.className == 'drop-optionnal-abilities') {
|
||||||
|
let abilityArray = duplicate(this.object.data.data.optionnalabilities);
|
||||||
|
abilityArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.optionnalabilities': abilityArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addRacePerk(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
newItem._id = randomID(dataItem.id.length);
|
||||||
|
if (event.toElement.className == 'drop-race-perk') {
|
||||||
|
let perkArray = duplicate(this.object.data.data.perks);
|
||||||
|
perkArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.perks': perkArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addSpecialisation(item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
newItem._id = randomID(dataItem.id.length);
|
||||||
|
let specArray = [newItem];
|
||||||
|
await this.object.update({ 'data.specialisation': specArray });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addRoleSpecialisation(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
newItem._id = randomID(dataItem.id.length);
|
||||||
|
console.log("Add spec", event, newItem);
|
||||||
|
if (event.toElement.className == 'drop-spec1') {
|
||||||
|
let specArray = duplicate(this.object.data.data.specialisationsplus1);
|
||||||
|
specArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.specialisationsplus1': specArray });
|
||||||
|
}
|
||||||
|
if (event.toElement.className == 'drop-spec2') {
|
||||||
|
let specArray = duplicate(this.object.data.data.specincrease);
|
||||||
|
specArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.specincrease': specArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addRolePerk(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
newItem._id = randomID(dataItem.id.length);
|
||||||
|
console.log("Add spec", event, newItem);
|
||||||
|
if (event.toElement.className == 'drop-perk2') {
|
||||||
|
let perkArray = duplicate(this.object.data.data.perks);
|
||||||
|
perkArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.perks': perkArray });
|
||||||
|
}
|
||||||
|
if (event.toElement.className == 'drop-specialperk1') {
|
||||||
|
let perkArray = duplicate(this.object.data.data.specialperk);
|
||||||
|
perkArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.specialperk': perkArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addPower(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
newItem._id = randomID(dataItem.id.length);
|
||||||
|
if (event.toElement.className == 'drop-spec-power') {
|
||||||
|
let powArray = duplicate(this.object.data.data.powers);
|
||||||
|
powArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.powers': powArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addAbilityPower(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
newItem._id = randomID(dataItem.id.length);
|
||||||
|
if (event.toElement.className == 'drop-ability-power') {
|
||||||
|
let powArray = duplicate(this.object.data.data.powersgained);
|
||||||
|
powArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.powersgained': powArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addAbilityEffect(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
newItem._id = randomID(dataItem.id.length);
|
||||||
|
if (event.toElement.className == 'drop-ability-effect') {
|
||||||
|
let powArray = duplicate(this.object.data.data.effectsgained);
|
||||||
|
powArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.effectsgained': powArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addAbilitySpec(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
newItem._id = randomID(dataItem.id.length);
|
||||||
|
if (event.toElement.className == 'drop-ability-spec') {
|
||||||
|
let powArray = duplicate(this.object.data.data.specialisations);
|
||||||
|
powArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.specialisations': powArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addAbilityWeaponArmor(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
newItem._id = randomID(dataItem.id.length);
|
||||||
|
if (event.toElement.className == 'drop-ability-weapon') {
|
||||||
|
let weaponArray = duplicate(this.object.data.data.attackgained);
|
||||||
|
weaponArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.attackgained': weaponArray });
|
||||||
|
}
|
||||||
|
if (event.toElement.className == 'drop-ability-armor') {
|
||||||
|
let armorArray = duplicate(this.object.data.data.armorgained);
|
||||||
|
armorArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.armorgained': armorArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addPerkSpecialisation(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
if (event.toElement.className == 'drop-spec-perk') {
|
||||||
|
//console.log("PER SPEC", event)
|
||||||
|
let key = event.toElement.dataset["key"];
|
||||||
|
if (key == 'affectedspec') {
|
||||||
|
await this.object.update({ 'data.features.affectedspec.value': newItem.name });
|
||||||
|
} else {
|
||||||
|
await this.object.update({ 'data.features.gainspecdice.value': newItem.name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addPerkEffect(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data)
|
||||||
|
if (event.toElement.className == 'drop-perk-effect') {
|
||||||
|
let effectArray = duplicate(this.object.data.data.effectsgained)
|
||||||
|
effectArray.push(newItem)
|
||||||
|
await this.object.update({ 'data.effectsgained': effectArray })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addEffectPower(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data)
|
||||||
|
if (event.toElement.className == 'drop-power-effect') {
|
||||||
|
let effectArray = duplicate(this.object.data.data.effectsgained)
|
||||||
|
effectArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.effectsgained': effectArray })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addEffectSpec(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
if (event.toElement.className == 'drop-effect-spec') {
|
||||||
|
let specArray = duplicate(this.object.data.data.recoveryrollspec);
|
||||||
|
specArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.recoveryrollspec': specArray });
|
||||||
|
}
|
||||||
|
if (event.toElement.className == 'drop-effect-specaffected') {
|
||||||
|
let specArray = duplicate(this.object.data.data.specaffected);
|
||||||
|
specArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.specaffected': specArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async addEffectItem(event, item, dataItem) {
|
||||||
|
let newItem = duplicate(item.data);
|
||||||
|
if (event.toElement.className == 'drop-equipment-effect') {
|
||||||
|
let effectArray = duplicate(this.object.data.data.effects);
|
||||||
|
effectArray.push(newItem);
|
||||||
|
await this.object.update({ 'data.effects': effectArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async _onDrop(event) {
|
||||||
|
|
||||||
|
if (this.object.type == 'weapon' || this.object.type == 'shield' || this.object.type == 'armor' || this.object.type == 'shield') {
|
||||||
|
let data = event.dataTransfer.getData('text/plain');
|
||||||
|
if (data) {
|
||||||
|
let dataItem = JSON.parse(data);
|
||||||
|
let item = await MournbladeUtility.searchItem(dataItem);
|
||||||
|
if (item.data.type == 'effect') {
|
||||||
|
return this.addEffectItem(event, item, dataItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.object.type == 'power') {
|
||||||
|
let data = event.dataTransfer.getData('text/plain');
|
||||||
|
if (data) {
|
||||||
|
let dataItem = JSON.parse(data);
|
||||||
|
let item = await MournbladeUtility.searchItem(dataItem);
|
||||||
|
if (item.data.type == 'effect') {
|
||||||
|
return this.addEffectPower(event, item, dataItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.object.type == 'effect') {
|
||||||
|
let data = event.dataTransfer.getData('text/plain');
|
||||||
|
if (data) {
|
||||||
|
let dataItem = JSON.parse(data);
|
||||||
|
let item = await MournbladeUtility.searchItem(dataItem);
|
||||||
|
if (item.data.type == 'specialisation') {
|
||||||
|
return this.addEffectSpec(event, item, dataItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.object.type == 'race') {
|
||||||
|
let data = event.dataTransfer.getData('text/plain');
|
||||||
|
if (data) {
|
||||||
|
let dataItem = JSON.parse(data);
|
||||||
|
let item = await MournbladeUtility.searchItem(dataItem);
|
||||||
|
if (item.data.type == 'ability') {
|
||||||
|
return this.addAbility(event, item, dataItem);
|
||||||
|
}
|
||||||
|
if (item.data.type == 'perk') {
|
||||||
|
return this.addRacePerk(event, item, dataItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.object.type == 'perk') {
|
||||||
|
let data = event.dataTransfer.getData('text/plain')
|
||||||
|
if (data) {
|
||||||
|
let dataItem = JSON.parse(data);
|
||||||
|
let item = await MournbladeUtility.searchItem(dataItem)
|
||||||
|
if (item.data.type == 'specialisation') {
|
||||||
|
return this.addPerkSpecialisation(event, item, dataItem)
|
||||||
|
}
|
||||||
|
if (item.data.type == 'effect') {
|
||||||
|
return this.addPerkEffect(event, item, dataItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.object.type == 'specialisation') {
|
||||||
|
let data = event.dataTransfer.getData('text/plain');
|
||||||
|
if (data) {
|
||||||
|
let dataItem = JSON.parse(data);
|
||||||
|
let item = await MournbladeUtility.searchItem(dataItem);
|
||||||
|
if (item.data.type == 'power') {
|
||||||
|
return this.addPower(event, item, dataItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.object.type == 'ability') {
|
||||||
|
let data = event.dataTransfer.getData('text/plain');
|
||||||
|
if (data) {
|
||||||
|
let dataItem = JSON.parse(data);
|
||||||
|
let item = await MournbladeUtility.searchItem(dataItem);
|
||||||
|
if (item.data.type == 'effect') {
|
||||||
|
return this.addAbilityEffect(event, item, dataItem);
|
||||||
|
}
|
||||||
|
if (item.data.type == 'power') {
|
||||||
|
return this.addAbilityPower(event, item, dataItem);
|
||||||
|
}
|
||||||
|
if (item.data.type == 'specialisation') {
|
||||||
|
return this.addAbilitySpec(event, item, dataItem);
|
||||||
|
}
|
||||||
|
if (item.data.type == 'weapon' || item.data.type == 'armor') {
|
||||||
|
return this.addAbilityWeaponArmor(event, item, dataItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.object.type == 'role') {
|
||||||
|
let data = event.dataTransfer.getData('text/plain');
|
||||||
|
if (data) {
|
||||||
|
let dataItem = JSON.parse(data);
|
||||||
|
let item = await MournbladeUtility.searchItem(dataItem);
|
||||||
|
if (item.data.type == 'specialisation') {
|
||||||
|
return this.addRoleSpecialisation(event, item, dataItem);
|
||||||
|
}
|
||||||
|
if (item.data.type == 'perk') {
|
||||||
|
return this.addRolePerk(event, item, dataItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.notifications.warn("This item can not be dropped over another item");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
get template() {
|
||||||
|
let type = this.item.type;
|
||||||
|
return `systems/fvtt-Mournblade-rpg/templates/item-${type}-sheet.html`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/** @override */
|
||||||
|
_updateObject(event, formData) {
|
||||||
|
return this.object.update(formData);
|
||||||
|
}
|
||||||
|
}
|
19
modules/mournblade-item.js
Normal file
19
modules/mournblade-item.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { MournbladeUtility } from "./mournblade-utility.js";
|
||||||
|
|
||||||
|
export const defaultItemImg = {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend the basic ItemSheet with some very simple modifications
|
||||||
|
* @extends {ItemSheet}
|
||||||
|
*/
|
||||||
|
export class MournbladeItem extends Item {
|
||||||
|
|
||||||
|
constructor(data, context) {
|
||||||
|
if (!data.img) {
|
||||||
|
data.img = defaultItemImg[data.type];
|
||||||
|
}
|
||||||
|
super(data, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
111
modules/mournblade-main.js
Normal file
111
modules/mournblade-main.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/**
|
||||||
|
* Mournblade system
|
||||||
|
* Author: Uberwald
|
||||||
|
* Software License: Prop
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
// Import Modules
|
||||||
|
import { MournbladeActor } from "./mournblade-actor.js";
|
||||||
|
import { MournbladeItemSheet } from "./mournblade-item-sheet.js";
|
||||||
|
import { MournbladeActorSheet } from "./mournblade-actor-sheet.js";
|
||||||
|
import { MournbladeNPCSheet } from "./mournblade-npc-sheet.js";
|
||||||
|
import { MournbladeUtility } from "./mournblade-utility.js";
|
||||||
|
import { MournbladeCombat } from "./mournblade-combat.js";
|
||||||
|
import { MournbladeItem } from "./mournblade-item.js";
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Foundry VTT Initialization */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/************************************************************************************/
|
||||||
|
Hooks.once("init", async function () {
|
||||||
|
console.log(`Initializing Mournblade RPG`);
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
// preload handlebars templates
|
||||||
|
MournbladeUtility.preloadHandlebarsTemplates();
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
// Set an initiative formula for the system
|
||||||
|
CONFIG.Combat.initiative = {
|
||||||
|
formula: "1d6",
|
||||||
|
decimals: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
game.socket.on("system.fvtt-mournblade-rpg", data => {
|
||||||
|
MournbladeUtility.onSocketMesssage(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
// Define custom Entity classes
|
||||||
|
CONFIG.Combat.documentClass = MournbladeCombat
|
||||||
|
CONFIG.Actor.documentClass = MournbladeActor
|
||||||
|
CONFIG.Item.documentClass = MournbladeItem
|
||||||
|
//CONFIG.Token.objectClass = MournbladeToken
|
||||||
|
game.system.Mournblade = { };
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
// Register sheet application classes
|
||||||
|
Actors.unregisterSheet("core", ActorSheet);
|
||||||
|
Actors.registerSheet("fvtt-mournblade", MournbladeActorSheet, { types: ["character"], makeDefault: true });
|
||||||
|
Actors.registerSheet("fvtt-mournblade", MournbladeNPCSheet, { types: ["npc"], makeDefault: false });
|
||||||
|
|
||||||
|
Items.unregisterSheet("core", ItemSheet);
|
||||||
|
Items.registerSheet("fvtt-mournblade", MournbladeItemSheet, { makeDefault: true });
|
||||||
|
|
||||||
|
MournbladeUtility.init();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
function welcomeMessage() {
|
||||||
|
ChatMessage.create({
|
||||||
|
user: game.user.id,
|
||||||
|
whisper: [game.user.id],
|
||||||
|
content: `<div id="welcome-message-Mournblade"><span class="rdd-roll-part">
|
||||||
|
<strong>Welcome to Mournblade RPG.</strong>
|
||||||
|
` });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Foundry VTT Initialization */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
Hooks.once("ready", function () {
|
||||||
|
|
||||||
|
MournbladeUtility.ready();
|
||||||
|
// User warning
|
||||||
|
if (!game.user.isGM && game.user.character == undefined) {
|
||||||
|
ui.notifications.info("Warning ! No character linked to your user !");
|
||||||
|
ChatMessage.create({
|
||||||
|
content: "<b>WARNING</b> The player " + game.user.name + " is not linked to a character !",
|
||||||
|
user: game.user._id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSS patch for v9
|
||||||
|
if (game.version) {
|
||||||
|
let sidebar = document.getElementById("sidebar");
|
||||||
|
sidebar.style.width = "min-content";
|
||||||
|
}
|
||||||
|
|
||||||
|
welcomeMessage();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Foundry VTT Initialization */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
Hooks.on("chatMessage", (html, content, msg) => {
|
||||||
|
if (content[0] == '/') {
|
||||||
|
let regExp = /(\S+)/g;
|
||||||
|
let commands = content.match(regExp);
|
||||||
|
if (game.system.Mournblade.commands.processChatCommand(commands, content, msg)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
239
modules/mournblade-roll-dialog.js
Normal file
239
modules/mournblade-roll-dialog.js
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
import { MournbladeUtility } from "./mournblade-utility.js";
|
||||||
|
|
||||||
|
export class MournbladeRollDialog extends Dialog {
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async create(actor, rollData ) {
|
||||||
|
|
||||||
|
let options = { classes: ["MournbladeDialog"], width: 620, height: 380, 'z-index': 99999 };
|
||||||
|
let html = await renderTemplate('systems/fvtt-mournblade/templates/roll-dialog-generic.html', rollData);
|
||||||
|
|
||||||
|
return new MournbladeRollDialog(actor, rollData, html, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
constructor(actor, rollData, html, options, close = undefined) {
|
||||||
|
let conf = {
|
||||||
|
title: (rollData.mode == "skill") ? "Skill" : "Roll",
|
||||||
|
content: html,
|
||||||
|
buttons: {
|
||||||
|
roll: {
|
||||||
|
icon: '<i class="fas fa-check"></i>',
|
||||||
|
label: "Roll !",
|
||||||
|
callback: () => { this.roll() }
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
icon: '<i class="fas fa-times"></i>',
|
||||||
|
label: "Cancel",
|
||||||
|
callback: () => { this.close() }
|
||||||
|
} },
|
||||||
|
close: close
|
||||||
|
}
|
||||||
|
|
||||||
|
super(conf, options);
|
||||||
|
|
||||||
|
this.actor = actor;
|
||||||
|
this.rollData = rollData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
roll () {
|
||||||
|
MournbladeUtility.rollMournblade( this.rollData )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
manageEffects( effectIdx, toggled) {
|
||||||
|
let effect = this.rollData.effectsList[effectIdx]
|
||||||
|
if (effect) {
|
||||||
|
effect.applied = toggled
|
||||||
|
|
||||||
|
let level, genre, idVal
|
||||||
|
if (effect.type == 'hindrance' ) {
|
||||||
|
level = effect.value
|
||||||
|
genre = 'positive'
|
||||||
|
idVal = "#hindranceDicesLevel"
|
||||||
|
}
|
||||||
|
if (effect.type == 'effect' ) {
|
||||||
|
let effectData = effect.effect
|
||||||
|
level = effectData.data.effectlevel
|
||||||
|
genre = effectData.data.genre
|
||||||
|
effectData.data.isUsed = toggled
|
||||||
|
if (effectData.data.bonusdice) {
|
||||||
|
idVal = "#bonusDicesLevel"
|
||||||
|
}
|
||||||
|
if (effectData.data.reducedicevalue || effectData.data.statdice) {
|
||||||
|
idVal = "#statDicesLevel"
|
||||||
|
}
|
||||||
|
if (effectData.data.otherdice) {
|
||||||
|
idVal = "#otherDicesLevel"
|
||||||
|
}
|
||||||
|
if (effectData.data.hindrance) {
|
||||||
|
idVal = "#hindranceDicesLevel"
|
||||||
|
genre = 'positive' // Dynamic fix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now process the dice level update
|
||||||
|
let newLevel = Number($(idVal).val())
|
||||||
|
console.log("Ongoing", newLevel, toggled, idVal )
|
||||||
|
if (toggled) {
|
||||||
|
if ( genre == 'positive') {
|
||||||
|
newLevel += Number(level)
|
||||||
|
}else {
|
||||||
|
newLevel -= Number(level)
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
if ( genre == 'positive') {
|
||||||
|
newLevel -= Number(level)
|
||||||
|
}else {
|
||||||
|
newLevel += Number(level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newLevel = (newLevel<0) ? 0 : newLevel
|
||||||
|
$(idVal).val(newLevel)
|
||||||
|
}
|
||||||
|
//console.log("Effect", effect, toggled)
|
||||||
|
this.rollData.statDicesLevel = Number($('#statDicesLevel').val())
|
||||||
|
this.rollData.specDicesLevel = Number($('#specDicesLevel').val())
|
||||||
|
this.rollData.bonusDicesLevel = Number($('#bonusDicesLevel').val())
|
||||||
|
this.rollData.hindranceDicesLevel = Number($('#hindranceDicesLevel').val())
|
||||||
|
this.rollData.otherDicesLevel = Number($('#otherDicesLevel').val())
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
manageArmors( armorIdx, toggled) {
|
||||||
|
let armor = this.rollData.armorsList[armorIdx]
|
||||||
|
if (armor) {
|
||||||
|
armor.applied = toggled
|
||||||
|
if (armor.type == 'other' ) {
|
||||||
|
if (toggled) {
|
||||||
|
this.rollData.otherDicesLevel += Number(armor.value)
|
||||||
|
} else {
|
||||||
|
this.rollData.otherDicesLevel -= Number(armor.value)
|
||||||
|
this.rollData.otherDicesLevel = (this.rollData.otherDicesLevel<0) ? 0 : this.rollData.otherDicesLevel
|
||||||
|
}
|
||||||
|
$("#otherDicesLevel").val(this.rollData.otherDicesLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("Armor", armorIdx, toggled)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
manageWeapons( weaponIdx, toggled) {
|
||||||
|
let weapon = this.rollData.weaponsList[weaponIdx]
|
||||||
|
if (weapon) {
|
||||||
|
if (toggled) {
|
||||||
|
this.rollData.weaponName = weapon.weapon.name
|
||||||
|
} else {
|
||||||
|
this.rollData.weaponName = undefined
|
||||||
|
}
|
||||||
|
weapon.applied = toggled
|
||||||
|
if (weapon.type == 'damage' || weapon.type == 'enhanced' ) {
|
||||||
|
if (toggled) {
|
||||||
|
this.rollData.otherDicesLevel += Number(weapon.value)
|
||||||
|
} else {
|
||||||
|
this.rollData.weaponName = undefined
|
||||||
|
this.rollData.otherDicesLevel -= Number(weapon.value)
|
||||||
|
this.rollData.otherDicesLevel = (this.rollData.otherDicesLevel<0) ? 0 : this.rollData.otherDicesLevel
|
||||||
|
}
|
||||||
|
$("#otherDicesLevel").val(this.rollData.otherDicesLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("Weapon", weaponIdx, toggled, this.rollData.otherDicesLevel, weapon)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
manageEquip( equipIdx, toggled) {
|
||||||
|
let equip = this.rollData.equipmentsList[equipIdx]
|
||||||
|
if (equip) {
|
||||||
|
equip.applied = toggled
|
||||||
|
let idVal = "#otherDicesLevel" // Default
|
||||||
|
if (equip.equip.data.bonusdice) {
|
||||||
|
idVal = "#bonusDicesLevel"
|
||||||
|
}
|
||||||
|
if (equip.equip.data.statdice) {
|
||||||
|
idVal = "#statDicesLevel"
|
||||||
|
}
|
||||||
|
if (equip.equip.data.otherdice) {
|
||||||
|
idVal = "#otherDicesLevel"
|
||||||
|
}
|
||||||
|
let newLevel = Number($(idVal).val())
|
||||||
|
if (toggled) {
|
||||||
|
newLevel += Number(equip.value)
|
||||||
|
} else {
|
||||||
|
newLevel -= Number(equip.value)
|
||||||
|
}
|
||||||
|
newLevel = (newLevel <0) ? 0 : newLevel
|
||||||
|
$(idVal).val(newLevel)
|
||||||
|
// Then refresh
|
||||||
|
this.rollData.statDicesLevel = Number($('#statDicesLevel').val())
|
||||||
|
this.rollData.specDicesLevel = Number($('#specDicesLevel').val())
|
||||||
|
this.rollData.bonusDicesLevel = Number($('#bonusDicesLevel').val())
|
||||||
|
this.rollData.hindranceDicesLevel = Number($('#hindranceDicesLevel').val())
|
||||||
|
this.rollData.otherDicesLevel = Number($('#otherDicesLevel').val())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
activateListeners(html) {
|
||||||
|
super.activateListeners(html);
|
||||||
|
|
||||||
|
var dialog = this;
|
||||||
|
function onLoad() {
|
||||||
|
}
|
||||||
|
$(function () { onLoad(); });
|
||||||
|
|
||||||
|
html.find('#specList').change(async (event) => {
|
||||||
|
this.rollData.selectedSpec = event.currentTarget.value
|
||||||
|
let spec = this.rollData.specList.find(item => item._id == this.rollData.selectedSpec)
|
||||||
|
if ( spec) {
|
||||||
|
this.rollData.specDiceLevel = spec.data.level
|
||||||
|
this.rollData.specName = spec.name
|
||||||
|
$('#specDicesLevel').val(this.rollData.specDiceLevel)
|
||||||
|
} else {
|
||||||
|
this.rollData.specName = undefined
|
||||||
|
$('#specDicesLevel').val(0)
|
||||||
|
}
|
||||||
|
const content = await renderTemplate("systems/fvtt-Mournblade-rpg/templates/roll-dialog-generic.html", this.rollData)
|
||||||
|
this.data.content = content
|
||||||
|
this.render(true)
|
||||||
|
});
|
||||||
|
html.find('#statDicesLevel').change((event) => {
|
||||||
|
this.rollData.statDicesLevel = Number(event.currentTarget.value)
|
||||||
|
});
|
||||||
|
html.find('#specDicesLevel').change((event) => {
|
||||||
|
this.rollData.specDicesLevel = Number(event.currentTarget.value)
|
||||||
|
});
|
||||||
|
html.find('#bonusDicesLevel').change((event) => {
|
||||||
|
this.rollData.bonusDicesLevel = Number(event.currentTarget.value)
|
||||||
|
});
|
||||||
|
html.find('#hindranceDicesLevel').change((event) => {
|
||||||
|
this.rollData.hindranceDicesLevel = Number(event.currentTarget.value)
|
||||||
|
});
|
||||||
|
html.find('#otherDicesLevel').change((event) => {
|
||||||
|
this.rollData.otherDicesLevel = Number(event.currentTarget.value)
|
||||||
|
});
|
||||||
|
html.find('.effect-clicked').change((event) => {
|
||||||
|
let toggled = event.currentTarget.checked
|
||||||
|
let effectIdx = $(event.currentTarget).data("effect-idx")
|
||||||
|
this.manageEffects( effectIdx, toggled)
|
||||||
|
});
|
||||||
|
html.find('.armor-clicked').change((event) => {
|
||||||
|
let toggled = event.currentTarget.checked
|
||||||
|
let armorIdx = $(event.currentTarget).data("armor-idx")
|
||||||
|
this.manageArmors( armorIdx, toggled)
|
||||||
|
});
|
||||||
|
html.find('.weapon-clicked').change((event) => {
|
||||||
|
let toggled = event.currentTarget.checked
|
||||||
|
let weaponIdx = $(event.currentTarget).data("weapon-idx")
|
||||||
|
this.manageWeapons( weaponIdx, toggled)
|
||||||
|
});
|
||||||
|
html.find('.equip-clicked').change((event) => {
|
||||||
|
let toggled = event.currentTarget.checked
|
||||||
|
let equipIdx = $(event.currentTarget).data("equip-idx")
|
||||||
|
this.manageEquip( equipIdx, toggled)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
395
modules/mournblade-utility.js
Normal file
395
modules/mournblade-utility.js
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
/* -------------------------------------------- */
|
||||||
|
import { MournbladeCombat } from "./mournblade-combat.js";
|
||||||
|
import { MournbladeCommands } from "./mournblade-commands.js";
|
||||||
|
import { MournbladeActorCreate } from "./mournblade-create-char.js";
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
export class MournbladeUtility {
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async init() {
|
||||||
|
Hooks.on('renderChatLog', (log, html, data) => MournbladeUtility.chatListeners(html));
|
||||||
|
Hooks.on("getCombatTrackerEntryContext", (html, options) => {
|
||||||
|
MournbladeUtility.pushInitiativeOptions(html, options);
|
||||||
|
});
|
||||||
|
Hooks.on("dropCanvasData", (canvas, data) => {
|
||||||
|
MournbladeUtility.dropItemOnToken(canvas, data)
|
||||||
|
});
|
||||||
|
|
||||||
|
this.rollDataStore = {}
|
||||||
|
this.defenderStore = {}
|
||||||
|
MournbladeCommands.init();
|
||||||
|
|
||||||
|
Handlebars.registerHelper('count', function (list) {
|
||||||
|
return list.length;
|
||||||
|
});
|
||||||
|
Handlebars.registerHelper('includes', function (array, val) {
|
||||||
|
return array.includes(val);
|
||||||
|
});
|
||||||
|
Handlebars.registerHelper('upper', function (text) {
|
||||||
|
return text.toUpperCase();
|
||||||
|
});
|
||||||
|
Handlebars.registerHelper('upperFirst', function (text) {
|
||||||
|
if (typeof text !== 'string') return text
|
||||||
|
return text.charAt(0).toUpperCase() + text.slice(1)
|
||||||
|
});
|
||||||
|
Handlebars.registerHelper('notEmpty', function (list) {
|
||||||
|
return list.length > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static pushInitiativeOptions(html, options) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getSkills() {
|
||||||
|
return this.skills
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async ready() {
|
||||||
|
const skills = await MournbladeUtility.loadCompendium("fvtt-mournblade.skills")
|
||||||
|
this.skills = specs.map(i => i.toObject())
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async loadCompendiumData(compendium) {
|
||||||
|
const pack = game.packs.get(compendium);
|
||||||
|
return await pack?.getDocuments() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async loadCompendium(compendium, filter = item => true) {
|
||||||
|
let compendiumData = await MournbladeUtility.loadCompendiumData(compendium);
|
||||||
|
return compendiumData.filter(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getOptionsStatusList() {
|
||||||
|
return this.optionsStatusList;
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async chatListeners(html) {
|
||||||
|
|
||||||
|
html.on("click", '.view-item-from-chat', event => {
|
||||||
|
game.system.Mournblade.creator.openItemView(event)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async preloadHandlebarsTemplates() {
|
||||||
|
|
||||||
|
const templatePaths = [
|
||||||
|
'systems/fvtt-mournblade/templates/editor-notes-gm.html',
|
||||||
|
'systems/fvtt-mournblade/templates/partial-roll-select-effects.html',
|
||||||
|
'systems/fvtt-mournblade/templates/partial-options-statistics.html',
|
||||||
|
'systems/fvtt-mournblade/templates/partial-options-level.html',
|
||||||
|
'systems/fvtt-mournblade/templates/partial-options-range.html',
|
||||||
|
'systems/fvtt-mournblade/templates/partial-options-equipment-types.html',
|
||||||
|
'systems/fvtt-mournblade/templates/partial-equipment-effects.html'
|
||||||
|
]
|
||||||
|
return loadTemplates(templatePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static removeChatMessageId(messageId) {
|
||||||
|
if (messageId) {
|
||||||
|
game.messages.get(messageId)?.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static findChatMessageId(current) {
|
||||||
|
return MournbladeUtility.getChatMessageId(MournbladeUtility.findChatMessage(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
static getChatMessageId(node) {
|
||||||
|
return node?.attributes.getNamedItem('data-message-id')?.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static findChatMessage(current) {
|
||||||
|
return MournbladeUtility.findNodeMatching(current, it => it.classList.contains('chat-message') && it.attributes.getNamedItem('data-message-id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
static findNodeMatching(current, predicate) {
|
||||||
|
if (current) {
|
||||||
|
if (predicate(current)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
return MournbladeUtility.findNodeMatching(current.parentElement, predicate);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static templateData(it) {
|
||||||
|
return MournbladeUtility.data(it)?.data ?? {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static data(it) {
|
||||||
|
if (it instanceof Actor || it instanceof Item || it instanceof Combatant) {
|
||||||
|
return it.data;
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static createDirectOptionList(min, max) {
|
||||||
|
let options = {};
|
||||||
|
for (let i = min; i <= max; i++) {
|
||||||
|
options[`${i}`] = `${i}`;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static buildListOptions(min, max) {
|
||||||
|
let options = ""
|
||||||
|
for (let i = min; i <= max; i++) {
|
||||||
|
options += `<option value="${i}">${i}</option>`
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getTarget() {
|
||||||
|
if (game.user.targets && game.user.targets.size == 1) {
|
||||||
|
for (let target of game.user.targets) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getDefenseState(actorId) {
|
||||||
|
return this.defenderStore[actorId];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static updateRollData(rollData) {
|
||||||
|
|
||||||
|
let id = rollData.rollId;
|
||||||
|
let oldRollData = this.rollDataStore[id] || {};
|
||||||
|
let newRollData = mergeObject(oldRollData, rollData);
|
||||||
|
this.rollDataStore[id] = newRollData;
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static saveRollData(rollData) {
|
||||||
|
game.socket.emit("system.Mournblade-rpg", {
|
||||||
|
name: "msg_update_roll", data: rollData
|
||||||
|
}); // Notify all other clients of the roll
|
||||||
|
this.updateRollData(rollData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getRollData(id) {
|
||||||
|
return this.rollDataStore[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static onSocketMesssage(msg) {
|
||||||
|
//console.log("SOCKET MESSAGE", msg.name, game.user.character.id, msg.data.defenderId);
|
||||||
|
if (msg.name == "msg_update_defense_state") {
|
||||||
|
this.updateDefenseState(msg.data.defenderId, msg.data.rollId);
|
||||||
|
}
|
||||||
|
if (msg.name == "msg_update_roll") {
|
||||||
|
this.updateRollData(msg.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static chatDataSetup(content, modeOverride, isRoll = false, forceWhisper) {
|
||||||
|
let chatData = {
|
||||||
|
user: game.user.id,
|
||||||
|
rollMode: modeOverride || game.settings.get("core", "rollMode"),
|
||||||
|
content: content
|
||||||
|
};
|
||||||
|
|
||||||
|
if (["gmroll", "blindroll"].includes(chatData.rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM").map(u => u.id);
|
||||||
|
if (chatData.rollMode === "blindroll") chatData["blind"] = true;
|
||||||
|
else if (chatData.rollMode === "selfroll") chatData["whisper"] = [game.user];
|
||||||
|
|
||||||
|
if (forceWhisper) { // Final force !
|
||||||
|
chatData["speaker"] = ChatMessage.getSpeaker();
|
||||||
|
chatData["whisper"] = ChatMessage.getWhisperRecipients(forceWhisper);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chatData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async showDiceSoNice(roll, rollMode) {
|
||||||
|
if (game.modules.get("dice-so-nice")?.active) {
|
||||||
|
if (game.dice3d) {
|
||||||
|
let whisper = null;
|
||||||
|
let blind = false;
|
||||||
|
rollMode = rollMode ?? game.settings.get("core", "rollMode");
|
||||||
|
switch (rollMode) {
|
||||||
|
case "blindroll": //GM only
|
||||||
|
blind = true;
|
||||||
|
case "gmroll": //GM + rolling player
|
||||||
|
whisper = this.getUsers(user => user.isGM);
|
||||||
|
break;
|
||||||
|
case "roll": //everybody
|
||||||
|
whisper = this.getUsers(user => user.active);
|
||||||
|
break;
|
||||||
|
case "selfroll":
|
||||||
|
whisper = [game.user.id];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await game.dice3d.showForRoll(roll, game.user, true, whisper, blind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async rollMournblade(rollData) {
|
||||||
|
|
||||||
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
|
content: await renderTemplate(`systems/fvtt-Mournblade-rpg/templates/chat-generic-result.html`, rollData)
|
||||||
|
});
|
||||||
|
|
||||||
|
// And save the roll
|
||||||
|
this.saveRollData(rollData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getUsers(filter) {
|
||||||
|
return game.users.filter(filter).map(user => user.data._id);
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getWhisperRecipients(rollMode, name) {
|
||||||
|
switch (rollMode) {
|
||||||
|
case "blindroll": return this.getUsers(user => user.isGM);
|
||||||
|
case "gmroll": return this.getWhisperRecipientsAndGMs(name);
|
||||||
|
case "selfroll": return [game.user.id];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getWhisperRecipientsAndGMs(name) {
|
||||||
|
let recep1 = ChatMessage.getWhisperRecipients(name) || [];
|
||||||
|
return recep1.concat(ChatMessage.getWhisperRecipients('GM'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static blindMessageToGM(chatOptions) {
|
||||||
|
let chatGM = duplicate(chatOptions);
|
||||||
|
chatGM.whisper = this.getUsers(user => user.isGM);
|
||||||
|
chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content;
|
||||||
|
console.log("blindMessageToGM", chatGM);
|
||||||
|
game.socket.emit("system.fvtt-weapons-of-the-gods", { msg: "msg_gm_chat_message", data: chatGM });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async searchItem(dataItem) {
|
||||||
|
let item;
|
||||||
|
if (dataItem.pack) {
|
||||||
|
item = await fromUuid("Compendium." + dataItem.pack + "." + dataItem.id);
|
||||||
|
} else {
|
||||||
|
item = game.items.get(dataItem.id)
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static split3Columns(data) {
|
||||||
|
|
||||||
|
let array = [[], [], []];
|
||||||
|
if (data == undefined) return array;
|
||||||
|
|
||||||
|
let col = 0;
|
||||||
|
for (let key in data) {
|
||||||
|
let keyword = data[key];
|
||||||
|
keyword.key = key; // Self-reference
|
||||||
|
array[col].push(keyword);
|
||||||
|
col++;
|
||||||
|
if (col == 3) col = 0;
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static createChatMessage(name, rollMode, chatOptions) {
|
||||||
|
switch (rollMode) {
|
||||||
|
case "blindroll": // GM only
|
||||||
|
if (!game.user.isGM) {
|
||||||
|
this.blindMessageToGM(chatOptions);
|
||||||
|
|
||||||
|
chatOptions.whisper = [game.user.id];
|
||||||
|
chatOptions.content = "Message only to the GM";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
chatOptions.whisper = this.getUsers(user => user.isGM);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
chatOptions.whisper = this.getWhisperRecipients(rollMode, name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chatOptions.alias = chatOptions.alias || name;
|
||||||
|
ChatMessage.create(chatOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getBasicRollData() {
|
||||||
|
let rollData = {
|
||||||
|
rollId: randomID(16),
|
||||||
|
rollMode: game.settings.get("core", "rollMode"),
|
||||||
|
}
|
||||||
|
MournbladeUtility.updateWithTarget(rollData)
|
||||||
|
return rollData
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static updateWithTarget(rollData) {
|
||||||
|
let objectDefender
|
||||||
|
let target = MournbladeUtility.getTarget();
|
||||||
|
if (target) {
|
||||||
|
let defenderActor = game.actors.get(target.data.actorId)
|
||||||
|
objectDefender = MournbladeUtility.data(defenderActor)
|
||||||
|
objectDefender = mergeObject(objectDefender, target.data.actorData)
|
||||||
|
rollData.defender = objectDefender
|
||||||
|
rollData.attackerId = this.id
|
||||||
|
rollData.defenderId = objectDefender._id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static createChatWithRollMode(name, chatOptions) {
|
||||||
|
this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async confirmDelete(actorSheet, li) {
|
||||||
|
let itemId = li.data("item-id");
|
||||||
|
let msgTxt = "<p>Are you sure to remove this Item ?";
|
||||||
|
let buttons = {
|
||||||
|
delete: {
|
||||||
|
icon: '<i class="fas fa-check"></i>',
|
||||||
|
label: "Yes, remove it",
|
||||||
|
callback: () => {
|
||||||
|
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
|
||||||
|
li.slideUp(200, () => actorSheet.render(false));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
icon: '<i class="fas fa-times"></i>',
|
||||||
|
label: "Cancel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msgTxt += "</p>";
|
||||||
|
let d = new Dialog({
|
||||||
|
title: "Confirm removal",
|
||||||
|
content: msgTxt,
|
||||||
|
buttons: buttons,
|
||||||
|
default: "cancel"
|
||||||
|
});
|
||||||
|
d.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1218
styles/simple.css
Normal file
1218
styles/simple.css
Normal file
File diff suppressed because it is too large
Load Diff
48
system.json
Normal file
48
system.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"author": "Uberwald/LeRatierBretonnien",
|
||||||
|
"compatibleCoreVersion": "9",
|
||||||
|
"description": "Mournblade RPG for FoundryVTT",
|
||||||
|
"download": "",
|
||||||
|
"esmodules": [
|
||||||
|
"modules/mournblade-main.js"
|
||||||
|
],
|
||||||
|
"gridDistance": 5,
|
||||||
|
"gridUnits": "m",
|
||||||
|
"languages": [
|
||||||
|
{
|
||||||
|
"lang": "en",
|
||||||
|
"name": "English",
|
||||||
|
"path": "lang/en.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"library": false,
|
||||||
|
"license": "LICENSE.txt",
|
||||||
|
"manifest": "",
|
||||||
|
"manifestPlusVersion": "1.0.0",
|
||||||
|
"media": [],
|
||||||
|
"minimumCoreVersion": "0.8.0",
|
||||||
|
"name": "fvtt-mournblade",
|
||||||
|
"packs": [
|
||||||
|
{
|
||||||
|
"entity": "Item",
|
||||||
|
"label": "Skills",
|
||||||
|
"name": "skills",
|
||||||
|
"path": "./packs/skills.db",
|
||||||
|
"system": "fvtt-mournblade",
|
||||||
|
"tags": [
|
||||||
|
"skill", "competence"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryTokenAttribute": "secondary.health",
|
||||||
|
"secondaryTokenAttribute": "secondary.delirium",
|
||||||
|
"socket": true,
|
||||||
|
"styles": [
|
||||||
|
"styles/simple.css"
|
||||||
|
],
|
||||||
|
"templateVersion": 1,
|
||||||
|
"title": "Mournblade",
|
||||||
|
"url": "",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"background": "./images/ui/mournblade_welcome.webp"
|
||||||
|
}
|
138
template.json
Normal file
138
template.json
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
{
|
||||||
|
"Actor": {
|
||||||
|
"types": ["character", "npc"],
|
||||||
|
"templates": {
|
||||||
|
"biodata": {
|
||||||
|
"biodata": {
|
||||||
|
"name": "",
|
||||||
|
"age": 0,
|
||||||
|
"alignement": "",
|
||||||
|
"poids": "",
|
||||||
|
"taille": "",
|
||||||
|
"cheveux": "",
|
||||||
|
"sexe": "",
|
||||||
|
"yeux": "",
|
||||||
|
"description": "",
|
||||||
|
"notes": "",
|
||||||
|
"gmnotes": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"core": {
|
||||||
|
"subactors": [],
|
||||||
|
"attributs": {
|
||||||
|
"adr":{
|
||||||
|
"label": "Adresse",
|
||||||
|
"abbrev": "adr",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"pui":{
|
||||||
|
"label": "Puissance",
|
||||||
|
"abbrev": "pui",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"cla":{
|
||||||
|
"label": "Clairvoyance",
|
||||||
|
"abbrev": "cla",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"pre":{
|
||||||
|
"label": "Présence",
|
||||||
|
"abbrev": "pre",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"tre":{
|
||||||
|
"label": "Trempe",
|
||||||
|
"abbrev": "tre",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bonneaventure": {
|
||||||
|
"base": 0,
|
||||||
|
"actuelle": 0
|
||||||
|
},
|
||||||
|
"experience": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"eclat": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"sante": {
|
||||||
|
"base": 0,
|
||||||
|
"nonletaux": 0,
|
||||||
|
"letaux": 0,
|
||||||
|
"sequelles": ""
|
||||||
|
},
|
||||||
|
"ame": {
|
||||||
|
"base": 0,
|
||||||
|
"value": 0,
|
||||||
|
"traumatismes": ""
|
||||||
|
},
|
||||||
|
"combat": {
|
||||||
|
"initiative": 0,
|
||||||
|
"vitesse": 0,
|
||||||
|
"bonusdegats": 0,
|
||||||
|
"defensebase": 0
|
||||||
|
},
|
||||||
|
"balance": {
|
||||||
|
"loi": 0,
|
||||||
|
"chaos": 0,
|
||||||
|
"aspect": 0,
|
||||||
|
"marge": 0,
|
||||||
|
"pointschaos": 0,
|
||||||
|
"pointsloi": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npccore": {
|
||||||
|
"npctype": "",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"character": {
|
||||||
|
"templates": [ "biodata", "core" ]
|
||||||
|
},
|
||||||
|
"npc": {
|
||||||
|
"templates": [ "npccore" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Item": {
|
||||||
|
"templates": {
|
||||||
|
"base": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"types": [ "arme", "competence", "protection", "pacte", "traitchaotique", "monnaie", "don", "tendance", "rune"],
|
||||||
|
"arme": {
|
||||||
|
"templates": [ "base" ]
|
||||||
|
},
|
||||||
|
"predilection": {
|
||||||
|
"competenceId": "",
|
||||||
|
"templates": [ "base" ]
|
||||||
|
},
|
||||||
|
"competence": {
|
||||||
|
"niveau": 0,
|
||||||
|
"attribut": "",
|
||||||
|
"templates": [ "base" ]
|
||||||
|
},
|
||||||
|
"protection": {
|
||||||
|
"templates": [ "base" ]
|
||||||
|
},
|
||||||
|
"pacte": {
|
||||||
|
"templates": [ "base" ]
|
||||||
|
},
|
||||||
|
"traitchaotique": {
|
||||||
|
"templates": [ "base" ]
|
||||||
|
},
|
||||||
|
"monnaie": {
|
||||||
|
"templates": [ "base" ]
|
||||||
|
},
|
||||||
|
"don": {
|
||||||
|
"templates": [ "base" ]
|
||||||
|
},
|
||||||
|
"tendance": {
|
||||||
|
"templates": [ "base" ]
|
||||||
|
},
|
||||||
|
"rune": {
|
||||||
|
"templates": [ "base" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user