mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
fix conflicts
This commit is contained in:
commit
7178148c80
63 changed files with 1969 additions and 3301 deletions
|
|
@ -13,6 +13,7 @@ import { dualityRollEnricher } from './module/enrichers/DualityRollEnricher.mjs'
|
|||
import { getCommandTarget, rollCommandToJSON, setDiceSoNiceForDualityRoll } from './module/helpers/utils.mjs';
|
||||
import { abilities } from './module/config/actorConfig.mjs';
|
||||
import Resources from './module/applications/resources.mjs';
|
||||
import DHDualityRoll from './module/data/chat-message/dualityRoll.mjs';
|
||||
|
||||
globalThis.SYSTEM = SYSTEM;
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ Hooks.once('init', () => {
|
|||
CONFIG.Item.dataModels = models.items.config;
|
||||
|
||||
const { Items, Actors } = foundry.documents.collections;
|
||||
Items.unregisterSheet('core', foundry.appv1.sheets.ItemSheet);
|
||||
Items.unregisterSheet('core', foundry.applications.sheets.ItemSheetV2);
|
||||
Items.registerSheet(SYSTEM.id, applications.DhpAncestry, { types: ['ancestry'], makeDefault: true });
|
||||
Items.registerSheet(SYSTEM.id, applications.DhpCommunity, { types: ['community'], makeDefault: true });
|
||||
Items.registerSheet(SYSTEM.id, applications.DhpClassSheet, { types: ['class'], makeDefault: true });
|
||||
|
|
@ -53,13 +54,10 @@ Hooks.once('init', () => {
|
|||
Items.registerSheet(SYSTEM.id, applications.DhpArmor, { types: ['armor'], makeDefault: true });
|
||||
|
||||
CONFIG.Actor.documentClass = documents.DhpActor;
|
||||
CONFIG.Actor.dataModels = {
|
||||
pc: models.DhpPC,
|
||||
adversary: models.DhpAdversary,
|
||||
environment: models.DhpEnvironment
|
||||
};
|
||||
Actors.unregisterSheet('core', foundry.appv1.sheets.ActorSheet);
|
||||
Actors.registerSheet(SYSTEM.id, applications.DhpPCSheet, { types: ['pc'], makeDefault: true });
|
||||
CONFIG.Actor.dataModels = models.actors.config;
|
||||
|
||||
Actors.unregisterSheet('core', foundry.applications.sheets.ActorSheetV2);
|
||||
Actors.registerSheet(SYSTEM.id, applications.DhCharacterSheet, { types: ['character'], makeDefault: true });
|
||||
Actors.registerSheet(SYSTEM.id, applications.DhpAdversarySheet, { types: ['adversary'], makeDefault: true });
|
||||
Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true });
|
||||
|
||||
|
|
@ -137,22 +135,28 @@ const renderDualityButton = async event => {
|
|||
title: button.dataset.label,
|
||||
value: rollModifier
|
||||
});
|
||||
|
||||
const systemData = new DHDualityRoll({
|
||||
title: button.dataset.label,
|
||||
origin: target.id,
|
||||
roll: roll._formula,
|
||||
modifiers: modifiers,
|
||||
hope: hope,
|
||||
fear: fear,
|
||||
advantage: advantage,
|
||||
disadvantage: disadvantage
|
||||
});
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const msgData = {
|
||||
type: 'dualityRoll',
|
||||
sound: CONFIG.sounds.dice,
|
||||
system: {
|
||||
title: button.dataset.label,
|
||||
origin: target.id,
|
||||
roll: roll._formula,
|
||||
modifiers: modifiers,
|
||||
hope: hope,
|
||||
fear: fear,
|
||||
advantage: advantage,
|
||||
disadvantage: disadvantage
|
||||
},
|
||||
system: systemData,
|
||||
user: game.user.id,
|
||||
content: 'systems/daggerheart/templates/chat/duality-roll.hbs',
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/duality-roll.hbs',
|
||||
systemData
|
||||
),
|
||||
rolls: [roll]
|
||||
};
|
||||
|
||||
|
|
@ -226,29 +230,34 @@ Hooks.on('chatMessage', (_, message) => {
|
|||
: undefined,
|
||||
title
|
||||
});
|
||||
}).then(({ roll, attribute, title }) => {
|
||||
}).then(async ({ roll, attribute, title }) => {
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = new DHDualityRoll({
|
||||
title: title,
|
||||
origin: target?.id,
|
||||
roll: roll._formula,
|
||||
modifiers: attribute ? [attribute] : [],
|
||||
hope: { dice: rollCommand.hope ?? 'd12', value: roll.dice[0].total },
|
||||
fear: { dice: rollCommand.fear ?? 'd12', value: roll.dice[1].total },
|
||||
advantage:
|
||||
rollCommand.advantage && !rollCommand.disadvantage
|
||||
? { dice: 'd6', value: roll.dice[2].total }
|
||||
: undefined,
|
||||
disadvantage:
|
||||
rollCommand.disadvantage && !rollCommand.advantage
|
||||
? { dice: 'd6', value: roll.dice[2].total }
|
||||
: undefined
|
||||
});
|
||||
|
||||
const msgData = {
|
||||
type: 'dualityRoll',
|
||||
sound: CONFIG.sounds.dice,
|
||||
system: {
|
||||
title: title,
|
||||
origin: target?.id,
|
||||
roll: roll._formula,
|
||||
modifiers: attribute ? [attribute] : [],
|
||||
hope: { dice: rollCommand.hope ?? 'd12', value: roll.dice[0].total },
|
||||
fear: { dice: rollCommand.fear ?? 'd12', value: roll.dice[1].total },
|
||||
advantage:
|
||||
rollCommand.advantage && !rollCommand.disadvantage
|
||||
? { dice: 'd6', value: roll.dice[2].total }
|
||||
: undefined,
|
||||
disadvantage:
|
||||
rollCommand.disadvantage && !rollCommand.advantage
|
||||
? { dice: 'd6', value: roll.dice[2].total }
|
||||
: undefined
|
||||
},
|
||||
system: systemData,
|
||||
user: game.user.id,
|
||||
content: 'systems/daggerheart/templates/chat/duality-roll.hbs',
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/duality-roll.hbs',
|
||||
systemData
|
||||
),
|
||||
rolls: [roll]
|
||||
};
|
||||
|
||||
|
|
@ -275,10 +284,10 @@ const preloadHandlebarsTemplates = async function () {
|
|||
'systems/daggerheart/templates/sheets/parts/heritage.hbs',
|
||||
'systems/daggerheart/templates/sheets/parts/subclassFeature.hbs',
|
||||
'systems/daggerheart/templates/sheets/parts/effects.hbs',
|
||||
'systems/daggerheart/templates/sheets/pc/sections/inventory.hbs',
|
||||
'systems/daggerheart/templates/sheets/pc/sections/loadout.hbs',
|
||||
'systems/daggerheart/templates/sheets/pc/parts/heritageCard.hbs',
|
||||
'systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs',
|
||||
'systems/daggerheart/templates/sheets/character/sections/inventory.hbs',
|
||||
'systems/daggerheart/templates/sheets/character/sections/loadout.hbs',
|
||||
'systems/daggerheart/templates/sheets/character/parts/heritageCard.hbs',
|
||||
'systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs',
|
||||
'systems/daggerheart/templates/components/card-preview.hbs',
|
||||
'systems/daggerheart/templates/views/levelup/parts/selectable-card-preview.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||
|
|
|
|||
230
lang/en.json
230
lang/en.json
|
|
@ -13,8 +13,7 @@
|
|||
"armor": "Armor"
|
||||
},
|
||||
"Actor": {
|
||||
"pc": "PC",
|
||||
"npc": "NPC",
|
||||
"character": "Character",
|
||||
"adversary": "Adversary",
|
||||
"environment": "Environment"
|
||||
}
|
||||
|
|
@ -110,8 +109,14 @@
|
|||
},
|
||||
"VariantRules": {
|
||||
"ActionTokens": {
|
||||
"Name": "Action Tokens",
|
||||
"Hint": "Give each player action tokens to use in combat"
|
||||
"label": "Action Tokens",
|
||||
"hint": "Give each player action tokens to use in combat"
|
||||
},
|
||||
"FIELDS": {
|
||||
"useCoins": {
|
||||
"label": "Use Coins",
|
||||
"hint": "test"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DualityRollColor": {
|
||||
|
|
@ -132,10 +137,6 @@
|
|||
"AttackTargetDoesNotExist": "The target token no longer exists"
|
||||
},
|
||||
"Error": {
|
||||
"NoClassSelected": "Your character has no class selected!",
|
||||
"LacksDomain": "Your character doesn't have the domain of the card!",
|
||||
"MaxLoadoutReached": "You can't have any more domain cards at this level!",
|
||||
"DuplicateDomainCard": "You already have a domain card with that name!",
|
||||
"ActionRequiresTarget": "The action requires at least one target",
|
||||
"NoAssignedPlayerCharacter": "You have no assigned character.",
|
||||
"NoSelectedToken": "You have no selected token",
|
||||
|
|
@ -262,46 +263,54 @@
|
|||
"Description": "When an effect makes a creature Restrained, it means they cannot move until this condition is cleared.\nThey can still take actions from their current position."
|
||||
}
|
||||
},
|
||||
"Tiers": {
|
||||
"tier1": "Tier 1",
|
||||
"tier2": "Tier 2",
|
||||
"tier3": "Tier 3",
|
||||
"tier4": "Tier 4"
|
||||
},
|
||||
"Adversary": {
|
||||
"Bruiser": {
|
||||
"Name": "Bruiser",
|
||||
"Description": "Tough adversaries with powerful attacks."
|
||||
},
|
||||
"Horde": {
|
||||
"Name": "Horde",
|
||||
"Description": "A Horde represents a number of foes working in a group."
|
||||
},
|
||||
"Leader": {
|
||||
"Name": "Leader",
|
||||
"Description": "Adversaries that command and summon other adversaries."
|
||||
},
|
||||
"Minion": {
|
||||
"Name": "Minion",
|
||||
"Description": "Basic enemies that are easily dispatched but dangerous in numbers."
|
||||
},
|
||||
"Ranged": {
|
||||
"Name": "Ranged",
|
||||
"Description": "Adversaries that attack from a distance."
|
||||
},
|
||||
"Skulker": {
|
||||
"Name": "Skulker",
|
||||
"Description": "Adversaries that maneuver and exploit opportunities to ambush their opponents."
|
||||
},
|
||||
"Social": {
|
||||
"Name": "Social",
|
||||
"Description": "Adversaries that are primarily interpersonal threats or challenges."
|
||||
},
|
||||
"Solo": {
|
||||
"Name": "Solo",
|
||||
"Description": "Designed to present a challenge to a whole party."
|
||||
},
|
||||
"Standard": {
|
||||
"Name": "Standard",
|
||||
"Description": "Rank and File adversaries."
|
||||
},
|
||||
"Support": {
|
||||
"Name": "Support",
|
||||
"Description": "Enemies that enhance their allies and/or disrupt their opponents."
|
||||
"Type": {
|
||||
"Bruiser": {
|
||||
"label": "Bruiser",
|
||||
"Description": "Tough adversaries with powerful attacks."
|
||||
},
|
||||
"Horde": {
|
||||
"label": "Horde",
|
||||
"Description": "A Horde represents a number of foes working in a group."
|
||||
},
|
||||
"Leader": {
|
||||
"label": "Leader",
|
||||
"Description": "Adversaries that command and summon other adversaries."
|
||||
},
|
||||
"Minion": {
|
||||
"label": "Minion",
|
||||
"Description": "Basic enemies that are easily dispatched but dangerous in numbers."
|
||||
},
|
||||
"Ranged": {
|
||||
"label": "Ranged",
|
||||
"Description": "Adversaries that attack from a distance."
|
||||
},
|
||||
"Skulk": {
|
||||
"label": "Skulk",
|
||||
"Description": "Adversaries that maneuver and exploit opportunities to ambush their opponents."
|
||||
},
|
||||
"Social": {
|
||||
"label": "Social",
|
||||
"Description": "Adversaries that are primarily interpersonal threats or challenges."
|
||||
},
|
||||
"Solo": {
|
||||
"label": "Solo",
|
||||
"Description": "Designed to present a challenge to a whole party."
|
||||
},
|
||||
"Standard": {
|
||||
"label": "Standard",
|
||||
"Description": "Rank and File adversaries."
|
||||
},
|
||||
"Support": {
|
||||
"label": "Support",
|
||||
"Description": "Enemies that enhance their allies and/or disrupt their opponents."
|
||||
}
|
||||
},
|
||||
"Trait": {
|
||||
"Relentless": {
|
||||
|
|
@ -321,6 +330,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Environment": {
|
||||
"Type": {
|
||||
"Exploration": {
|
||||
"label": "Exploration",
|
||||
"description": ""
|
||||
},
|
||||
"Social": {
|
||||
"label": "Social",
|
||||
"description": ""
|
||||
},
|
||||
"Traversal": {
|
||||
"label": "Traversal",
|
||||
"description": ""
|
||||
},
|
||||
"Event": {
|
||||
"label": "Event",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"Domains": {
|
||||
"Arcana": {
|
||||
"Description": "This is the domain of the innate or instinctual use of magic. Those who walk this path tap into the raw, enigmatic forces of the realms to manipulate both the elements and their own energy. Arcana offers wielders a volatile power, but it is incredibly potent when correctly channeled."
|
||||
|
|
@ -1004,40 +1033,81 @@
|
|||
}
|
||||
},
|
||||
"Adversary": {
|
||||
"Description": "Description",
|
||||
"MotivesAndTactics": "Motives & Tactics",
|
||||
"Tier": "Tier",
|
||||
"Type": "Type",
|
||||
"Attack": {
|
||||
"Title": "Attack",
|
||||
"Modifier": "Attack Modifier",
|
||||
"Name": "Name",
|
||||
"Range": "Range",
|
||||
"Damage": {
|
||||
"Title": "Damage",
|
||||
"Value": "Value",
|
||||
"Type": "Type"
|
||||
"FIELDS": {
|
||||
"tier": { "label": "Tier" },
|
||||
"type": { "label": "Type" },
|
||||
"description": { "label": "Description" },
|
||||
"motivesAndTactics": { "label": "Motives & Tactics" },
|
||||
"difficulty": { "label": "Difficulty" },
|
||||
"damageThresholds": {
|
||||
"major": { "label": "Major" },
|
||||
"severe": { "label": "Severe" }
|
||||
},
|
||||
"resources": {
|
||||
"hitPoints": {
|
||||
"value": { "label": "Current" },
|
||||
"max": { "label": "Max" }
|
||||
},
|
||||
"stress": {
|
||||
"value": { "label": "Current" },
|
||||
"max": { "label": "Max" }
|
||||
}
|
||||
},
|
||||
"experiences": {
|
||||
"element": {
|
||||
"name": { "label": "Name" },
|
||||
"value": { "label": "Modifier" }
|
||||
}
|
||||
},
|
||||
"attack": {
|
||||
"name": { "label": "Name" },
|
||||
"modifier": { "label": "Modifier" },
|
||||
"range": { "label": "Range" },
|
||||
"damage": {
|
||||
"value": { "label": "Damage" },
|
||||
"type": { "label": "Damage Type" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"Difficulty": "Difficulty",
|
||||
"Reaction": "Reaction Roll",
|
||||
"DamageThresholds": {
|
||||
"Title": "Damage Thresholds",
|
||||
"Minor": "Minor",
|
||||
"Major": "Major",
|
||||
"Severe": "Severe"
|
||||
"Tabs": {
|
||||
"Main": "Data",
|
||||
"Information": "Information"
|
||||
},
|
||||
"HP": "HP",
|
||||
"General": "General",
|
||||
"DamageThresholds": "Damage Thresholds",
|
||||
"HitPoints": "Hit Points",
|
||||
"Stress": "Stress",
|
||||
"Experience": "Experience",
|
||||
"Experiences": "Experiences",
|
||||
"Features": "Features",
|
||||
"NewFeature": "New Feature"
|
||||
"Attack": "Attack"
|
||||
},
|
||||
"Environment": {
|
||||
"ToneAndFeel": "Tone And feel",
|
||||
"PotentialAdversaries": "Potential Adversaries",
|
||||
"NewFeature": "New Feature"
|
||||
"FIELDS": {
|
||||
"tier": {
|
||||
"label": "Tier"
|
||||
},
|
||||
"type": {
|
||||
"label": "Type"
|
||||
},
|
||||
"difficulty": {
|
||||
"label": "Difficulty"
|
||||
}
|
||||
},
|
||||
"Tabs": {
|
||||
"Main": "Data",
|
||||
"Information": "Information"
|
||||
},
|
||||
"general": "General",
|
||||
"newAdversary": "New Adversary",
|
||||
"newFeature": "New feature",
|
||||
"description": "Description",
|
||||
"impulses": "Impulses",
|
||||
"potentialAdversaries": {
|
||||
"label": "Potential Adversaries",
|
||||
"placeholder": "Optionally drag and drop adversaries here"
|
||||
},
|
||||
"features": {
|
||||
"label": "Features"
|
||||
}
|
||||
},
|
||||
"Armor": {
|
||||
"baseScore": "Base Score",
|
||||
|
|
@ -1183,6 +1253,18 @@
|
|||
"Description": "Description"
|
||||
}
|
||||
},
|
||||
"Item": {
|
||||
"Errors": {
|
||||
"MissingClass": "The character is missing a class",
|
||||
"SubclassNotInClass": "The subclass does not belong to the character's class",
|
||||
"ClassAlreadySelected": "The character already has a class",
|
||||
"SubclassAlreadySelected": "The character already has a subclass for that class.",
|
||||
"NoClassSelected": "Your character has no class selected!",
|
||||
"LacksDomain": "Your character doesn't have the domain of the card!",
|
||||
"MaxLoadoutReached": "You can't have any more domain cards at this level!",
|
||||
"DuplicateDomainCard": "You already have a domain card with that name!"
|
||||
}
|
||||
},
|
||||
"Effects": {
|
||||
"Types": {
|
||||
"health": {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export { default as DhpPCSheet } from './sheets/pc.mjs';
|
||||
export { default as DhCharacterSheet } from './sheets/character.mjs';
|
||||
export { default as DhpAdversarySheet } from './sheets/adversary.mjs';
|
||||
export { default as DhpClassSheet } from './sheets/items/class.mjs';
|
||||
export { default as DhpSubclass } from './sheets/items/subclass.mjs';
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
import { DualityRollColor } from '../data/settings/Appearance.mjs';
|
||||
import DHDualityRoll from "../data/chat-message/dualityRoll.mjs";
|
||||
import DHDualityRoll from '../data/chat-message/dualityRoll.mjs';
|
||||
|
||||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||
async renderHTML() {
|
||||
if (this.type === 'dualityRoll' || this.type === 'adversaryRoll' || this.type === 'abilityUse') {
|
||||
this.content = await foundry.applications.handlebars.renderTemplate(this.content, this.system);
|
||||
}
|
||||
|
||||
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
||||
const html = await super.renderHTML();
|
||||
|
||||
|
|
|
|||
|
|
@ -149,7 +149,10 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
const experienceIncreaseValues = experienceIncreases
|
||||
.filter(exp => exp.data.length > 0)
|
||||
.flatMap(exp =>
|
||||
exp.data.map(data => this.actor.system.experiences.find(x => x.id === data).description)
|
||||
exp.data.map(data => {
|
||||
const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
|
||||
return this.actor.system.experiences[experience].description;
|
||||
})
|
||||
);
|
||||
context.experienceIncreases = {
|
||||
values: experienceIncreaseValues,
|
||||
|
|
@ -201,7 +204,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
|
||||
const multiclassSubclass = this.actor.system.multiclass?.system?.subclasses?.[0];
|
||||
const possibleSubclasses = [
|
||||
this.actor.system.subclass,
|
||||
this.actor.system.class.subclass,
|
||||
...(multiclassSubclass ? [multiclassSubclass] : [])
|
||||
];
|
||||
const selectedSubclasses = possibleSubclasses.filter(x => subclassSelections.includes(x.uuid));
|
||||
|
|
@ -230,7 +233,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
domains:
|
||||
multiclass?.system?.domains.map(key => {
|
||||
const domain = domains[key];
|
||||
const alreadySelected = this.actor.system.class.system.domains.includes(key);
|
||||
const alreadySelected = this.actor.system.class.value.system.domains.includes(key);
|
||||
|
||||
return {
|
||||
...domain,
|
||||
|
|
@ -274,8 +277,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
|
||||
context.achievements = {
|
||||
proficiency: {
|
||||
old: this.actor.system.proficiency.value,
|
||||
new: this.actor.system.proficiency.value + achivementProficiency,
|
||||
old: this.actor.system.proficiency,
|
||||
new: this.actor.system.proficiency + achivementProficiency,
|
||||
shown: achivementProficiency > 0
|
||||
},
|
||||
damageThresholds: {
|
||||
|
|
@ -328,10 +331,12 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
break;
|
||||
case 'experience':
|
||||
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||
const data = checkbox.data.map(
|
||||
data =>
|
||||
this.actor.system.experiences.find(x => x.id === data)?.description ?? ''
|
||||
);
|
||||
const data = checkbox.data.map(data => {
|
||||
const experience = Object.keys(this.actor.system.experiences).find(
|
||||
x => x === data
|
||||
);
|
||||
return this.actor.system.experiences[experience]?.description ?? '';
|
||||
});
|
||||
advancement[choiceKey].push({ data: data, value: checkbox.value });
|
||||
break;
|
||||
}
|
||||
|
|
@ -354,8 +359,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
new: this.actor.system.resources.stress.max + (advancement.stress ?? 0)
|
||||
},
|
||||
evasion: {
|
||||
old: this.actor.system.evasion.value,
|
||||
new: this.actor.system.evasion.value + (advancement.evasion ?? 0)
|
||||
old: this.actor.system.evasion,
|
||||
new: this.actor.system.evasion + (advancement.evasion ?? 0)
|
||||
}
|
||||
},
|
||||
traits:
|
||||
|
|
@ -421,8 +426,9 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
if (experienceIncreaseTagify) {
|
||||
tagifyElement(
|
||||
experienceIncreaseTagify,
|
||||
this.actor.system.experiences.reduce((acc, experience) => {
|
||||
acc[experience.id] = { label: experience.description };
|
||||
Object.keys(this.actor.system.experiences).reduce((acc, id) => {
|
||||
const experience = this.actor.system.experiences[id];
|
||||
acc[id] = { label: experience.description };
|
||||
|
||||
return acc;
|
||||
}, {}),
|
||||
|
|
@ -480,7 +486,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
const target = event.target.closest('.card-preview-container');
|
||||
if (item.type === 'domainCard') {
|
||||
if (
|
||||
!this.actor.system.class.system.domains.includes(item.system.domain) &&
|
||||
!this.actor.system.class.value.system.domains.includes(item.system.domain) &&
|
||||
this.levelup.classUpgradeChoices?.multiclass?.domain !== item.system.domain
|
||||
) {
|
||||
ui.notifications.error(
|
||||
|
|
@ -522,7 +528,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
} else if (event.target.closest('.multiclass-cards')) {
|
||||
const target = event.target.closest('.multiclass-cards');
|
||||
if (item.type === 'class') {
|
||||
if (item.name === this.actor.system.class.name) {
|
||||
if (item.name === this.actor.system.class.value.name) {
|
||||
ui.notifications.error(
|
||||
game.i18n.localize('DAGGERHEART.Application.LevelUp.notifications.error.alreadySelectedClass')
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,115 +0,0 @@
|
|||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhpMulticlassDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(actorName, actorClass, resolve) {
|
||||
super({});
|
||||
|
||||
this.actorName = actorName;
|
||||
this.actorClass = actorClass;
|
||||
this.resolve = resolve;
|
||||
|
||||
this.classChoices = Array.from(
|
||||
game.items.reduce((acc, x) => {
|
||||
if (x.type === 'class' && x.name !== actorClass.name) {
|
||||
acc.add(x);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, new Set())
|
||||
);
|
||||
this.subclassChoices = [];
|
||||
this.domainChoices = [];
|
||||
|
||||
this.data = {
|
||||
class: null,
|
||||
subclass: null,
|
||||
domain: null
|
||||
};
|
||||
}
|
||||
|
||||
get title() {
|
||||
return `${this.actorName} - Multiclass`;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['daggerheart', 'views', 'multiclass'],
|
||||
position: { width: 600, height: 'auto' },
|
||||
actions: {
|
||||
selectClass: this.selectClass,
|
||||
selectSubclass: this.selectSubclass,
|
||||
selectDomain: this.selectDomain,
|
||||
finish: this.finish
|
||||
}
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
form: {
|
||||
id: 'levelup',
|
||||
template: 'systems/daggerheart/templates/views/multiclass.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.classChoices = this.classChoices;
|
||||
context.subclassChoices = this.subclassChoices;
|
||||
context.domainChoices = this.domainChoices;
|
||||
context.disabledFinish = !this.data.class || !this.data.subclass || !this.data.domain;
|
||||
context.data = this.data;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async selectClass(_, button) {
|
||||
const oldClass = this.data.class;
|
||||
this.data.class = this.data.class?.uuid === button.dataset.class ? null : await fromUuid(button.dataset.class);
|
||||
if (oldClass !== button.dataset.class) {
|
||||
this.data.subclass = null;
|
||||
this.data.domain = null;
|
||||
this.subclassChoices = this.data.class ? this.data.class.system.subclasses : [];
|
||||
|
||||
//FIXME
|
||||
this.domainChoices = this.data.class
|
||||
? this.data.class.system.domains.map(x => {
|
||||
const config = SYSTEM.DOMAIN.domains[x];
|
||||
return {
|
||||
name: game.i18n.localize(config.name),
|
||||
id: config.id,
|
||||
img: config.src,
|
||||
disabled: this.actorClass.system.domains.includes(config.id)
|
||||
};
|
||||
})
|
||||
: [];
|
||||
}
|
||||
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
static async selectSubclass(_, button) {
|
||||
this.data.subclass =
|
||||
this.data.subclass?.uuid === button.dataset.subclass
|
||||
? null
|
||||
: this.subclassChoices.find(x => x.uuid === button.dataset.subclass);
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
static async selectDomain(_, button) {
|
||||
const domain =
|
||||
this.data.domain?.id === button.dataset.domain
|
||||
? null
|
||||
: this.domainChoices.find(x => x.id === button.dataset.domain);
|
||||
if (domain?.disabled) return;
|
||||
|
||||
this.data.domain = domain;
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
static finish() {
|
||||
this.close({}, this.data);
|
||||
}
|
||||
|
||||
async close(options = {}, data = null) {
|
||||
this.resolve(data);
|
||||
super.close(options);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(experiences, bonusDamage, hopeResource, resolve, isNpc) {
|
||||
constructor(experiences, hopeResource, resolve) {
|
||||
super({}, {});
|
||||
|
||||
this.experiences = experiences;
|
||||
|
|
@ -17,23 +17,13 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
|||
fear: ['d12'],
|
||||
advantage: null,
|
||||
disadvantage: null,
|
||||
bonusDamage: bonusDamage.reduce((acc, x) => {
|
||||
if (x.appliesOn === SYSTEM.EFFECTS.applyLocations.attackRoll.id) {
|
||||
acc.push({
|
||||
...x,
|
||||
hopeUses: 0
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []),
|
||||
hopeResource: hopeResource
|
||||
};
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'roll-selection', //Having an id causes a new instance to overwrite previous.
|
||||
id: 'roll-selection',
|
||||
classes: ['daggerheart', 'views', 'roll-selection'],
|
||||
position: {
|
||||
width: 400,
|
||||
|
|
@ -41,8 +31,6 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
|||
},
|
||||
actions: {
|
||||
selectExperience: this.selectExperience,
|
||||
decreaseHopeUse: this.decreaseHopeUse,
|
||||
increaseHopeUse: this.increaseHopeUse,
|
||||
setAdvantage: this.setAdvantage,
|
||||
setDisadvantage: this.setDisadvantage,
|
||||
finish: this.finish
|
||||
|
|
@ -74,27 +62,14 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
|||
context.fear = this.data.fear;
|
||||
context.advantage = this.data.advantage;
|
||||
context.disadvantage = this.data.disadvantage;
|
||||
context.experiences = this.experiences.map(x => ({
|
||||
...x,
|
||||
selected: this.selectedExperiences.find(selected => selected.id === x.id)
|
||||
}));
|
||||
context.bonusDamage = this.data.bonusDamage;
|
||||
context.experiences = Object.keys(this.experiences).map(id => ({ id, ...this.experiences[id] }));
|
||||
context.hopeResource = this.data.hopeResource + 1;
|
||||
context.hopeUsed = this.getHopeUsed();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static updateSelection(event, _, formData) {
|
||||
const { bonusDamage, ...rest } = foundry.utils.expandObject(formData.object);
|
||||
|
||||
for (var index in bonusDamage) {
|
||||
this.data.bonusDamage[index].initiallySelected = bonusDamage[index].initiallySelected;
|
||||
if (bonusDamage[index].hopeUses) {
|
||||
const value = Number.parseInt(bonusDamage[index].hopeUses);
|
||||
if (!Number.isNaN(value)) this.data.bonusDamage[index].hopeUses = value;
|
||||
}
|
||||
}
|
||||
const { ...rest } = foundry.utils.expandObject(formData.object);
|
||||
|
||||
this.data = foundry.utils.mergeObject(this.data, rest);
|
||||
this.render();
|
||||
|
|
@ -104,35 +79,12 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
|||
if (this.selectedExperiences.find(x => x.id === button.dataset.key)) {
|
||||
this.selectedExperiences = this.selectedExperiences.filter(x => x.id !== button.dataset.key);
|
||||
} else {
|
||||
this.selectedExperiences = [
|
||||
...this.selectedExperiences,
|
||||
this.experiences.find(x => x.id === button.dataset.key)
|
||||
];
|
||||
this.selectedExperiences = [...this.selectedExperiences, button.dataset.key];
|
||||
}
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
getHopeUsed() {
|
||||
return this.data.bonusDamage.reduce((acc, x) => acc + x.hopeUses, 0);
|
||||
}
|
||||
|
||||
static decreaseHopeUse(_, button) {
|
||||
const index = Number.parseInt(button.dataset.index);
|
||||
if (this.data.bonusDamage[index].hopeUses - 1 >= 0) {
|
||||
this.data.bonusDamage[index].hopeUses -= 1;
|
||||
this.render(true);
|
||||
}
|
||||
}
|
||||
|
||||
static increaseHopeUse(_, button) {
|
||||
const index = Number.parseInt(button.dataset.index);
|
||||
if (this.data.bonusDamage[index].hopeUses <= this.data.hopeResource + 1) {
|
||||
this.data.bonusDamage[index].hopeUses += 1;
|
||||
this.render(true);
|
||||
}
|
||||
}
|
||||
|
||||
static setAdvantage() {
|
||||
this.data.advantage = this.data.advantage ? null : 'd6';
|
||||
this.data.disadvantage = null;
|
||||
|
|
@ -149,11 +101,10 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
|||
|
||||
static async finish() {
|
||||
const { diceOptions, ...rest } = this.data;
|
||||
|
||||
this.resolve({
|
||||
...rest,
|
||||
experiences: this.selectedExperiences,
|
||||
hopeUsed: this.getHopeUsed(),
|
||||
bonusDamage: this.data.bonusDamage.reduce((acc, x) => acc.concat(` + ${1 + x.hopeUses}${x.value}`), '')
|
||||
experiences: this.selectedExperiences.map(x => ({ id: x, ...this.experiences[x] }))
|
||||
});
|
||||
this.close();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,219 +1,12 @@
|
|||
// import DhpApplicationMixin from '../daggerheart-sheet.mjs';
|
||||
|
||||
// export class Teest extends DhpApplicationMixin(ActorSheet) {
|
||||
// static documentType = "adversary";
|
||||
|
||||
// constructor(options){
|
||||
// super(options);
|
||||
|
||||
// this.editMode = false;
|
||||
// }
|
||||
|
||||
// /** @override */
|
||||
// static get defaultOptions() {
|
||||
// return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
// classes: ["daggerheart", "sheet", "adversary"],
|
||||
// width: 600,
|
||||
// height: 'auto',
|
||||
// resizable: false,
|
||||
// });
|
||||
// }
|
||||
|
||||
// async getData() {
|
||||
// const context = super.getData();
|
||||
// context.config = SYSTEM;
|
||||
// context.editMode = this.editMode;
|
||||
// context.title = `${this.actor.name} - ${game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.actor.system.type].name)}`;
|
||||
|
||||
// context.data = {
|
||||
// description: this.object.system.description,
|
||||
// motivesAndTactics: this.object.system.motivesAndTactics.join(', '),
|
||||
// tier: this.object.system.tier,
|
||||
// type: game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.object.system.type].name),
|
||||
// attack: {
|
||||
// name: this.object.system.attack.name,
|
||||
// attackModifier: this.object.system.attackModifier,
|
||||
// range: this.object.system.attack.range ? game.i18n.localize(SYSTEM.GENERAL.range[this.object.system.attack.range].name) : null,
|
||||
// damage: {
|
||||
// value: this.object.system.attack.damage.value,
|
||||
// type: this.object.system.attack.damage.type,
|
||||
// typeName: this.object.system.attack.damage.type ? game.i18n.localize(SYSTEM.GENERAL.damageTypes[this.object.system.attack.damage.type].abbreviation).toLowerCase() : null,
|
||||
// },
|
||||
// },
|
||||
// damageThresholds: this.object.system.damageThresholds,
|
||||
// difficulty: this.object.system.difficulty,
|
||||
// hp: { ...this.object.system.resources.health, lastRowIndex: Math.floor(this.object.system.resources.health.max/5)*5 },
|
||||
// stress: { ...this.object.system.resources.stress, lastRowIndex: Math.floor(this.object.system.resources.stress.max/5)*5 },
|
||||
// moves: this.object.system.moves,
|
||||
// };
|
||||
|
||||
// return context;
|
||||
// }
|
||||
|
||||
// async _handleAction(action, event, button) {
|
||||
// switch(action){
|
||||
// case 'viewMove':
|
||||
// await this.viewMove(button);
|
||||
// break;
|
||||
// case 'addMove':
|
||||
// this.addMove();
|
||||
// break;
|
||||
// case 'removeMove':
|
||||
// await this.removeMove(button);
|
||||
// break;
|
||||
// case 'toggleSlider':
|
||||
// this.toggleEditMode();
|
||||
// break;
|
||||
// case 'addMotive':
|
||||
// await this.addMotive();
|
||||
// break;
|
||||
// case 'removeMotive':
|
||||
// await this.removeMotive(button);
|
||||
// break;
|
||||
// case 'reactionRoll':
|
||||
// await this.reactionRoll(event);
|
||||
// break;
|
||||
// case 'attackRoll':
|
||||
// await this.attackRoll(event);
|
||||
// break;
|
||||
// case 'addExperience':
|
||||
// await this.addExperience();
|
||||
// break;
|
||||
// case 'removeExperience':
|
||||
// await this.removeExperience(button);
|
||||
// break;
|
||||
// case 'toggleHP':
|
||||
// await this.toggleHP(button);
|
||||
// break;
|
||||
// case 'toggleStress':
|
||||
// await this.toggleStress(button);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// async viewMove(button){
|
||||
// const move = await fromUuid(button.dataset.move);
|
||||
// move.sheet.render(true);
|
||||
// }
|
||||
|
||||
// async addMove(){
|
||||
// const result = await this.object.createEmbeddedDocuments("Item", [{
|
||||
// name: game.i18n.localize('DAGGERHEART.Sheets.Adversary.NewMove'),
|
||||
// type: 'feature',
|
||||
// }]);
|
||||
|
||||
// await result[0].sheet.render(true);
|
||||
// }
|
||||
|
||||
// async removeMove(button){
|
||||
// await this.object.items.find(x => x.uuid === button.dataset.move).delete();
|
||||
// }
|
||||
|
||||
// toggleEditMode(){
|
||||
// this.editMode = !this.editMode;
|
||||
// this.render();
|
||||
// }
|
||||
|
||||
// async addMotive(){
|
||||
// await this.object.update({ "system.motivesAndTactics": [...this.object.system.motivesAndTactics, ''] });
|
||||
// }
|
||||
|
||||
// async removeMotive(button){
|
||||
// await this.object.update({ "system.motivesAndTactics": this.object.system.motivesAndTactics.filter((_, index) => index !== Number.parseInt(button.dataset.motive) )});
|
||||
// }
|
||||
|
||||
// async reactionRoll(event){
|
||||
// const { roll, diceResults, modifiers } = await this.actor.diceRoll({ title: `${this.actor.name} - Reaction Roll`, value: 0 }, event.shiftKey);
|
||||
|
||||
// const cls = getDocumentClass("ChatMessage");
|
||||
// const msg = new cls({
|
||||
// type: 'adversaryRoll',
|
||||
// system: {
|
||||
// roll: roll._formula,
|
||||
// total: roll._total,
|
||||
// modifiers: modifiers,
|
||||
// diceResults: diceResults,
|
||||
// },
|
||||
// content: "systems/daggerheart/templates/chat/adversary-roll.hbs",
|
||||
// rolls: [roll]
|
||||
// });
|
||||
|
||||
// cls.create(msg.toObject());
|
||||
// }
|
||||
|
||||
// async attackRoll(event){
|
||||
// const modifier = Number.parseInt(event.currentTarget.dataset.value);
|
||||
|
||||
// const { roll, diceResults, modifiers } = await this.actor.diceRoll({ title: `${this.actor.name} - Attack Roll`, value: modifier }, event.shiftKey);
|
||||
|
||||
// const targets = Array.from(game.user.targets).map(x => ({
|
||||
// id: x.id,
|
||||
// name: x.actor.name,
|
||||
// img: x.actor.img,
|
||||
// difficulty: x.actor.system.difficulty,
|
||||
// evasion: x.actor.system.evasion,
|
||||
// }));
|
||||
|
||||
// const cls = getDocumentClass("ChatMessage");
|
||||
// const msg = new cls({
|
||||
// type: 'adversaryRoll',
|
||||
// system: {
|
||||
// roll: roll._formula,
|
||||
// total: roll._total,
|
||||
// modifiers: modifiers,
|
||||
// diceResults: diceResults,
|
||||
// targets: targets,
|
||||
// damage: { value: event.currentTarget.dataset.damage, type: event.currentTarget.dataset.damageType },
|
||||
// },
|
||||
// content: "systems/daggerheart/templates/chat/adversary-attack-roll.hbs",
|
||||
// rolls: [roll]
|
||||
// });
|
||||
|
||||
// cls.create(msg.toObject());
|
||||
// }
|
||||
|
||||
// async addExperience(){
|
||||
// await this.object.update({ "system.experiences": [...this.object.system.experiences, { name: 'Experience', value: 1 }] });
|
||||
// }
|
||||
|
||||
// async removeExperience(button){
|
||||
// await this.object.update({ "system.experiences": this.object.system.experiences.filter((_, index) => index !== Number.parseInt(button.dataset.experience) )});
|
||||
// }
|
||||
|
||||
// async toggleHP(button){
|
||||
// const index = Number.parseInt(button.dataset.index);
|
||||
// const newHP = index < this.object.system.resources.health.value ? index : index+1;
|
||||
// await this.object.update({ "system.resources.health.value": newHP });
|
||||
// }
|
||||
|
||||
// async toggleStress(button){
|
||||
// const index = Number.parseInt(button.dataset.index);
|
||||
// const newStress = index < this.object.system.resources.stress.value ? index : index+1;
|
||||
// await this.object.update({ "system.resources.stress.value": newStress });
|
||||
// }
|
||||
// }
|
||||
|
||||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
|
||||
this.editMode = false;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'sheet', 'adversary'],
|
||||
position: { width: 600 },
|
||||
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'adversary'],
|
||||
position: { width: 450, height: 1000 },
|
||||
actions: {
|
||||
viewMove: this.viewMove,
|
||||
addMove: this.addMove,
|
||||
removeMove: this.removeMove,
|
||||
toggleSlider: this.toggleEditMode,
|
||||
addMotive: this.addMotive,
|
||||
removeMotive: this.removeMotive,
|
||||
reactionRoll: this.reactionRoll,
|
||||
attackRoll: this.attackRoll,
|
||||
addExperience: this.addExperience,
|
||||
|
|
@ -229,54 +22,35 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
};
|
||||
|
||||
static PARTS = {
|
||||
form: {
|
||||
id: 'feature',
|
||||
template: 'systems/daggerheart/templates/sheets/adversary.hbs'
|
||||
header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' },
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
main: { template: 'systems/daggerheart/templates/sheets/actors/adversary/main.hbs' },
|
||||
information: { template: 'systems/daggerheart/templates/sheets/actors/adversary/information.hbs' }
|
||||
};
|
||||
|
||||
static TABS = {
|
||||
main: {
|
||||
active: true,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'main',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.Sheets.Adversary.Tabs.Main'
|
||||
},
|
||||
information: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'information',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.Sheets.Adversary.Tabs.Information'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.document = this.document;
|
||||
context.config = SYSTEM;
|
||||
context.editMode = this.editMode;
|
||||
context.title = `${this.actor.name} - ${game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.actor.system.type].name)}`;
|
||||
|
||||
context.data = {
|
||||
description: this.document.system.description,
|
||||
motivesAndTactics: this.document.system.motivesAndTactics.join(', '),
|
||||
tier: this.document.system.tier,
|
||||
type: game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.document.system.type].name),
|
||||
attack: {
|
||||
name: this.document.system.attack.name,
|
||||
attackModifier: this.document.system.attackModifier,
|
||||
range: this.document.system.attack.range
|
||||
? game.i18n.localize(SYSTEM.GENERAL.range[this.document.system.attack.range].name)
|
||||
: null,
|
||||
damage: {
|
||||
value: this.document.system.attack.damage.value,
|
||||
type: this.document.system.attack.damage.type,
|
||||
typeName: this.document.system.attack.damage.type
|
||||
? game.i18n
|
||||
.localize(
|
||||
SYSTEM.GENERAL.damageTypes[this.document.system.attack.damage.type].abbreviation
|
||||
)
|
||||
.toLowerCase()
|
||||
: null
|
||||
}
|
||||
},
|
||||
damageThresholds: this.document.system.damageThresholds,
|
||||
difficulty: this.document.system.difficulty,
|
||||
hp: {
|
||||
...this.document.system.resources.health,
|
||||
lastRowIndex: Math.floor(this.document.system.resources.health.max / 5) * 5
|
||||
},
|
||||
stress: {
|
||||
...this.document.system.resources.stress,
|
||||
lastRowIndex: Math.floor(this.document.system.resources.stress.max / 5) * 5
|
||||
},
|
||||
moves: this.document.system.moves
|
||||
};
|
||||
context.tabs = super._getTabs(this.constructor.TABS);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
@ -286,43 +60,6 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
this.render();
|
||||
}
|
||||
|
||||
static async viewMove(_, button) {
|
||||
const move = await fromUuid(button.dataset.move);
|
||||
move.sheet.render(true);
|
||||
}
|
||||
|
||||
static async addMove() {
|
||||
const result = await this.document.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
name: game.i18n.localize('DAGGERHEART.Sheets.Adversary.NewMove'),
|
||||
type: 'feature'
|
||||
}
|
||||
]);
|
||||
|
||||
await result[0].sheet.render(true);
|
||||
}
|
||||
|
||||
static async removeMove(_, button) {
|
||||
await this.document.items.find(x => x.uuid === button.dataset.move).delete();
|
||||
}
|
||||
|
||||
static toggleEditMode() {
|
||||
this.editMode = !this.editMode;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async addMotive() {
|
||||
await this.document.update({ 'system.motivesAndTactics': [...this.document.system.motivesAndTactics, ''] });
|
||||
}
|
||||
|
||||
static async removeMotive(button) {
|
||||
await this.document.update({
|
||||
'system.motivesAndTactics': this.document.system.motivesAndTactics.filter(
|
||||
(_, index) => index !== Number.parseInt(button.dataset.motive)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
static async reactionRoll(event) {
|
||||
const { roll, diceResults, modifiers } = await this.actor.diceRoll(
|
||||
{ title: `${this.actor.name} - Reaction Roll`, value: 0 },
|
||||
|
|
@ -349,9 +86,8 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
static async attackRoll(event, button) {
|
||||
const modifier = Number.parseInt(button.dataset.value);
|
||||
|
||||
static async attackRoll() {
|
||||
const { modifier, damage, name: attackName } = this.actor.system.attack;
|
||||
const { roll, dice, advantageState, modifiers } = await this.actor.diceRoll(
|
||||
{ title: `${this.actor.name} - Attack Roll`, value: modifier },
|
||||
event.shiftKey
|
||||
|
|
@ -362,12 +98,12 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
name: x.actor.name,
|
||||
img: x.actor.img,
|
||||
difficulty: x.actor.system.difficulty,
|
||||
evasion: x.actor.system.evasion.value
|
||||
evasion: x.actor.system.evasion
|
||||
}));
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
title: button.dataset.name,
|
||||
title: attackName,
|
||||
origin: this.document.id,
|
||||
roll: roll._formula,
|
||||
advantageState,
|
||||
|
|
@ -375,7 +111,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
modifiers: modifiers,
|
||||
dice: dice,
|
||||
targets: targets,
|
||||
damage: { value: button.dataset.damage, type: button.dataset.damageType }
|
||||
damage: { value: damage.value, type: damage.type }
|
||||
};
|
||||
const msg = new cls({
|
||||
type: 'adversaryRoll',
|
||||
|
|
|
|||
703
module/applications/sheets/character.mjs
Normal file
703
module/applications/sheets/character.mjs
Normal file
|
|
@ -0,0 +1,703 @@
|
|||
import { capitalize } from '../../helpers/utils.mjs';
|
||||
import DhpDeathMove from '../deathMove.mjs';
|
||||
import DhpDowntime from '../downtime.mjs';
|
||||
import AncestrySelectionDialog from '../ancestrySelectionDialog.mjs';
|
||||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||
import { abilities } from '../../config/actorConfig.mjs';
|
||||
import DhlevelUp from '../levelup.mjs';
|
||||
import DHDualityRoll from '../../data/chat-message/dualityRoll.mjs';
|
||||
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
const { TextEditor } = foundry.applications.ux;
|
||||
export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'sheet', 'pc'],
|
||||
position: { width: 810, height: 1080 },
|
||||
actions: {
|
||||
attributeRoll: this.rollAttribute,
|
||||
toggleMarks: this.toggleMarks,
|
||||
toggleHP: this.toggleHP,
|
||||
toggleStress: this.toggleStress,
|
||||
toggleHope: this.toggleHope,
|
||||
toggleGold: this.toggleGold,
|
||||
attackRoll: this.attackRoll,
|
||||
useDomainCard: this.useDomainCard,
|
||||
removeCard: this.removeDomainCard,
|
||||
selectClass: this.selectClass,
|
||||
selectSubclass: this.selectSubclass,
|
||||
selectAncestry: this.selectAncestry,
|
||||
selectCommunity: this.selectCommunity,
|
||||
viewObject: this.viewObject,
|
||||
useFeature: this.useFeature,
|
||||
takeShortRest: this.takeShortRest,
|
||||
takeLongRest: this.takeLongRest,
|
||||
deleteItem: this.deleteItem,
|
||||
addScar: this.addScar,
|
||||
deleteScar: this.deleteScar,
|
||||
makeDeathMove: this.makeDeathMove,
|
||||
itemQuantityDecrease: (_, button) => this.setItemQuantity(button, -1),
|
||||
itemQuantityIncrease: (_, button) => this.setItemQuantity(button, 1),
|
||||
useAbility: this.useAbility,
|
||||
useAdvancementCard: this.useAdvancementCard,
|
||||
useAdvancementAbility: this.useAdvancementAbility,
|
||||
toggleEquipItem: this.toggleEquipItem,
|
||||
levelup: this.openLevelUp
|
||||
},
|
||||
window: {
|
||||
minimizable: false,
|
||||
resizable: true
|
||||
},
|
||||
form: {
|
||||
handler: this.updateForm,
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
},
|
||||
dragDrop: [
|
||||
{ dragSelector: null, dropSelector: '.weapon-section' },
|
||||
{ dragSelector: null, dropSelector: '.armor-section' },
|
||||
{ dragSelector: '.item-list .item', dropSelector: null }
|
||||
]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
form: {
|
||||
id: 'character',
|
||||
template: 'systems/daggerheart/templates/sheets/character/character.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
_getTabs() {
|
||||
const setActive = tabs => {
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
|
||||
v.cssClass = v.active ? 'active' : '';
|
||||
}
|
||||
};
|
||||
|
||||
const primaryTabs = {
|
||||
features: {
|
||||
active: true,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'features',
|
||||
icon: null,
|
||||
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Features')
|
||||
},
|
||||
loadout: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'loadout',
|
||||
icon: null,
|
||||
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Loadout')
|
||||
},
|
||||
inventory: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'inventory',
|
||||
icon: null,
|
||||
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Inventory')
|
||||
},
|
||||
story: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'story',
|
||||
icon: null,
|
||||
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Story')
|
||||
}
|
||||
};
|
||||
const secondaryTabs = {
|
||||
foundation: {
|
||||
active: true,
|
||||
cssClass: '',
|
||||
group: 'secondary',
|
||||
id: 'foundation',
|
||||
icon: null,
|
||||
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Foundation')
|
||||
},
|
||||
loadout: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'secondary',
|
||||
id: 'loadout',
|
||||
icon: null,
|
||||
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Loadout')
|
||||
},
|
||||
vault: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'secondary',
|
||||
id: 'vault',
|
||||
icon: null,
|
||||
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Vault')
|
||||
}
|
||||
};
|
||||
|
||||
setActive(primaryTabs);
|
||||
setActive(secondaryTabs);
|
||||
|
||||
return { primary: primaryTabs, secondary: secondaryTabs };
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelector('.level-value').addEventListener('change', this.onLevelChange.bind(this));
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.document = this.document;
|
||||
context.tabs = this._getTabs();
|
||||
|
||||
context.config = SYSTEM;
|
||||
|
||||
const selectedAttributes = Object.values(this.document.system.traits).map(x => x.base);
|
||||
context.abilityScoreArray = JSON.parse(
|
||||
await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray)
|
||||
).reduce((acc, x) => {
|
||||
const selectedIndex = selectedAttributes.indexOf(x);
|
||||
if (selectedIndex !== -1) {
|
||||
selectedAttributes.splice(selectedIndex, 1);
|
||||
} else {
|
||||
acc.push({ name: x, value: x });
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
if (!context.abilityScoreArray.includes(0)) context.abilityScoreArray.push({ name: 0, value: 0 });
|
||||
context.abilityScoresFinished = context.abilityScoreArray.every(x => x.value === 0);
|
||||
|
||||
context.attributes = Object.keys(this.document.system.traits).reduce((acc, key) => {
|
||||
acc[key] = {
|
||||
...this.document.system.traits[key],
|
||||
name: game.i18n.localize(SYSTEM.ACTOR.abilities[key].name),
|
||||
verbs: SYSTEM.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x))
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const ancestry = await this.mapFeatureType(
|
||||
this.document.system.ancestry ? [this.document.system.ancestry] : [],
|
||||
SYSTEM.GENERAL.objectTypes
|
||||
);
|
||||
const community = await this.mapFeatureType(
|
||||
this.document.system.community ? [this.document.system.community] : [],
|
||||
SYSTEM.GENERAL.objectTypes
|
||||
);
|
||||
const foundation = {
|
||||
ancestry: ancestry[0],
|
||||
community: community[0],
|
||||
advancement: {}
|
||||
};
|
||||
|
||||
const nrLoadoutCards = this.document.system.domainCards.loadout.length;
|
||||
const loadout = await this.mapFeatureType(this.document.system.domainCards.loadout, SYSTEM.DOMAIN.cardTypes);
|
||||
const vault = await this.mapFeatureType(this.document.system.domainCards.vault, SYSTEM.DOMAIN.cardTypes);
|
||||
context.abilities = {
|
||||
foundation: foundation,
|
||||
loadout: {
|
||||
top: loadout.slice(0, Math.min(2, nrLoadoutCards)),
|
||||
bottom: nrLoadoutCards > 2 ? loadout.slice(2, Math.min(5, nrLoadoutCards)) : [],
|
||||
nrTotal: nrLoadoutCards
|
||||
},
|
||||
vault: vault.map(x => ({
|
||||
...x,
|
||||
uuid: x.uuid,
|
||||
sendToLoadoutDisabled: this.document.system.domainCards.loadout.length >= 5
|
||||
}))
|
||||
};
|
||||
|
||||
context.inventory = {
|
||||
consumable: {
|
||||
titles: {
|
||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.ConsumableTitle'),
|
||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||
},
|
||||
items: this.document.items.filter(x => x.type === 'consumable')
|
||||
},
|
||||
miscellaneous: {
|
||||
titles: {
|
||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.MiscellaneousTitle'),
|
||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||
},
|
||||
items: this.document.items.filter(x => x.type === 'miscellaneous')
|
||||
},
|
||||
weapons: {
|
||||
titles: {
|
||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.WeaponsTitle'),
|
||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||
},
|
||||
items: this.document.items.filter(x => x.type === 'weapon')
|
||||
},
|
||||
armor: {
|
||||
titles: {
|
||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.ArmorsTitle'),
|
||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||
},
|
||||
items: this.document.items.filter(x => x.type === 'armor')
|
||||
}
|
||||
};
|
||||
|
||||
if (context.inventory.length === 0) {
|
||||
context.inventory = Array(1).fill(Array(5).fill([]));
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateForm(event, _, formData) {
|
||||
await this.document.update(formData.object);
|
||||
this.render();
|
||||
}
|
||||
|
||||
async mapFeatureType(data, configType) {
|
||||
return await Promise.all(
|
||||
data.map(async x => {
|
||||
const abilities = x.system.abilities
|
||||
? await Promise.all(x.system.abilities.map(async x => await fromUuid(x.uuid)))
|
||||
: [];
|
||||
|
||||
return {
|
||||
...x,
|
||||
uuid: x.uuid,
|
||||
system: {
|
||||
...x.system,
|
||||
abilities: abilities,
|
||||
type: game.i18n.localize(configType[x.system.type ?? x.type].label)
|
||||
}
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
static async rollAttribute(event, button) {
|
||||
const { roll, hope, fear, advantage, disadvantage, modifiers } = await this.document.dualityRoll(
|
||||
{ title: game.i18n.localize(abilities[button.dataset.attribute].label), value: button.dataset.value },
|
||||
event.shiftKey
|
||||
);
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
|
||||
const systemContent = new DHDualityRoll({
|
||||
title: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
|
||||
ability: game.i18n.localize(abilities[button.dataset.attribute].label)
|
||||
}),
|
||||
origin: this.document.id,
|
||||
roll: roll._formula,
|
||||
modifiers: modifiers,
|
||||
hope: hope,
|
||||
fear: fear,
|
||||
advantage: advantage,
|
||||
disadvantage: disadvantage
|
||||
});
|
||||
|
||||
await cls.create({
|
||||
type: 'dualityRoll',
|
||||
sound: CONFIG.sounds.dice,
|
||||
system: systemContent,
|
||||
user: game.user.id,
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/duality-roll.hbs',
|
||||
systemContent
|
||||
),
|
||||
rolls: [roll]
|
||||
});
|
||||
}
|
||||
|
||||
static async toggleMarks(_, button) {
|
||||
const markValue = Number.parseInt(button.dataset.value);
|
||||
const newValue = this.document.system.armor.system.marks.value >= markValue ? markValue - 1 : markValue;
|
||||
await this.document.system.armor.update({ 'system.marks.value': newValue });
|
||||
}
|
||||
|
||||
static async toggleHP(_, button) {
|
||||
const healthValue = Number.parseInt(button.dataset.value);
|
||||
const newValue = this.document.system.resources.hitPoints.value >= healthValue ? healthValue - 1 : healthValue;
|
||||
await this.document.update({ 'system.resources.hitPoints.value': newValue });
|
||||
}
|
||||
|
||||
static async toggleStress(_, button) {
|
||||
const healthValue = Number.parseInt(button.dataset.value);
|
||||
const newValue = this.document.system.resources.stress.value >= healthValue ? healthValue - 1 : healthValue;
|
||||
await this.document.update({ 'system.resources.stress.value': newValue });
|
||||
}
|
||||
|
||||
static async toggleHope(_, button) {
|
||||
const hopeValue = Number.parseInt(button.dataset.value);
|
||||
const newValue = this.document.system.resources.hope.value >= hopeValue ? hopeValue - 1 : hopeValue;
|
||||
await this.document.update({ 'system.resources.hope.value': newValue });
|
||||
}
|
||||
|
||||
static async toggleGold(_, button) {
|
||||
const goldValue = Number.parseInt(button.dataset.value);
|
||||
const goldType = button.dataset.type;
|
||||
const newValue = this.document.system.gold[goldType] >= goldValue ? goldValue - 1 : goldValue;
|
||||
|
||||
const update = `system.gold.${goldType}`;
|
||||
await this.document.update({ [update]: newValue });
|
||||
}
|
||||
|
||||
static async attackRoll(event, button) {
|
||||
const weapon = await fromUuid(button.dataset.weapon);
|
||||
const damage = {
|
||||
value: `${this.document.system.proficiency}${weapon.system.damage.value}`,
|
||||
type: weapon.system.damage.type
|
||||
};
|
||||
const modifier = this.document.system.traits[weapon.system.trait].value;
|
||||
|
||||
const { roll, hope, fear, advantage, disadvantage, modifiers } = await this.document.dualityRoll(
|
||||
{ title: game.i18n.localize(abilities[weapon.system.trait].label), value: modifier },
|
||||
event.shiftKey
|
||||
);
|
||||
|
||||
const targets = Array.from(game.user.targets).map(x => ({
|
||||
id: x.id,
|
||||
name: x.actor.name,
|
||||
img: x.actor.img,
|
||||
difficulty: x.actor.system.difficulty,
|
||||
evasion: x.actor.system.evasion
|
||||
}));
|
||||
|
||||
const systemData = new DHDualityRoll({
|
||||
title: weapon.name,
|
||||
origin: this.document.id,
|
||||
roll: roll._formula,
|
||||
modifiers: modifiers,
|
||||
hope: hope,
|
||||
fear: fear,
|
||||
advantage: advantage,
|
||||
disadvantage: disadvantage,
|
||||
damage: damage,
|
||||
targets: targets
|
||||
});
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const msg = new cls({
|
||||
type: 'dualityRoll',
|
||||
sound: CONFIG.sounds.dice,
|
||||
system: systemData,
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/attack-roll.hbs',
|
||||
systemData
|
||||
),
|
||||
rolls: [roll]
|
||||
});
|
||||
|
||||
await cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
static openLevelUp() {
|
||||
if (!this.document.system.class.value || !this.document.system.class.subclass) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Sheets.PC.Errors.missingClassOrSubclass'));
|
||||
return;
|
||||
}
|
||||
|
||||
new DhlevelUp(this.document).render(true);
|
||||
}
|
||||
|
||||
static async useDomainCard(_, button) {
|
||||
const card = this.document.items.find(x => x.uuid === button.dataset.key);
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
title: `${game.i18n.localize('DAGGERHEART.Chat.DomainCard.Title')} - ${capitalize(button.dataset.domain)}`,
|
||||
origin: this.document.id,
|
||||
img: card.img,
|
||||
name: card.name,
|
||||
description: card.system.effect,
|
||||
actions: card.system.actions
|
||||
};
|
||||
const msg = new cls({
|
||||
type: 'abilityUse',
|
||||
user: game.user.id,
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||
systemData
|
||||
),
|
||||
system: systemData
|
||||
});
|
||||
|
||||
cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
static async removeDomainCard(_, button) {
|
||||
if (button.dataset.type === 'domainCard') {
|
||||
const card = this.document.items.find(x => x.uuid === button.dataset.key);
|
||||
await card.delete();
|
||||
}
|
||||
}
|
||||
|
||||
static async selectClass() {
|
||||
(await game.packs.get('daggerheart.classes'))?.render(true);
|
||||
}
|
||||
|
||||
static async selectSubclass() {
|
||||
(await game.packs.get('daggerheart.subclasses'))?.render(true);
|
||||
}
|
||||
|
||||
static async selectAncestry() {
|
||||
const dialogClosed = new Promise((resolve, _) => {
|
||||
new AncestrySelectionDialog(resolve).render(true);
|
||||
});
|
||||
const result = await dialogClosed;
|
||||
|
||||
for (var ancestry of this.document.items.filter(x => x => x.type === 'ancestry')) {
|
||||
await ancestry.delete();
|
||||
}
|
||||
|
||||
const createdItems = [];
|
||||
for (var feature of this.document.items.filter(
|
||||
x => x.type === 'feature' && x.system.type === SYSTEM.ITEM.featureTypes.ancestry.id
|
||||
)) {
|
||||
await feature.delete();
|
||||
}
|
||||
|
||||
createdItems.push(result.data);
|
||||
|
||||
await this.document.createEmbeddedDocuments('Item', createdItems);
|
||||
}
|
||||
|
||||
static async selectCommunity() {
|
||||
(await game.packs.get('daggerheart.communities'))?.render(true);
|
||||
}
|
||||
|
||||
static async viewObject(_, button) {
|
||||
const object = await fromUuid(button.dataset.value);
|
||||
if (!object) return;
|
||||
|
||||
const tab = button.dataset.tab;
|
||||
if (tab && object.sheet._tabs) object.sheet._tabs[0].active = tab;
|
||||
|
||||
if (object.sheet.editMode) object.sheet.editMode = false;
|
||||
|
||||
object.sheet.render(true);
|
||||
}
|
||||
|
||||
static async takeShortRest() {
|
||||
await new DhpDowntime(this.document, true).render(true);
|
||||
await this.minimize();
|
||||
}
|
||||
|
||||
static async takeLongRest() {
|
||||
await new DhpDowntime(this.document, false).render(true);
|
||||
await this.minimize();
|
||||
}
|
||||
|
||||
static async addScar() {
|
||||
if (this.document.system.story.scars.length === 5) return;
|
||||
|
||||
await this.document.update({
|
||||
'system.story.scars': [
|
||||
...this.document.system.story.scars,
|
||||
{ name: game.i18n.localize('DAGGERHEART.Sheets.PC.NewScar'), description: '' }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
static async deleteScar(event, button) {
|
||||
event.stopPropagation();
|
||||
await this.document.update({
|
||||
'system.story.scars': this.document.system.story.scars.filter(
|
||||
(_, index) => index !== Number.parseInt(button.currentTarget.dataset.scar)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
static async makeDeathMove() {
|
||||
if (this.document.system.resources.hitPoints.value === this.document.system.resources.hitPoints.max) {
|
||||
await new DhpDeathMove(this.document).render(true);
|
||||
await this.minimize();
|
||||
}
|
||||
}
|
||||
|
||||
async itemUpdate(event) {
|
||||
const name = event.currentTarget.dataset.item;
|
||||
const item = await fromUuid($(event.currentTarget).closest('[data-item-id]')[0].dataset.itemId);
|
||||
await item.update({ [name]: event.currentTarget.value });
|
||||
}
|
||||
|
||||
async onLevelChange(event) {
|
||||
await this.document.updateLevel(Number(event.currentTarget.value));
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async deleteItem(_, button) {
|
||||
const item = await fromUuid($(button).closest('[data-item-id]')[0].dataset.itemId);
|
||||
await item.delete();
|
||||
}
|
||||
|
||||
static async setItemQuantity(button, value) {
|
||||
const item = await fromUuid($(button).closest('[data-item-id]')[0].dataset.itemId);
|
||||
await item.update({ 'system.quantity': Math.max(item.system.quantity + value, 1) });
|
||||
}
|
||||
|
||||
static async useFeature(_, button) {
|
||||
const item = await fromUuid(button.dataset.id);
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
title: game.i18n.localize('DAGGERHEART.Chat.FeatureTitle'),
|
||||
origin: this.document.id,
|
||||
img: item.img,
|
||||
name: item.name,
|
||||
description: item.system.description,
|
||||
actions: item.system.actions
|
||||
};
|
||||
const msg = new cls({
|
||||
type: 'abilityUse',
|
||||
user: game.user.id,
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||
systemData
|
||||
),
|
||||
system: systemData
|
||||
});
|
||||
|
||||
cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
static async useAbility(_, button) {
|
||||
const item = await fromUuid(button.dataset.feature);
|
||||
const type = button.dataset.type;
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
title:
|
||||
type === 'ancestry'
|
||||
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.AncestryTitle')
|
||||
: type === 'community'
|
||||
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.CommunityTitle')
|
||||
: game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
|
||||
origin: this.document.id,
|
||||
img: item.img,
|
||||
name: item.name,
|
||||
description: item.system.description,
|
||||
actions: []
|
||||
};
|
||||
const msg = new cls({
|
||||
type: 'abilityUse',
|
||||
user: game.user.id,
|
||||
system: systemData,
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||
systemData
|
||||
)
|
||||
});
|
||||
|
||||
cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
static async useAdvancementCard(_, button) {
|
||||
const item =
|
||||
button.dataset.multiclass === 'true'
|
||||
? this.document.system.multiclass.subclass
|
||||
: this.document.system.class.subclass;
|
||||
const ability = item.system[`${button.dataset.key}Feature`];
|
||||
const title = `${item.name} - ${game.i18n.localize(`DAGGERHEART.Sheets.PC.DomainCard.${capitalize(button.dataset.key)}Title`)}`;
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
title: game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
|
||||
origin: this.document.id,
|
||||
name: title,
|
||||
img: item.img,
|
||||
description: ability.description
|
||||
};
|
||||
const msg = new cls({
|
||||
type: 'abilityUse',
|
||||
user: game.user.id,
|
||||
system: systemData,
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||
systemData
|
||||
)
|
||||
});
|
||||
|
||||
cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
static async useAdvancementAbility(_, button) {
|
||||
const item = this.document.items.find(x => x.uuid === button.dataset.id);
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
title: game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
|
||||
origin: this.document.id,
|
||||
name: item.name,
|
||||
img: item.img,
|
||||
description: item.system.description
|
||||
};
|
||||
const msg = new cls({
|
||||
user: game.user.id,
|
||||
system: systemData,
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||
systemData
|
||||
)
|
||||
});
|
||||
|
||||
cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
static async toggleEquipItem(_, button) {
|
||||
const item = this.document.items.get(button.id);
|
||||
if (item.system.equipped) {
|
||||
await item.update({ 'system.equipped': false });
|
||||
return;
|
||||
}
|
||||
|
||||
switch (item.type) {
|
||||
case 'armor':
|
||||
const currentArmor = this.document.system.armor;
|
||||
if (currentArmor) {
|
||||
await currentArmor.update({ 'system.equipped': false });
|
||||
}
|
||||
|
||||
await item.update({ 'system.equipped': true });
|
||||
break;
|
||||
case 'weapon':
|
||||
await this.document.system.constructor.unequipBeforeEquip.bind(this.document.system)(item);
|
||||
|
||||
await item.update({ 'system.equipped': true });
|
||||
break;
|
||||
}
|
||||
this.render();
|
||||
}
|
||||
|
||||
async _onDragStart(_, event) {
|
||||
super._onDragStart(event);
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
super._onDrop(event);
|
||||
this._onDropItem(event, TextEditor.getDragEventData(event));
|
||||
}
|
||||
|
||||
async _onDropItem(event, data) {
|
||||
const item = await Item.implementation.fromDropData(data);
|
||||
const itemData = item.toObject();
|
||||
|
||||
if (item.type === 'domainCard' && this.document.system.domainCards.loadout.length >= 5) {
|
||||
itemData.system.inVault = true;
|
||||
}
|
||||
|
||||
if (this.document.uuid === item.parent?.uuid) return this._onSortItem(event, itemData);
|
||||
const createdItem = await this._onDropItemCreate(itemData);
|
||||
|
||||
return createdItem;
|
||||
}
|
||||
|
||||
async _onDropItemCreate(itemData, event) {
|
||||
itemData = itemData instanceof Array ? itemData : [itemData];
|
||||
return this.document.createEmbeddedDocuments('Item', itemData);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ export default function DhpApplicationMixin(Base) {
|
|||
|
||||
async _prepareContext(_options, objectPath = 'document') {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.source = this[objectPath].toObject();
|
||||
context.source = this[objectPath];
|
||||
context.fields = this[objectPath].schema.fields;
|
||||
context.systemFields = this[objectPath].system ? this[objectPath].system.schema.fields : {};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,78 +1,60 @@
|
|||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||
|
||||
const { DocumentSheetV2 } = foundry.applications.api;
|
||||
export default class DhpEnvironment extends DaggerheartSheet(DocumentSheetV2) {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.editMode = false;
|
||||
}
|
||||
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'sheet', 'adversary', 'environment'],
|
||||
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'environment'],
|
||||
position: {
|
||||
width: 600,
|
||||
height: 'auto'
|
||||
width: 450,
|
||||
height: 1000
|
||||
},
|
||||
actions: {
|
||||
toggleSlider: this.toggleSlider,
|
||||
viewFeature: this.viewFeature,
|
||||
addAdversary: this.addAdversary,
|
||||
addFeature: this.addFeature,
|
||||
removeFeature: this.removeFeature,
|
||||
addTone: this.addTone,
|
||||
removeTone: this.removeTone,
|
||||
useFeature: this.useFeature
|
||||
deleteProperty: this.deleteProperty,
|
||||
viewAdversary: this.viewAdversary
|
||||
},
|
||||
form: {
|
||||
handler: this._updateForm,
|
||||
closeOnSubmit: false,
|
||||
submitOnChange: true
|
||||
}
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
},
|
||||
dragDrop: [{ dragSelector: null, dropSelector: '.adversary-container' }]
|
||||
};
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
form: {
|
||||
id: 'form',
|
||||
template: 'systems/daggerheart/templates/sheets/environment.hbs'
|
||||
}
|
||||
header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' },
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
main: { template: 'systems/daggerheart/templates/sheets/actors/environment/main.hbs' },
|
||||
information: { template: 'systems/daggerheart/templates/sheets/actors/environment/information.hbs' }
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritDoc */
|
||||
get title() {
|
||||
return `${game.i18n.localize('Environment')} - ${this.document.name}`;
|
||||
}
|
||||
static TABS = {
|
||||
main: {
|
||||
active: true,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'main',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.Sheets.Environment.Tabs.Main'
|
||||
},
|
||||
information: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'information',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.Sheets.Environment.Tabs.Information'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
return {
|
||||
title: `${this.document.name} - ${game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.document.system.type].name)}`,
|
||||
user: this.document,
|
||||
source: this.document.toObject(),
|
||||
fields: this.document.schema.fields,
|
||||
data: {
|
||||
type: game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.document.system.type].name),
|
||||
features: this.document.items.reduce((acc, x) => {
|
||||
if (x.type === 'feature') {
|
||||
const feature = x.toObject();
|
||||
acc.push({
|
||||
...feature,
|
||||
system: {
|
||||
...feature.system,
|
||||
actionType: game.i18n.localize(SYSTEM.ITEM.actionTypes[feature.system.actionType].name)
|
||||
},
|
||||
uuid: x.uuid
|
||||
});
|
||||
}
|
||||
const context = await super._prepareContext(_options);
|
||||
context.document = this.document;
|
||||
context.tabs = super._getTabs(this.constructor.TABS);
|
||||
|
||||
return acc;
|
||||
}, [])
|
||||
},
|
||||
editMode: this.editMode,
|
||||
config: SYSTEM
|
||||
};
|
||||
return context;
|
||||
}
|
||||
|
||||
static async _updateForm(event, _, formData) {
|
||||
|
|
@ -80,60 +62,41 @@ export default class DhpEnvironment extends DaggerheartSheet(DocumentSheetV2) {
|
|||
this.render();
|
||||
}
|
||||
|
||||
static toggleSlider() {
|
||||
this.editMode = !this.editMode;
|
||||
static async addAdversary() {
|
||||
await this.document.update({
|
||||
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
||||
'DAGGERHEART.Sheets.Environment.newAdversary'
|
||||
)
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async viewFeature(_, button) {
|
||||
const move = await fromUuid(button.dataset.feature);
|
||||
move.sheet.render(true);
|
||||
}
|
||||
|
||||
static async addFeature() {
|
||||
const result = await this.document.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
name: game.i18n.localize('DAGGERHEART.Sheets.Environment.NewFeature'),
|
||||
type: 'feature'
|
||||
}
|
||||
]);
|
||||
|
||||
await result[0].sheet.render(true);
|
||||
ui.notifications.error('Not Implemented yet. Awaiting datamodel rework');
|
||||
}
|
||||
|
||||
static async removeFeature(_, button) {
|
||||
await this.document.items.find(x => x.uuid === button.dataset.feature).delete();
|
||||
static async deleteProperty(_, target) {
|
||||
await this.document.update({ [`${target.dataset.path}.-=${target.id}`]: null });
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async addTone() {
|
||||
await this.document.update({ 'system.toneAndFeel': [...this.document.system.toneAndFeel, ''] });
|
||||
static async viewAdversary(_, button) {
|
||||
const adversary = foundry.utils.getProperty(
|
||||
this.document.system.potentialAdversaries,
|
||||
`${button.dataset.potentialAdversary}.adversaries.${button.dataset.adversary}`
|
||||
);
|
||||
adversary.sheet.render(true);
|
||||
}
|
||||
|
||||
static async removeTone(button) {
|
||||
await this.document.update({
|
||||
'system.toneAndFeel': this.document.system.toneAndFeel.filter(
|
||||
(_, index) => index !== Number.parseInt(button.dataset.tone)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
static async useFeature(_, button) {
|
||||
const item = this.document.items.find(x => x.uuid === button.dataset.feature);
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const msg = new cls({
|
||||
user: game.user.id,
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||
{
|
||||
title: game.i18n.format('DAGGERHEART.Chat.EnvironmentTitle', {
|
||||
actionType: button.dataset.actionType
|
||||
}),
|
||||
card: { name: item.name, img: item.img, description: item.system.description }
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
cls.create(msg.toObject());
|
||||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
const item = await fromUuid(data.uuid);
|
||||
if (item.type === 'adversary') {
|
||||
const target = event.target.closest('.adversary-container');
|
||||
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries.${item.id}`;
|
||||
await this.document.update({
|
||||
[path]: item.uuid
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
|
|||
actions: {
|
||||
removeSubclass: this.removeSubclass,
|
||||
viewSubclass: this.viewSubclass,
|
||||
removeFeature: this.removeFeature,
|
||||
viewFeature: this.viewFeature,
|
||||
deleteFeature: this.deleteFeature,
|
||||
editFeature: this.editFeature,
|
||||
removeItem: this.removeItem,
|
||||
viewItem: this.viewItem,
|
||||
removePrimaryWeapon: this.removePrimaryWeapon,
|
||||
|
|
@ -153,13 +153,13 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
|
|||
subclass.sheet.render(true);
|
||||
}
|
||||
|
||||
static async removeFeature(_, button) {
|
||||
static async deleteFeature(_, button) {
|
||||
await this.document.update({
|
||||
'system.features': this.document.system.features.filter(x => x.uuid !== button.dataset.feature)
|
||||
'system.features': this.document.system.features.map(x => x.uuid).filter(x => x !== button.dataset.feature)
|
||||
});
|
||||
}
|
||||
|
||||
static async viewFeature(_, button) {
|
||||
static async editFeature(_, button) {
|
||||
const feature = await fromUuid(button.dataset.feature);
|
||||
feature.sheet.render(true);
|
||||
}
|
||||
|
|
@ -198,15 +198,11 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
|
|||
const item = await fromUuid(data.uuid);
|
||||
if (item.type === 'subclass') {
|
||||
await this.document.update({
|
||||
'system.subclasses': [
|
||||
...this.document.system.subclasses, item.uuid
|
||||
]
|
||||
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
} else if (item.type === 'feature') {
|
||||
await this.document.update({
|
||||
'system.features': [
|
||||
...this.document.system.features, item.uuid
|
||||
]
|
||||
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
} else if (item.type === 'weapon') {
|
||||
if (event.currentTarget.classList.contains('primary-weapon-section')) {
|
||||
|
|
@ -231,25 +227,19 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
|
|||
if (item.type === 'miscellaneous' || item.type === 'consumable') {
|
||||
if (this.document.system.inventory.choiceA.length < 2)
|
||||
await this.document.update({
|
||||
'system.inventory.choiceA': [
|
||||
...this.document.system.inventory.choiceA, item.uuid
|
||||
]
|
||||
'system.inventory.choiceA': [...this.document.system.inventory.choiceA, item.uuid]
|
||||
});
|
||||
}
|
||||
} else if (item.type === 'miscellaneous') {
|
||||
if (event.currentTarget.classList.contains('take-section')) {
|
||||
if (this.document.system.inventory.take.length < 3)
|
||||
await this.document.update({
|
||||
'system.inventory.take': [
|
||||
...this.document.system.inventory.take, item.uuid
|
||||
]
|
||||
'system.inventory.take': [...this.document.system.inventory.take, item.uuid]
|
||||
});
|
||||
} else if (event.currentTarget.classList.contains('choice-b-section')) {
|
||||
if (this.document.system.inventory.choiceB.length < 2)
|
||||
await this.document.update({
|
||||
'system.inventory.choiceB': [
|
||||
...this.document.system.inventory.choiceB, item.uuid
|
||||
]
|
||||
'system.inventory.choiceB': [...this.document.system.inventory.choiceB, item.uuid]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -76,53 +76,82 @@ export const featureProperties = {
|
|||
},
|
||||
spellcastingTrait: {
|
||||
name: 'DAGGERHEART.FeatureProperty.SpellcastingTrait',
|
||||
path: actor => actor.system.traits[actor.system.subclass.system.spellcastingTrait].data.value
|
||||
path: actor => actor.system.traits[actor.system.class.subclass.system.spellcastingTrait].data.value
|
||||
}
|
||||
};
|
||||
|
||||
export const adversaryTypes = {
|
||||
bruiser: {
|
||||
name: 'DAGGERHEART.Adversary.Bruiser.Name',
|
||||
id: 'bruiser',
|
||||
label: 'DAGGERHEART.Adversary.Type.Bruiser.label',
|
||||
description: 'DAGGERHEART.Adversary.Bruiser.Description'
|
||||
},
|
||||
horde: {
|
||||
name: 'DAGGERHEART.Adversary.Horde.Name',
|
||||
id: 'horde',
|
||||
label: 'DAGGERHEART.Adversary.Type.Horde.label',
|
||||
description: 'DAGGERHEART.Adversary.Horde.Description'
|
||||
},
|
||||
leader: {
|
||||
name: 'DAGGERHEART.Adversary.Leader.Name',
|
||||
id: 'leader',
|
||||
label: 'DAGGERHEART.Adversary.Type.Leader.label',
|
||||
description: 'DAGGERHEART.Adversary.Leader.Description'
|
||||
},
|
||||
minion: {
|
||||
name: 'DAGGERHEART.Adversary.Minion.Name',
|
||||
id: 'minion',
|
||||
label: 'DAGGERHEART.Adversary.Type.Minion.label',
|
||||
description: 'DAGGERHEART.Adversary.Minion.Description'
|
||||
},
|
||||
ranged: {
|
||||
name: 'DAGGERHEART.Adversary.Ranged.Name',
|
||||
id: 'ranged',
|
||||
label: 'DAGGERHEART.Adversary.Type.Ranged.label',
|
||||
description: 'DAGGERHEART.Adversary.Ranged.Description'
|
||||
},
|
||||
skulker: {
|
||||
name: 'DAGGERHEART.Adversary.Skulker.Name',
|
||||
description: 'DAGGERHEART.Adversary.Skulker.Description'
|
||||
skulk: {
|
||||
id: 'skulk',
|
||||
label: 'DAGGERHEART.Adversary.Type.Skulk.label',
|
||||
description: 'DAGGERHEART.Adversary.Skulk.Description'
|
||||
},
|
||||
social: {
|
||||
name: 'DAGGERHEART.Adversary.Social.Name',
|
||||
id: 'social',
|
||||
label: 'DAGGERHEART.Adversary.Type.Social.label',
|
||||
description: 'DAGGERHEART.Adversary.Social.Description'
|
||||
},
|
||||
solo: {
|
||||
name: 'DAGGERHEART.Adversary.Solo.Name',
|
||||
id: 'solo',
|
||||
label: 'DAGGERHEART.Adversary.Type.Solo.label',
|
||||
description: 'DAGGERHEART.Adversary.Solo.Description'
|
||||
},
|
||||
standard: {
|
||||
name: 'DAGGERHEART.Adversary.Standard.Name',
|
||||
id: 'standard',
|
||||
label: 'DAGGERHEART.Adversary.Type.Standard.label',
|
||||
description: 'DAGGERHEART.Adversary.Standard.Description'
|
||||
},
|
||||
support: {
|
||||
name: 'DAGGERHEART.Adversary.Support.Name',
|
||||
id: 'support',
|
||||
label: 'DAGGERHEART.Adversary.Type.Support.label',
|
||||
description: 'DAGGERHEART.Adversary.Support.Description'
|
||||
}
|
||||
};
|
||||
|
||||
export const environmentTypes = {
|
||||
exploration: {
|
||||
label: 'DAGGERHEART.Environment.Type.Exploration.label',
|
||||
description: 'DAGGERHEART.Environment.Type.Exploration.description'
|
||||
},
|
||||
social: {
|
||||
label: 'DAGGERHEART.Environment.Type.Social.label',
|
||||
description: 'DAGGERHEART.Environment.Type.Social.description'
|
||||
},
|
||||
traversal: {
|
||||
label: 'DAGGERHEART.Environment.Type.Traversal.label',
|
||||
description: 'DAGGERHEART.Environment.Type.Traversal.description'
|
||||
},
|
||||
event: {
|
||||
label: 'DAGGERHEART.Environment.Type.Event.label',
|
||||
description: 'DAGGERHEART.Environment.Type.Event.description'
|
||||
}
|
||||
};
|
||||
|
||||
export const adversaryTraits = {
|
||||
relentless: {
|
||||
name: 'DAGGERHEART.Adversary.Trait..Name',
|
||||
|
|
|
|||
|
|
@ -5,26 +5,31 @@ export const range = {
|
|||
distance: 0
|
||||
},
|
||||
melee: {
|
||||
id: 'melee',
|
||||
label: 'DAGGERHEART.Range.melee.name',
|
||||
description: 'DAGGERHEART.Range.melee.description',
|
||||
distance: 1
|
||||
},
|
||||
veryClose: {
|
||||
id: 'veryClose',
|
||||
label: 'DAGGERHEART.Range.veryClose.name',
|
||||
description: 'DAGGERHEART.Range.veryClose.description',
|
||||
distance: 3
|
||||
},
|
||||
close: {
|
||||
id: 'close',
|
||||
label: 'DAGGERHEART.Range.close.name',
|
||||
description: 'DAGGERHEART.Range.close.description',
|
||||
distance: 10
|
||||
},
|
||||
far: {
|
||||
id: 'far',
|
||||
label: 'DAGGERHEART.Range.far.name',
|
||||
description: 'DAGGERHEART.Range.far.description',
|
||||
distance: 20
|
||||
},
|
||||
veryFar: {
|
||||
id: 'veryFar',
|
||||
label: 'DAGGERHEART.Range.veryFar.name',
|
||||
description: 'DAGGERHEART.Range.veryFar.description',
|
||||
distance: 30
|
||||
|
|
@ -180,31 +185,27 @@ export const deathMoves = {
|
|||
};
|
||||
|
||||
export const tiers = {
|
||||
0: {
|
||||
key: 0,
|
||||
id: 'tier0',
|
||||
name: 'DAGGERHEART.General.Tier.0'
|
||||
},
|
||||
1: {
|
||||
key: 1,
|
||||
tier1: {
|
||||
id: 'tier1',
|
||||
name: 'DAGGERHEART.General.Tier.1'
|
||||
label: 'DAGGERHEART.Tiers.tier1'
|
||||
},
|
||||
2: {
|
||||
key: 2,
|
||||
tier2: {
|
||||
id: 'tier2',
|
||||
name: 'DAGGERHEART.General.Tier.2'
|
||||
label: 'DAGGERHEART.Tiers.tier2'
|
||||
},
|
||||
3: {
|
||||
key: 3,
|
||||
tier3: {
|
||||
id: 'tier3',
|
||||
name: 'DAGGERHEART.General.Tier.3'
|
||||
label: 'DAGGERHEART.Tiers.tier3'
|
||||
},
|
||||
tier4: {
|
||||
id: 'tier4',
|
||||
label: 'DAGGERHEART.Tiers.tier4'
|
||||
}
|
||||
};
|
||||
|
||||
export const objectTypes = {
|
||||
pc: {
|
||||
name: 'TYPES.Actor.pc'
|
||||
character: {
|
||||
name: 'TYPES.Actor.character'
|
||||
},
|
||||
npc: {
|
||||
name: 'TYPES.Actor.npc'
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
export { default as DhpPC } from './pc.mjs';
|
||||
export { default as DhClass } from './item/class.mjs';
|
||||
export { default as DhSubclass } from './item/subclass.mjs';
|
||||
export { default as DhCombat } from './combat.mjs';
|
||||
export { default as DhCombatant } from './combatant.mjs';
|
||||
export { default as DhpAdversary } from './adversary.mjs';
|
||||
export { default as DhpEnvironment } from './environment.mjs';
|
||||
|
||||
export * as actors from './actor/_module.mjs';
|
||||
export * as items from './item/_module.mjs';
|
||||
export { actionsTypes } from './action/_module.mjs';
|
||||
export * as messages from './chat-message/_modules.mjs';
|
||||
|
|
|
|||
11
module/data/actor/_module.mjs
Normal file
11
module/data/actor/_module.mjs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import DhCharacter from './character.mjs';
|
||||
import DhAdversary from './adversary.mjs';
|
||||
import DhEnvironment from './environment.mjs';
|
||||
|
||||
export { DhCharacter, DhAdversary, DhEnvironment };
|
||||
|
||||
export const config = {
|
||||
character: DhCharacter,
|
||||
adversary: DhAdversary,
|
||||
environment: DhEnvironment
|
||||
};
|
||||
68
module/data/actor/adversary.mjs
Normal file
68
module/data/actor/adversary.mjs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import BaseDataActor from './base.mjs';
|
||||
|
||||
const resourceField = () =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
max: new foundry.data.fields.NumberField({ initial: 0, integer: true })
|
||||
});
|
||||
|
||||
export default class DhpAdversary extends BaseDataActor {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Adversary'];
|
||||
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.adversary',
|
||||
type: 'adversary'
|
||||
});
|
||||
}
|
||||
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
tier: new fields.StringField({
|
||||
required: true,
|
||||
choices: SYSTEM.GENERAL.tiers,
|
||||
initial: SYSTEM.GENERAL.tiers.tier1.id
|
||||
}),
|
||||
type: new fields.StringField({
|
||||
required: true,
|
||||
choices: SYSTEM.ACTOR.adversaryTypes,
|
||||
initial: SYSTEM.ACTOR.adversaryTypes.standard.id
|
||||
}),
|
||||
motivesAndTactics: new fields.HTMLField(),
|
||||
difficulty: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
||||
damageThresholds: new fields.SchemaField({
|
||||
major: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
severe: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(),
|
||||
stress: resourceField()
|
||||
}),
|
||||
attack: new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
modifier: new fields.NumberField({ required: true, integer: true, initial: 0 }),
|
||||
range: new fields.StringField({
|
||||
required: true,
|
||||
choices: SYSTEM.GENERAL.range,
|
||||
initial: SYSTEM.GENERAL.range.melee.id
|
||||
}),
|
||||
damage: new fields.SchemaField({
|
||||
value: new fields.StringField(),
|
||||
type: new fields.StringField({
|
||||
required: true,
|
||||
choices: SYSTEM.GENERAL.damageTypes,
|
||||
initial: SYSTEM.GENERAL.damageTypes.physical.id
|
||||
})
|
||||
})
|
||||
}),
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField(),
|
||||
value: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
||||
})
|
||||
)
|
||||
/* Features waiting on pseudo-document data model addition */
|
||||
};
|
||||
}
|
||||
}
|
||||
34
module/data/actor/base.mjs
Normal file
34
module/data/actor/base.mjs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Describes metadata about the actor data model type
|
||||
* @typedef {Object} ActorDataModelMetadata
|
||||
* @property {string} label - A localizable label used on application.
|
||||
* @property {string} type - The system type that this data model represents.
|
||||
*/
|
||||
export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||
/** @returns {ActorDataModelMetadata}*/
|
||||
static get metadata() {
|
||||
return {
|
||||
label: 'Base Actor',
|
||||
type: 'base'
|
||||
};
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
description: new fields.HTMLField({ required: true, nullable: true })
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
||||
* @param {object} [options] - Options which modify the getRollData method.
|
||||
* @returns {object}
|
||||
*/
|
||||
getRollData() {
|
||||
const data = { ...this };
|
||||
return data;
|
||||
}
|
||||
}
|
||||
240
module/data/actor/character.mjs
Normal file
240
module/data/actor/character.mjs
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
import { burden } from '../../config/generalConfig.mjs';
|
||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||
import { LevelOptionType } from '../levelTier.mjs';
|
||||
import BaseDataActor from './base.mjs';
|
||||
|
||||
const attributeField = () =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
tierMarked: new foundry.data.fields.BooleanField({ initial: false })
|
||||
});
|
||||
|
||||
const resourceField = max =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
max: new foundry.data.fields.NumberField({ initial: max, integer: true })
|
||||
});
|
||||
|
||||
export default class DhCharacter extends BaseDataActor {
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.character',
|
||||
type: 'character'
|
||||
});
|
||||
}
|
||||
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(6),
|
||||
stress: resourceField(6),
|
||||
hope: resourceField(6)
|
||||
}),
|
||||
traits: new fields.SchemaField({
|
||||
agility: attributeField(),
|
||||
strength: attributeField(),
|
||||
finesse: attributeField(),
|
||||
instinct: attributeField(),
|
||||
presence: attributeField(),
|
||||
knowledge: attributeField()
|
||||
}),
|
||||
proficiency: new fields.NumberField({ initial: 1, integer: true }),
|
||||
evasion: new fields.NumberField({ initial: 0, integer: true }),
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
description: new fields.StringField({}),
|
||||
value: new fields.NumberField({ integer: true, nullable: true, initial: null })
|
||||
}),
|
||||
{
|
||||
initial: {
|
||||
[foundry.utils.randomID()]: { description: '', value: 2 },
|
||||
[foundry.utils.randomID()]: { description: '', value: 2 }
|
||||
}
|
||||
}
|
||||
),
|
||||
gold: new fields.SchemaField({
|
||||
coins: new fields.NumberField({ initial: 0, integer: true }),
|
||||
handfulls: new fields.NumberField({ initial: 0, integer: true }),
|
||||
bags: new fields.NumberField({ initial: 0, integer: true }),
|
||||
chests: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
pronouns: new fields.StringField({}),
|
||||
scars: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
description: new fields.HTMLField()
|
||||
})
|
||||
),
|
||||
story: new fields.HTMLField(),
|
||||
class: new fields.SchemaField({
|
||||
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
||||
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
|
||||
}),
|
||||
multiclass: new fields.SchemaField({
|
||||
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
||||
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
|
||||
}),
|
||||
levelData: new fields.EmbeddedDataField(DhPCLevelData)
|
||||
};
|
||||
}
|
||||
|
||||
get ancestry() {
|
||||
return this.parent.items.find(x => x.type === 'ancestry') ?? null;
|
||||
}
|
||||
|
||||
get community() {
|
||||
return this.parent.items.find(x => x.type === 'community') ?? null;
|
||||
}
|
||||
|
||||
get domains() {
|
||||
const classDomains = this.class ? this.class.system.domains : [];
|
||||
const multiclassDomains = this.multiclass ? this.multiclass.system.domains : [];
|
||||
return [...classDomains, ...multiclassDomains];
|
||||
}
|
||||
|
||||
get domainCards() {
|
||||
const domainCards = this.parent.items.filter(x => x.type === 'domainCard');
|
||||
const loadout = domainCards.filter(x => !x.system.inVault);
|
||||
const vault = domainCards.filter(x => x.system.inVault);
|
||||
|
||||
return {
|
||||
loadout: loadout,
|
||||
vault: vault,
|
||||
total: [...loadout, ...vault]
|
||||
};
|
||||
}
|
||||
|
||||
get armor() {
|
||||
return this.parent.items.find(x => x.type === 'armor' && x.system.equipped);
|
||||
}
|
||||
|
||||
get primaryWeapon() {
|
||||
return this.parent.items.find(x => x.type === 'weapon' && x.system.equipped && !x.system.secondary);
|
||||
}
|
||||
|
||||
get secondaryWeapon() {
|
||||
return this.parent.items.find(x => x.type === 'weapon' && x.system.equipped && x.system.secondary);
|
||||
}
|
||||
|
||||
get getWeaponBurden() {
|
||||
return this.primaryWeapon?.system?.burden === burden.twoHanded.value ||
|
||||
(this.primaryWeapon && this.secondaryWeapon)
|
||||
? burden.twoHanded.value
|
||||
: this.primaryWeapon || this.secondaryWeapon
|
||||
? burden.oneHanded.value
|
||||
: null;
|
||||
}
|
||||
|
||||
get refreshableFeatures() {
|
||||
return this.parent.items.reduce(
|
||||
(acc, x) => {
|
||||
if (x.type === 'feature' && x.system.refreshData?.type === 'feature' && x.system.refreshData?.type) {
|
||||
acc[x.system.refreshData.type].push(x);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ shortRest: [], longRest: [] }
|
||||
);
|
||||
}
|
||||
|
||||
static async unequipBeforeEquip(itemToEquip) {
|
||||
const primary = this.primaryWeapon,
|
||||
secondary = this.secondaryWeapon;
|
||||
if (itemToEquip.system.secondary) {
|
||||
if (primary && primary.burden === SYSTEM.GENERAL.burden.twoHanded.value) {
|
||||
await primary.update({ 'system.equipped': false });
|
||||
}
|
||||
|
||||
if (secondary) {
|
||||
await secondary.update({ 'system.equipped': false });
|
||||
}
|
||||
} else {
|
||||
if (secondary && itemToEquip.system.burden === SYSTEM.GENERAL.burden.twoHanded.value) {
|
||||
await secondary.update({ 'system.equipped': false });
|
||||
}
|
||||
|
||||
if (primary) {
|
||||
await primary.update({ 'system.equipped': false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prepareBaseData() {
|
||||
for (var attributeKey in this.traits) {
|
||||
const attribute = this.traits[attributeKey];
|
||||
/* Levleup handling */
|
||||
}
|
||||
|
||||
const armor = this.armor;
|
||||
this.damageThresholds = {
|
||||
major: armor
|
||||
? armor.system.baseThresholds.major + this.levelData.level.current
|
||||
: this.levelData.level.current,
|
||||
severe: armor
|
||||
? armor.system.baseThresholds.severe + this.levelData.level.current
|
||||
: this.levelData.level.current * 2
|
||||
};
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
this.resources.hope.max -= Object.keys(this.scars).length;
|
||||
this.resources.hope.value = Math.min(this.resources.hope.value, this.resources.hope.max);
|
||||
}
|
||||
}
|
||||
|
||||
class DhPCLevelData extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
level: new fields.SchemaField({
|
||||
current: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||
changed: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
||||
}),
|
||||
levelups: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
achievements: new fields.SchemaField(
|
||||
{
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true }),
|
||||
modifier: new fields.NumberField({ required: true, integer: true })
|
||||
})
|
||||
),
|
||||
domainCards: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
uuid: new fields.StringField({ required: true }),
|
||||
itemUuid: new fields.StringField({ required: true })
|
||||
})
|
||||
),
|
||||
proficiency: new fields.NumberField({ integer: true })
|
||||
},
|
||||
{ nullable: true, initial: null }
|
||||
),
|
||||
selections: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
tier: new fields.NumberField({ required: true, integer: true }),
|
||||
level: new fields.NumberField({ required: true, integer: true }),
|
||||
optionKey: new fields.StringField({ required: true }),
|
||||
type: new fields.StringField({ required: true, choices: LevelOptionType }),
|
||||
checkboxNr: new fields.NumberField({ required: true, integer: true }),
|
||||
value: new fields.NumberField({ integer: true }),
|
||||
minCost: new fields.NumberField({ integer: true }),
|
||||
amount: new fields.NumberField({ integer: true }),
|
||||
data: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||
secondaryData: new fields.StringField(),
|
||||
itemUuid: new fields.StringField({ required: true })
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
get canLevelUp() {
|
||||
return this.level.current < this.level.changed;
|
||||
}
|
||||
}
|
||||
35
module/data/actor/environment.mjs
Normal file
35
module/data/actor/environment.mjs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { environmentTypes } from '../../config/actorConfig.mjs';
|
||||
import BaseDataActor from './base.mjs';
|
||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||
|
||||
export default class DhEnvironment extends BaseDataActor {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Environment'];
|
||||
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.environment',
|
||||
type: 'environment'
|
||||
});
|
||||
}
|
||||
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
tier: new fields.StringField({
|
||||
required: true,
|
||||
choices: SYSTEM.GENERAL.tiers,
|
||||
initial: SYSTEM.GENERAL.tiers.tier1.id
|
||||
}),
|
||||
type: new fields.StringField({ choices: environmentTypes }),
|
||||
impulses: new fields.HTMLField(),
|
||||
difficulty: new fields.NumberField({ required: true, initial: 11, integer: true }),
|
||||
potentialAdversaries: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
label: new fields.StringField(),
|
||||
adversaries: new fields.TypedObjectField(new ForeignDocumentUUIDField({ type: 'Actor' }))
|
||||
})
|
||||
)
|
||||
/* Features pending datamodel rework */
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
export default class DhpAdversary extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
resources: new fields.SchemaField({
|
||||
health: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||
min: new fields.NumberField({ initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
stress: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||
min: new fields.NumberField({ initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ initial: 0, integer: true })
|
||||
})
|
||||
}),
|
||||
tier: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.tiers), integer: false }),
|
||||
type: new fields.StringField({
|
||||
choices: Object.keys(SYSTEM.ACTOR.adversaryTypes),
|
||||
integer: false,
|
||||
initial: Object.keys(SYSTEM.ACTOR.adversaryTypes).find(x => x === 'standard')
|
||||
}),
|
||||
description: new fields.StringField({}),
|
||||
motivesAndTactics: new fields.ArrayField(new fields.StringField({})),
|
||||
attackModifier: new fields.NumberField({ integer: true, nullabe: true, initial: null }),
|
||||
attack: new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
range: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.range), integer: false }),
|
||||
damage: new fields.SchemaField({
|
||||
value: new fields.StringField({}),
|
||||
type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false })
|
||||
})
|
||||
}),
|
||||
difficulty: new fields.NumberField({ initial: 1, integer: true }),
|
||||
damageThresholds: new fields.SchemaField({
|
||||
major: new fields.NumberField({ initial: 0, integer: true }),
|
||||
severe: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
id: new fields.StringField({ required: true }),
|
||||
name: new fields.StringField(),
|
||||
value: new fields.NumberField({ integer: true, nullable: true, initial: null })
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
get features() {
|
||||
return this.parent.items.filter(x => x.type === 'feature');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export default class DhpEnvironment extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
resources: new fields.SchemaField({}),
|
||||
tier: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.tiers), integer: false }),
|
||||
type: new fields.StringField({
|
||||
choices: Object.keys(SYSTEM.ACTOR.adversaryTypes),
|
||||
integer: false,
|
||||
initial: Object.keys(SYSTEM.ACTOR.adversaryTypes).find(x => x === 'standard')
|
||||
}),
|
||||
description: new fields.StringField({}),
|
||||
toneAndFeel: new fields.StringField({}),
|
||||
difficulty: new fields.NumberField({ initial: 1, integer: true }),
|
||||
potentialAdversaries: new fields.StringField({})
|
||||
};
|
||||
}
|
||||
|
||||
get features() {
|
||||
return this.parent.items.filter(x => x.type === 'feature');
|
||||
}
|
||||
}
|
||||
|
|
@ -3,40 +3,39 @@
|
|||
* that resolves to either the document, the index(for items in compenidums) or the UUID string.
|
||||
*/
|
||||
export default class ForeignDocumentUUIDField extends foundry.data.fields.DocumentUUIDField {
|
||||
/**
|
||||
* @param {foundry.data.types.DocumentUUIDFieldOptions} [options] Options which configure the behavior of the field
|
||||
* @param {foundry.data.types.DataFieldContext} [context] Additional context which describes the field
|
||||
*/
|
||||
constructor(options, context) {
|
||||
super(options, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {foundry.data.types.DocumentUUIDFieldOptions} [options] Options which configure the behavior of the field
|
||||
* @param {foundry.data.types.DataFieldContext} [context] Additional context which describes the field
|
||||
*/
|
||||
constructor(options, context) {
|
||||
super(options, context);
|
||||
}
|
||||
/** @inheritdoc */
|
||||
static get _defaults() {
|
||||
return foundry.utils.mergeObject(super._defaults, {
|
||||
nullable: true,
|
||||
readonly: false,
|
||||
idOnly: false
|
||||
});
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
static get _defaults() {
|
||||
return foundry.utils.mergeObject(super._defaults, {
|
||||
nullable: true,
|
||||
readonly: false,
|
||||
idOnly: false,
|
||||
});
|
||||
}
|
||||
/**@override */
|
||||
initialize(value, _model, _options = {}) {
|
||||
if (this.idOnly) return value;
|
||||
return (() => {
|
||||
try {
|
||||
const doc = fromUuidSync(value);
|
||||
return doc;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return value ?? null;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
/**@override */
|
||||
initialize(value, _model, _options = {}) {
|
||||
if (this.idOnly) return value;
|
||||
return () => {
|
||||
try {
|
||||
const doc = fromUuidSync(value);
|
||||
return doc;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return value ?? null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**@override */
|
||||
toObject(value) {
|
||||
return value?.uuid ?? value;
|
||||
}
|
||||
/**@override */
|
||||
toObject(value) {
|
||||
return value?.uuid ?? value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getTier } from '../../helpers/utils.mjs';
|
||||
import BaseDataItem from './base.mjs';
|
||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||
|
||||
|
|
@ -6,9 +5,9 @@ export default class DHClass extends BaseDataItem {
|
|||
/** @inheritDoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: "TYPES.Item.class",
|
||||
type: "class",
|
||||
hasDescription: true,
|
||||
label: 'TYPES.Item.class',
|
||||
type: 'class',
|
||||
hasDescription: true
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -19,15 +18,22 @@ export default class DHClass extends BaseDataItem {
|
|||
...super.defineSchema(),
|
||||
domains: new fields.ArrayField(new fields.StringField(), { max: 2 }),
|
||||
|
||||
classItems: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item" })),
|
||||
classItems: new fields.ArrayField(new ForeignDocumentUUIDField({ type: 'Item' })),
|
||||
evasion: new fields.NumberField({ initial: 0, integer: true }),
|
||||
features: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item" })),
|
||||
|
||||
subclasses: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item", required: false, nullable: true, initial: undefined })),
|
||||
features: new fields.ArrayField(new ForeignDocumentUUIDField({ type: 'Item' })),
|
||||
subclasses: new fields.ArrayField(
|
||||
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
|
||||
),
|
||||
inventory: new fields.SchemaField({
|
||||
take: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item", required: false, nullable: true, initial: undefined })),
|
||||
choiceA: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item", required: false, nullable: true, initial: undefined })),
|
||||
choiceB: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item", required: false, nullable: true, initial: undefined })),
|
||||
take: new fields.ArrayField(
|
||||
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
|
||||
),
|
||||
choiceA: new fields.ArrayField(
|
||||
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
|
||||
),
|
||||
choiceB: new fields.ArrayField(
|
||||
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
|
||||
)
|
||||
}),
|
||||
characterGuide: new fields.SchemaField({
|
||||
suggestedTraits: new fields.SchemaField({
|
||||
|
|
@ -38,15 +44,45 @@ export default class DHClass extends BaseDataItem {
|
|||
presence: new fields.NumberField({ initial: 0, integer: true }),
|
||||
knowledge: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
suggestedPrimaryWeapon: new ForeignDocumentUUIDField({ type: "Item" }),
|
||||
suggestedSecondaryWeapon: new ForeignDocumentUUIDField({ type: "Item" }),
|
||||
suggestedArmor: new ForeignDocumentUUIDField({ type: "Item" }),
|
||||
suggestedPrimaryWeapon: new ForeignDocumentUUIDField({ type: 'Item' }),
|
||||
suggestedSecondaryWeapon: new ForeignDocumentUUIDField({ type: 'Item' }),
|
||||
suggestedArmor: new ForeignDocumentUUIDField({ type: 'Item' })
|
||||
}),
|
||||
multiclass: new fields.NumberField({ initial: null, nullable: true, integer: true }),
|
||||
isMulticlass: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
}
|
||||
|
||||
get multiclassTier() {
|
||||
return getTier(this.multiclass, true);
|
||||
async _preCreate(data, options, user) {
|
||||
const allowed = await super._preCreate(data, options, user);
|
||||
if (allowed === false) return;
|
||||
|
||||
if (this.actor?.type === 'character') {
|
||||
const path = data.system.isMulticlass ? 'system.multiclass.value' : 'system.class.value';
|
||||
if (foundry.utils.getProperty(this.actor, path)) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.ClassAlreadySelected'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onCreate(data, options, userId) {
|
||||
super._onCreate(data, options, userId);
|
||||
if (options.parent?.type === 'character') {
|
||||
const path = `system.${data.system.isMulticlass ? 'multiclass.value' : 'class.value'}`;
|
||||
options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` });
|
||||
}
|
||||
}
|
||||
|
||||
_onDelete(options, userId) {
|
||||
super._onDelete(options, userId);
|
||||
|
||||
if (options.parent?.type === 'character') {
|
||||
const path = `system.${this.isMulticlass ? 'multiclass' : 'class'}`;
|
||||
options.parent.update({
|
||||
[`${path}.value`]: null
|
||||
});
|
||||
|
||||
foundry.utils.getProperty(options.parent, `${path}.subclass`)?.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ export default class DHDomainCard extends BaseDataItem {
|
|||
/** @inheritDoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: "TYPES.Item.domainCard",
|
||||
type: "domainCard",
|
||||
hasDescription: true,
|
||||
label: 'TYPES.Item.domainCard',
|
||||
type: 'domainCard',
|
||||
hasDescription: true
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -19,10 +19,37 @@ export default class DHDomainCard extends BaseDataItem {
|
|||
domain: new fields.StringField({ choices: SYSTEM.DOMAIN.domains, required: true, blank: true }),
|
||||
level: new fields.NumberField({ initial: 1, integer: true }),
|
||||
recallCost: new fields.NumberField({ initial: 0, integer: true }),
|
||||
type: new fields.StringField({ choices: SYSTEM.DOMAIN.cardTypes, required: true, blank: true}),
|
||||
type: new fields.StringField({ choices: SYSTEM.DOMAIN.cardTypes, required: true, blank: true }),
|
||||
foundation: new fields.BooleanField({ initial: false }),
|
||||
inVault: new fields.BooleanField({ initial: false }),
|
||||
actions: new fields.ArrayField(new fields.EmbeddedDataField(DHAction))
|
||||
};
|
||||
}
|
||||
|
||||
async _preCreate(data, options, user) {
|
||||
const allowed = await super._preCreate(data, options, user);
|
||||
if (allowed === false) return;
|
||||
|
||||
if (this.actor?.type === 'character') {
|
||||
if (!this.actor.system.class.value) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.NoClassSelected'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.actor.system.domains.find(x => x === item.system.domain)) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.LacksDomain'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.actor.system.domainCards.total.length === 5) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.MaxLoadoutReached'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.actor.system.domainCards.total.find(x => x.name === item.name)) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.DuplicateDomainCard'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { getTier } from '../../helpers/utils.mjs';
|
||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||
import BaseDataItem from './base.mjs';
|
||||
|
||||
export default class DHSubclass extends BaseDataItem {
|
||||
/** @inheritDoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: "TYPES.Item.subclass",
|
||||
type: "subclass",
|
||||
hasDescription: true,
|
||||
label: 'TYPES.Item.subclass',
|
||||
type: 'subclass',
|
||||
hasDescription: true
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -22,45 +22,48 @@ export default class DHSubclass extends BaseDataItem {
|
|||
nullable: true,
|
||||
initial: null
|
||||
}),
|
||||
foundationFeature: new fields.SchemaField({
|
||||
description: new fields.HTMLField({}),
|
||||
abilities: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
img: new fields.StringField({}),
|
||||
uuid: new fields.StringField({})
|
||||
})
|
||||
)
|
||||
}),
|
||||
specializationFeature: new fields.SchemaField({
|
||||
unlocked: new fields.BooleanField({ initial: false }),
|
||||
tier: new fields.NumberField({ initial: null, nullable: true, integer: true }),
|
||||
description: new fields.HTMLField({}),
|
||||
abilities: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
img: new fields.StringField({}),
|
||||
uuid: new fields.StringField({})
|
||||
})
|
||||
)
|
||||
}),
|
||||
masteryFeature: new fields.SchemaField({
|
||||
unlocked: new fields.BooleanField({ initial: false }),
|
||||
tier: new fields.NumberField({ initial: null, nullable: true, integer: true }),
|
||||
description: new fields.HTMLField({}),
|
||||
abilities: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
img: new fields.StringField({}),
|
||||
uuid: new fields.StringField({})
|
||||
})
|
||||
)
|
||||
}),
|
||||
multiclass: new fields.NumberField({ initial: null, nullable: true, integer: true })
|
||||
foundationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
|
||||
specializationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
|
||||
masteryFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
|
||||
isMulticlass: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
}
|
||||
|
||||
get multiclassTier() {
|
||||
return getTier(this.multiclass);
|
||||
async _preCreate(data, options, user) {
|
||||
const allowed = await super._preCreate(data, options, user);
|
||||
if (allowed === false) return;
|
||||
|
||||
if (this.actor?.type === 'character') {
|
||||
const path = data.system.isMulticlass ? 'system.multiclass' : 'system.class';
|
||||
const classData = foundry.utils.getProperty(this.actor, path);
|
||||
if (!classData.value) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.MissingClass'));
|
||||
return false;
|
||||
} else if (classData.subclass) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassAlreadySelected'));
|
||||
return false;
|
||||
} else if (classData.value.system.subclasses.every(x => x.uuid !== `Item.${data._id}`)) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassNotInClass'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onCreate(data, options, userId) {
|
||||
super._onCreate(data, options, userId);
|
||||
|
||||
if (options.parent?.type === 'character') {
|
||||
const path = `system.${data.system.isMulticlass ? 'multiclass.subclass' : 'class.subclass'}`;
|
||||
options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` });
|
||||
}
|
||||
}
|
||||
|
||||
_onDelete(options, userId) {
|
||||
super._onDelete(options, userId);
|
||||
|
||||
if (options.parent?.type === 'character') {
|
||||
const path = `system.${this.isMulticlass ? 'multiclass.subclass' : 'class.subclass'}`;
|
||||
options.parent.update({ [path]: null });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,412 +0,0 @@
|
|||
import { getPathValue } from '../helpers/utils.mjs';
|
||||
import { LevelOptionType } from './levelTier.mjs';
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
const attributeField = () =>
|
||||
new fields.SchemaField({
|
||||
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||
base: new fields.NumberField({ initial: 0, integer: true }),
|
||||
tierMarked: new fields.BooleanField({ required: true, initial: false })
|
||||
});
|
||||
|
||||
const resourceField = max =>
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||
min: new fields.NumberField({ initial: 0, integer: true }),
|
||||
baseMax: new fields.NumberField({ initial: max, integer: true })
|
||||
});
|
||||
|
||||
export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
return {
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(6),
|
||||
stress: resourceField(6),
|
||||
hope: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: -1, integer: true }), // FIXME. Logic is gte and needs -1 in PC/Hope. Change to 0
|
||||
min: new fields.NumberField({ initial: 0, integer: true })
|
||||
})
|
||||
}),
|
||||
bonuses: new fields.SchemaField({
|
||||
damage: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
type: new fields.StringField({ nullable: true }),
|
||||
initiallySelected: new fields.BooleanField(),
|
||||
hopeIncrease: new fields.StringField({ initial: null, nullable: true }),
|
||||
description: new fields.StringField({})
|
||||
})
|
||||
)
|
||||
}),
|
||||
traits: new fields.SchemaField({
|
||||
agility: attributeField(),
|
||||
strength: attributeField(),
|
||||
finesse: attributeField(),
|
||||
instinct: attributeField(),
|
||||
presence: attributeField(),
|
||||
knowledge: attributeField()
|
||||
}),
|
||||
proficiency: new fields.SchemaField({
|
||||
base: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
||||
bonus: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
evasion: new fields.SchemaField({
|
||||
bonus: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
experiences: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
id: new fields.StringField({ required: true }),
|
||||
description: new fields.StringField({}),
|
||||
value: new fields.NumberField({ integer: true, nullable: true, initial: null })
|
||||
}),
|
||||
{
|
||||
initial: [
|
||||
{ id: foundry.utils.randomID(), description: '', value: 2 },
|
||||
{ id: foundry.utils.randomID(), description: '', value: 2 }
|
||||
]
|
||||
}
|
||||
),
|
||||
gold: new fields.SchemaField({
|
||||
coins: new fields.NumberField({ initial: 0, integer: true }),
|
||||
handfulls: new fields.NumberField({ initial: 0, integer: true }),
|
||||
bags: new fields.NumberField({ initial: 0, integer: true }),
|
||||
chests: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
pronouns: new fields.StringField({}),
|
||||
domainData: new fields.SchemaField({
|
||||
maxLoadout: new fields.NumberField({ initial: 2, integer: true }),
|
||||
maxCards: new fields.NumberField({ initial: 2, integer: true })
|
||||
}),
|
||||
story: new fields.SchemaField({
|
||||
background: new fields.HTMLField(),
|
||||
appearance: new fields.HTMLField(),
|
||||
connections: new fields.HTMLField(),
|
||||
scars: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
description: new fields.HTMLField()
|
||||
})
|
||||
)
|
||||
}),
|
||||
description: new fields.StringField({}),
|
||||
//Temporary until new FoundryVersion fix --> See Armor.Mjs DataPreparation
|
||||
armorMarks: new fields.SchemaField({
|
||||
max: new fields.NumberField({ initial: 6, integer: true }),
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
levelData: new fields.EmbeddedDataField(DhPCLevelData)
|
||||
};
|
||||
}
|
||||
|
||||
get tier() {
|
||||
return this.#getTier(this.levelData.currentLevel);
|
||||
}
|
||||
|
||||
get ancestry() {
|
||||
return this.parent.items.find(x => x.type === 'ancestry') ?? null;
|
||||
}
|
||||
|
||||
get class() {
|
||||
return this.parent.items.find(x => x.type === 'class' && !x.system.multiclass) ?? null;
|
||||
}
|
||||
|
||||
get multiclass() {
|
||||
return this.parent.items.find(x => x.type === 'class' && x.system.multiclass) ?? null;
|
||||
}
|
||||
|
||||
get multiclassSubclass() {
|
||||
return this.parent.items.find(x => x.type === 'subclass' && x.system.multiclass) ?? null;
|
||||
}
|
||||
|
||||
get subclass() {
|
||||
return this.parent.items.find(x => x.type === 'subclass' && !x.system.multiclass) ?? null;
|
||||
}
|
||||
|
||||
get subclassFeatures() {
|
||||
const subclass = this.subclass;
|
||||
const multiclass = this.multiclassSubclass;
|
||||
const subclassItems = this.parent.items.filter(x => x.type === 'feature' && x.system.type === 'subclass');
|
||||
return {
|
||||
subclass: !subclass
|
||||
? {}
|
||||
: {
|
||||
foundation: subclassItems.filter(x =>
|
||||
subclass.system.foundationFeature.abilities.some(ability => ability.uuid === x.uuid)
|
||||
),
|
||||
specialization: subclassItems.filter(x =>
|
||||
subclass.system.specializationFeature.abilities.some(ability => ability.uuid === x.uuid)
|
||||
),
|
||||
mastery: subclassItems.filter(x =>
|
||||
subclass.system.masteryFeature.abilities.some(ability => ability.uuid === x.uuid)
|
||||
)
|
||||
},
|
||||
multiclassSubclass: !multiclass
|
||||
? {}
|
||||
: {
|
||||
foundation: subclassItems.filter(x =>
|
||||
multiclass.system.foundationFeature.abilities.some(ability => ability.uuid === x.uuid)
|
||||
),
|
||||
specialization: subclassItems.filter(x =>
|
||||
multiclass.system.specializationFeature.abilities.some(ability => ability.uuid === x.uuid)
|
||||
),
|
||||
mastery: subclassItems.filter(x =>
|
||||
multiclass.system.masteryFeature.abilities.some(ability => ability.uuid === x.uuid)
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get community() {
|
||||
return this.parent.items.find(x => x.type === 'community') ?? null;
|
||||
}
|
||||
|
||||
get classFeatures() {
|
||||
return this.parent.items.filter(
|
||||
x => x.type === 'feature' && x.system.type === SYSTEM.ITEM.featureTypes.class.id && !x.system.multiclass
|
||||
);
|
||||
}
|
||||
|
||||
get multiclassFeatures() {
|
||||
return this.parent.items.filter(
|
||||
x => x.type === 'feature' && x.system.type === SYSTEM.ITEM.featureTypes.class.id && x.system.multiclass
|
||||
);
|
||||
}
|
||||
|
||||
get domains() {
|
||||
const classDomains = this.class ? this.class.system.domains : [];
|
||||
const multiclassDomains = this.multiclass ? this.multiclass.system.domains : [];
|
||||
return [...classDomains, ...multiclassDomains];
|
||||
}
|
||||
|
||||
get domainCards() {
|
||||
const domainCards = this.parent.items.filter(x => x.type === 'domainCard');
|
||||
const loadout = domainCards.filter(x => !x.system.inVault);
|
||||
const vault = domainCards.filter(x => x.system.inVault);
|
||||
|
||||
return {
|
||||
loadout: loadout,
|
||||
vault: vault,
|
||||
total: [...loadout, ...vault]
|
||||
};
|
||||
}
|
||||
|
||||
get armor() {
|
||||
return this.parent.items.find(x => x.type === 'armor' && x.system.equipped);
|
||||
}
|
||||
|
||||
get equippedWeapons() {
|
||||
const primaryWeapon = this.parent.items.find(
|
||||
x => x.type === 'weapon' && x.system.equipped && !x.system.secondary
|
||||
);
|
||||
const secondaryWeapon = this.parent.items.find(
|
||||
x => x.type === 'weapon' && x.system.equipped && x.system.secondary
|
||||
);
|
||||
return {
|
||||
primary: this.#weaponData(primaryWeapon),
|
||||
secondary: this.#weaponData(secondaryWeapon),
|
||||
burden: this.getBurden(primaryWeapon, secondaryWeapon)
|
||||
};
|
||||
}
|
||||
|
||||
static async unequipBeforeEquip(itemToEquip) {
|
||||
const equippedWeapons = this.equippedWeapons;
|
||||
|
||||
if (itemToEquip.system.secondary) {
|
||||
if (equippedWeapons.primary && equippedWeapons.primary.burden === SYSTEM.GENERAL.burden.twoHanded.value) {
|
||||
await this.parent.items.get(equippedWeapons.primary.id).update({ 'system.equipped': false });
|
||||
}
|
||||
|
||||
if (equippedWeapons.secondary) {
|
||||
await this.parent.items.get(equippedWeapons.secondary.id).update({ 'system.equipped': false });
|
||||
}
|
||||
} else {
|
||||
if (equippedWeapons.secondary && itemToEquip.system.burden === SYSTEM.GENERAL.burden.twoHanded.value) {
|
||||
await this.parent.items.get(equippedWeapons.secondary.id).update({ 'system.equipped': false });
|
||||
}
|
||||
|
||||
if (equippedWeapons.primary) {
|
||||
await this.parent.items.get(equippedWeapons.primary.id).update({ 'system.equipped': false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get effects() {
|
||||
return this.parent.items.reduce((acc, item) => {
|
||||
const effects = item.system.effectData;
|
||||
if (effects && !item.system.disabled) {
|
||||
for (var key in effects) {
|
||||
const effect = effects[key];
|
||||
for (var effectEntry of effect) {
|
||||
if (!acc[key]) acc[key] = [];
|
||||
acc[key].push({ name: item.name, value: effectEntry });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
get refreshableFeatures() {
|
||||
return this.parent.items.reduce(
|
||||
(acc, x) => {
|
||||
if (x.type === 'feature' && x.system.refreshData?.type === 'feature' && x.system.refreshData?.type) {
|
||||
acc[x.system.refreshData.type].push(x);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ shortRest: [], longRest: [] }
|
||||
);
|
||||
}
|
||||
|
||||
//Should not be done in data?
|
||||
//TODO: REMOVE THIS
|
||||
#weaponData(weapon) {
|
||||
return weapon
|
||||
? {
|
||||
id: weapon.id,
|
||||
name: weapon.name,
|
||||
trait: game.i18n.localize(CONFIG.daggerheart.ACTOR.abilities[weapon.system.trait].label),
|
||||
range: CONFIG.daggerheart.GENERAL.range[weapon.system.range],
|
||||
damage: {
|
||||
value: weapon.system.damage.value,
|
||||
type: CONFIG.daggerheart.GENERAL.damageTypes[weapon.system.damage.type]
|
||||
},
|
||||
burden: weapon.system.burden,
|
||||
feature: CONFIG.daggerheart.ITEM.weaponFeatures[weapon.system.feature],
|
||||
img: weapon.img,
|
||||
uuid: weapon.uuid
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
prepareBaseData() {
|
||||
this.resources.hitPoints.max = this.resources.hitPoints.baseMax + this.resources.hitPoints.bonus;
|
||||
this.resources.stress.max = this.resources.stress.baseMax + this.resources.stress.bonus;
|
||||
this.evasion.value = (this.class?.system?.evasion ?? 0) + this.evasion.bonus;
|
||||
this.proficiency.value = this.proficiency.base + this.proficiency.bonus;
|
||||
|
||||
for (var attributeKey in this.traits) {
|
||||
const attribute = this.traits[attributeKey];
|
||||
attribute.value = attribute.base + attribute.bonus;
|
||||
}
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
this.resources.hope.max = 6 - this.story.scars.length;
|
||||
if (this.resources.hope.value >= this.resources.hope.max) {
|
||||
this.resources.hope.value = Math.max(this.resources.hope.max - 1, 0);
|
||||
}
|
||||
|
||||
const armor = this.armor;
|
||||
this.damageThresholds = {
|
||||
major: armor
|
||||
? armor.system.baseThresholds.major + this.levelData.level.current
|
||||
: this.levelData.level.current,
|
||||
severe: armor
|
||||
? armor.system.baseThresholds.severe + this.levelData.level.current
|
||||
: this.levelData.level.current * 2
|
||||
};
|
||||
|
||||
this.applyEffects();
|
||||
}
|
||||
|
||||
applyEffects() {
|
||||
const effects = this.effects;
|
||||
for (var key in effects) {
|
||||
const effectType = effects[key];
|
||||
for (var effect of effectType) {
|
||||
switch (key) {
|
||||
case SYSTEM.EFFECTS.effectTypes.health.id:
|
||||
this.resources.hitPoints.bonus += effect.value.valueData.value;
|
||||
break;
|
||||
case SYSTEM.EFFECTS.effectTypes.stress.id:
|
||||
this.resources.stress.bonus += effect.value.valueData.value;
|
||||
break;
|
||||
case SYSTEM.EFFECTS.effectTypes.damage.id:
|
||||
this.bonuses.damage.push({
|
||||
value: getPathValue(effect.value.valueData.value, this),
|
||||
type: 'physical',
|
||||
description: effect.name,
|
||||
hopeIncrease: effect.value.valueData.hopeIncrease,
|
||||
initiallySelected: effect.value.initiallySelected,
|
||||
appliesOn: effect.value.appliesOn
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBurden(primary, secondary) {
|
||||
const twoHanded =
|
||||
primary?.system?.burden === 'twoHanded' ||
|
||||
secondary?.system?.burden === 'twoHanded' ||
|
||||
(primary?.system?.burden === 'oneHanded' && secondary?.system?.burden === 'oneHanded');
|
||||
const oneHanded =
|
||||
!twoHanded && (primary?.system?.burden === 'oneHanded' || secondary?.system?.burden === 'oneHanded');
|
||||
|
||||
return twoHanded ? 'twoHanded' : oneHanded ? 'oneHanded' : null;
|
||||
}
|
||||
|
||||
#getTier(level) {
|
||||
if (level >= 8) return 3;
|
||||
else if (level >= 5) return 2;
|
||||
else if (level >= 2) return 1;
|
||||
else return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class DhPCLevelData extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
return {
|
||||
level: new fields.SchemaField({
|
||||
current: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||
changed: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
||||
}),
|
||||
levelups: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
achievements: new fields.SchemaField(
|
||||
{
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true }),
|
||||
modifier: new fields.NumberField({ required: true, integer: true })
|
||||
})
|
||||
),
|
||||
domainCards: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
uuid: new fields.StringField({ required: true }),
|
||||
itemUuid: new fields.StringField({ required: true })
|
||||
})
|
||||
),
|
||||
proficiency: new fields.NumberField({ integer: true })
|
||||
},
|
||||
{ nullable: true, initial: null }
|
||||
),
|
||||
selections: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
tier: new fields.NumberField({ required: true, integer: true }),
|
||||
level: new fields.NumberField({ required: true, integer: true }),
|
||||
optionKey: new fields.StringField({ required: true }),
|
||||
type: new fields.StringField({ required: true, choices: LevelOptionType }),
|
||||
checkboxNr: new fields.NumberField({ required: true, integer: true }),
|
||||
value: new fields.NumberField({ integer: true }),
|
||||
minCost: new fields.NumberField({ integer: true }),
|
||||
amount: new fields.NumberField({ integer: true }),
|
||||
data: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||
secondaryData: new fields.StringField(),
|
||||
itemUuid: new fields.StringField({ required: true })
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
get canLevelUp() {
|
||||
return this.level.current < this.level.changed;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
export default class DhVariantRules extends foundry.abstract.DataModel {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Settings.VariantRules'];
|
||||
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
actionTokens: new fields.SchemaField({
|
||||
enabled: new fields.BooleanField({ required: true, initial: false }),
|
||||
tokens: new fields.NumberField({ required: true, integer: true, initial: 3 })
|
||||
})
|
||||
}),
|
||||
useCoins: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export default class DhpActor extends Actor {
|
|||
|
||||
// Configure prototype token settings
|
||||
const prototypeToken = {};
|
||||
if (this.type === 'pc')
|
||||
if (this.type === 'character')
|
||||
Object.assign(prototypeToken, {
|
||||
sight: { enabled: true },
|
||||
actorLink: true,
|
||||
|
|
@ -28,7 +28,7 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
|
||||
async updateLevel(newLevel) {
|
||||
if (this.type !== 'pc' || newLevel === this.system.levelData.level.changed) return;
|
||||
if (this.type !== 'character' || newLevel === this.system.levelData.level.changed) return;
|
||||
|
||||
if (newLevel > this.system.levelData.level.current) {
|
||||
await this.update({ 'system.levelData.level.changed': newLevel });
|
||||
|
|
@ -124,7 +124,7 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
|
||||
async diceRoll(modifier, shiftKey) {
|
||||
if (this.type === 'pc') {
|
||||
if (this.type === 'character') {
|
||||
return await this.dualityRoll(modifier, shiftKey);
|
||||
} else {
|
||||
return await this.npcRoll(modifier, shiftKey);
|
||||
|
|
@ -173,12 +173,11 @@ export default class DhpActor extends Actor {
|
|||
return { roll, dice: dice[0], modifiers, advantageState: advantage === true ? 1 : advantage === false ? 2 : 0 };
|
||||
}
|
||||
|
||||
async dualityRoll(modifier, shiftKey, bonusDamage = []) {
|
||||
async dualityRoll(modifier, shiftKey) {
|
||||
let hopeDice = 'd12',
|
||||
fearDice = 'd12',
|
||||
advantageDice = null,
|
||||
disadvantageDice = null,
|
||||
bonusDamageString = '';
|
||||
disadvantageDice = null;
|
||||
|
||||
const modifiers =
|
||||
modifier.value !== null
|
||||
|
|
@ -195,12 +194,9 @@ export default class DhpActor extends Actor {
|
|||
: [];
|
||||
if (!shiftKey) {
|
||||
const dialogClosed = new Promise((resolve, _) => {
|
||||
new RollSelectionDialog(
|
||||
this.system.experiences,
|
||||
bonusDamage,
|
||||
this.system.resources.hope.value,
|
||||
resolve
|
||||
).render(true);
|
||||
new RollSelectionDialog(this.system.experiences, this.system.resources.hope.value, resolve).render(
|
||||
true
|
||||
);
|
||||
});
|
||||
const result = await dialogClosed;
|
||||
(hopeDice = result.hope),
|
||||
|
|
@ -214,7 +210,6 @@ export default class DhpActor extends Actor {
|
|||
title: x.description
|
||||
})
|
||||
);
|
||||
bonusDamageString = result.bonusDamage;
|
||||
|
||||
const automateHope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope);
|
||||
|
||||
|
|
@ -268,8 +263,7 @@ export default class DhpActor extends Actor {
|
|||
fear: { dice: fearDice, value: fear },
|
||||
advantage: { dice: advantageDice, value: advantage },
|
||||
disadvantage: { dice: disadvantageDice, value: disadvantage },
|
||||
modifiers: modifiers,
|
||||
bonusDamageString
|
||||
modifiers: modifiers
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -401,11 +395,6 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
}
|
||||
|
||||
async emulateItemDrop(data) {
|
||||
const event = new DragEvent('drop', { altKey: game.keyboard.isModifierActive('Alt') });
|
||||
return this.sheet._onDropItem(event, { data: data });
|
||||
}
|
||||
|
||||
//Move to action-scope?
|
||||
async useAction(action) {
|
||||
const userTargets = Array.from(game.user.targets);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,6 @@
|
|||
export default class DhpItem extends Item {
|
||||
_preCreate(data, changes, user) {
|
||||
super._preCreate(data, changes, user);
|
||||
}
|
||||
|
||||
prepareData() {
|
||||
super.prepareData();
|
||||
|
||||
if (this.type === 'class') {
|
||||
// Bad. Make this better.
|
||||
// this.system.domains = CONFIG.daggerheart.DOMAIN.classDomainMap[Object.keys(CONFIG.daggerheart.DOMAIN.classDomainMap).find(x => x === this.name.toLowerCase())];
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
|
@ -22,7 +13,7 @@ export default class DhpItem extends Item {
|
|||
/**
|
||||
* @inheritdoc
|
||||
* @param {object} options - Options which modify the getRollData method.
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
getRollData(options = {}) {
|
||||
let data;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export default class RegisterHandlebarsHelpers {
|
|||
includes: this.includes,
|
||||
debug: this.debug,
|
||||
signedNumber: this.signedNumber,
|
||||
length: this.length,
|
||||
switch: this.switch,
|
||||
case: this.case
|
||||
});
|
||||
|
|
@ -82,6 +83,10 @@ export default class RegisterHandlebarsHelpers {
|
|||
return number >= 0 ? `+${number}` : number;
|
||||
}
|
||||
|
||||
static length(obj) {
|
||||
return Object.keys(obj).length;
|
||||
}
|
||||
|
||||
static switch(value, options) {
|
||||
this.switch_value = value;
|
||||
this.switch_break = false;
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ export const getCommandTarget = () => {
|
|||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.NoSelectedToken'));
|
||||
return null;
|
||||
}
|
||||
if (target.type !== 'pc') {
|
||||
if (target.type !== 'character') {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.OnlyUseableByPC'));
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2370,229 +2370,6 @@ div.daggerheart.views.multiclass {
|
|||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-header-container {
|
||||
position: relative;
|
||||
background-color: grey;
|
||||
display: flex;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-header-container .adversary-header {
|
||||
flex: 1;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-header-container .adversary-header img {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-header-container .adversary-header .adversary-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-header-container .adversary-header .adversary-title .title-text {
|
||||
width: 100%;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-header-container .adversary-header .adversary-title input {
|
||||
font-size: 28px;
|
||||
border: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-header-container .adversary-toggle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: white;
|
||||
color: black;
|
||||
flex: 0;
|
||||
}
|
||||
.daggerheart.sheet.adversary .motive-container {
|
||||
background: lightgrey;
|
||||
margin-bottom: 8px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .motive-container .motive-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.daggerheart.sheet.adversary .motive-container .motive-title .motive-title-base {
|
||||
font-size: 21px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .motive-container .motive-title .motive-title-value {
|
||||
font-style: italic;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .motive-container .motive-title i {
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.daggerheart.sheet.adversary .motive-container .motive-title i:hover {
|
||||
filter: drop-shadow(0 0 3px red);
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-content-container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container {
|
||||
flex: 1;
|
||||
margin-right: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-title {
|
||||
flex: 0;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-row .statistic-value {
|
||||
flex: 0;
|
||||
white-space: nowrap;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-row .adversary-roll {
|
||||
border: 0;
|
||||
width: 16px;
|
||||
margin-left: 4px;
|
||||
align-self: baseline;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-row .adversary-roll:hover {
|
||||
transform: rotate(30deg);
|
||||
filter: drop-shadow(0px 0px 3px red);
|
||||
cursor: pointer;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-resource-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-resource-container label {
|
||||
min-width: 44px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-resource-container .statistic-resource-inner-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-resource-container .resource-title {
|
||||
align-self: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-resource-container .statistic-resource-input {
|
||||
margin: 0;
|
||||
flex: 0;
|
||||
min-width: 16px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .attack-container {
|
||||
border: 1px solid black dotted;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .experience-row {
|
||||
display: flex;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .experience-row * {
|
||||
flex: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .experience-container i {
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .experience-container i:hover {
|
||||
filter: drop-shadow(0 0 3px red);
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .experience-chip {
|
||||
border: 2px solid #708090;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .experience-chip .experience-text {
|
||||
flex: 1;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .experience-chip .experience-value {
|
||||
flex: 0;
|
||||
min-width: 26px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-statistics-container .experience-chip .experience-button {
|
||||
flex: 0;
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-damage-threshold-container input {
|
||||
min-width: 26px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-moves-container {
|
||||
flex: 2.5;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-moves-container .moves-title {
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-moves-container .move-container {
|
||||
cursor: pointer;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-moves-container .move-container:hover {
|
||||
background: #2f4f4f40;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-moves-container .move-container .moves-name {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-moves-container .move-container .move-description p {
|
||||
margin-top: 0;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-moves-container .moves-edit-container i {
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.daggerheart.sheet.adversary .adversary-moves-container .moves-edit-container i:hover {
|
||||
filter: drop-shadow(0 0 3px red);
|
||||
}
|
||||
.daggerheart.sheet.adversary .chip-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #778899;
|
||||
padding: 8px;
|
||||
border: 2px solid black;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .chip-container:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .chip-container .chip-inner-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.daggerheart.sheet.adversary .chip-container .chip-inner-container img {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.daggerheart.sheet.adversary .chip-container .chip-inner-container .chip-title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
.daggerheart.sheet.adversary .chip-container button {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
background: white;
|
||||
}
|
||||
.daggerheart.sheet .title-container {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
|
@ -3078,6 +2855,31 @@ div.daggerheart.views.multiclass {
|
|||
#resources:has(.fear-bar) {
|
||||
min-width: 200px;
|
||||
}
|
||||
.application.sheet.daggerheart.actor.dh-style.adversary .window-content {
|
||||
overflow: auto;
|
||||
}
|
||||
.daggerheart.sheet.actor.environment .potential-adversary-container {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
}
|
||||
.daggerheart.sheet.actor.environment .potential-adversary-container .adversary-placeholder {
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.daggerheart.sheet.actor.environment .potential-adversary-container .adversaries-container {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.daggerheart.sheet.actor.environment .potential-adversary-container .adversaries-container .adversary-container {
|
||||
border: 1px solid var(--color-dark-5);
|
||||
border-radius: 6px;
|
||||
padding: 0 2px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
||||
color: var(--color-light-3);
|
||||
}
|
||||
.application.sheet.daggerheart.dh-style.feature .item-sheet-header {
|
||||
display: flex;
|
||||
}
|
||||
|
|
@ -3354,6 +3156,12 @@ div.daggerheart.views.multiclass {
|
|||
grid-template-columns: 1fr 2fr;
|
||||
gap: 10px;
|
||||
}
|
||||
.application.sheet.dh-style fieldset.two-columns.even {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
.application.sheet.dh-style fieldset.two-columns .full-width {
|
||||
grid-column: span 2;
|
||||
}
|
||||
.application.sheet.dh-style fieldset legend {
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-weight: bold;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@
|
|||
@import './resources.less';
|
||||
|
||||
// new styles imports
|
||||
@import './less/actors/character.less';
|
||||
@import './less/actors/adversary.less';
|
||||
@import './less/actors/environment.less';
|
||||
|
||||
@import './less/items/feature.less';
|
||||
@import './less/items/domainCard.less';
|
||||
@import './less/items/class.less';
|
||||
|
|
|
|||
5
styles/less/actors/adversary.less
Normal file
5
styles/less/actors/adversary.less
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.application.sheet.daggerheart.actor.dh-style.adversary {
|
||||
.window-content {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
0
styles/less/actors/character.less
Normal file
0
styles/less/actors/character.less
Normal file
27
styles/less/actors/environment.less
Normal file
27
styles/less/actors/environment.less
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
.daggerheart.sheet.actor.environment {
|
||||
.potential-adversary-container {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
|
||||
.adversary-placeholder {
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.adversaries-container {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.adversary-container {
|
||||
border: 1px solid var(--color-dark-5);
|
||||
border-radius: 6px;
|
||||
padding: 0 2px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
||||
color: var(--color-light-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -103,6 +103,14 @@
|
|||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 10px;
|
||||
|
||||
&.even {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
|
||||
legend {
|
||||
|
|
|
|||
|
|
@ -1,280 +0,0 @@
|
|||
.daggerheart.sheet.adversary {
|
||||
.adversary-header-container {
|
||||
position: relative;
|
||||
background-color: grey;
|
||||
display: flex;
|
||||
|
||||
.adversary-header {
|
||||
flex: 1;
|
||||
|
||||
img {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.adversary-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
|
||||
.title-text {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 28px;
|
||||
border: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adversary-toggle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: white;
|
||||
color: black;
|
||||
flex: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.motive-container {
|
||||
background: lightgrey;
|
||||
margin-bottom: @fullMargin;
|
||||
padding-bottom: @fullPadding;
|
||||
|
||||
.motive-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.motive-title-base {
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.motive-title-value {
|
||||
font-style: italic;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
filter: drop-shadow(0 0 3px red);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adversary-content-container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.adversary-statistics-container {
|
||||
flex: 1;
|
||||
margin-right: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: @mediumMargin;
|
||||
|
||||
.statistic-title {
|
||||
flex: 0;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.statistic-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.statistic-value {
|
||||
flex: 0;
|
||||
white-space: nowrap;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.adversary-roll {
|
||||
border: 0;
|
||||
width: 16px;
|
||||
margin-left: 4px;
|
||||
align-self: baseline;
|
||||
transition: transform 0.2s;
|
||||
|
||||
&:hover {
|
||||
transform: rotate(30deg);
|
||||
filter: drop-shadow(0px 0px 3px red);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statistic-resource-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
label {
|
||||
min-width: 44px;
|
||||
}
|
||||
|
||||
.statistic-resource-inner-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: @halfMargin;
|
||||
}
|
||||
|
||||
.resource-title {
|
||||
align-self: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.statistic-resource-input {
|
||||
margin: 0;
|
||||
flex: 0;
|
||||
min-width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.attack-container {
|
||||
border: 1px solid black dotted;
|
||||
}
|
||||
|
||||
.experience-row {
|
||||
display: flex;
|
||||
|
||||
* {
|
||||
flex: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.experience-container {
|
||||
i {
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
filter: drop-shadow(0 0 3px red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.experience-chip {
|
||||
border: 2px solid @secondaryAccent;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
margin-bottom: 6px;
|
||||
|
||||
.experience-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.experience-value {
|
||||
flex: 0;
|
||||
min-width: @inputSingleMinWidth;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.experience-button {
|
||||
flex: 0;
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adversary-damage-threshold-container {
|
||||
input {
|
||||
min-width: @inputSingleMinWidth;
|
||||
}
|
||||
}
|
||||
|
||||
.adversary-moves-container {
|
||||
flex: 2.5;
|
||||
|
||||
.moves-title {
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
}
|
||||
.move-container {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: @hoverBackground;
|
||||
}
|
||||
|
||||
.moves-name {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.move-description {
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.moves-edit-container {
|
||||
i {
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
filter: drop-shadow(0 0 3px red);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chip-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: @primaryAccent;
|
||||
padding: 8px;
|
||||
border: 2px solid black;
|
||||
border-radius: 6px;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.chip-inner-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.chip-title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
@import './heritage.less';
|
||||
@import './class.less';
|
||||
@import './adversary.less';
|
||||
|
||||
.daggerheart.sheet {
|
||||
.title-container {
|
||||
|
|
|
|||
24
system.json
24
system.json
|
|
@ -163,10 +163,7 @@
|
|||
"name": "Daggerheart",
|
||||
"sorting": "m",
|
||||
"color": "#08718c",
|
||||
"packs": [
|
||||
"adversaries",
|
||||
"environments"
|
||||
],
|
||||
"packs": ["adversaries", "environments"],
|
||||
"folders": [
|
||||
{
|
||||
"name": "Character Options",
|
||||
|
|
@ -186,12 +183,7 @@
|
|||
"name": "Items",
|
||||
"sorting": "m",
|
||||
"color": "#000000",
|
||||
"packs": [
|
||||
"weapons",
|
||||
"armors",
|
||||
"consumables",
|
||||
"general-items"
|
||||
]
|
||||
"packs": ["weapons", "armors", "consumables", "general-items"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -211,9 +203,15 @@
|
|||
},
|
||||
"documentTypes": {
|
||||
"Actor": {
|
||||
"pc": {},
|
||||
"adversary": {},
|
||||
"environment": {}
|
||||
"character": {
|
||||
"htmlFields": ["story", "description", "scars.*.description"]
|
||||
},
|
||||
"adversary": {
|
||||
"htmlFields": ["description", "motivesAndTactics"]
|
||||
},
|
||||
"environment": {
|
||||
"htmlFields": ["description", "impulses"]
|
||||
}
|
||||
},
|
||||
"Item": {
|
||||
"ancestry": {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
<div class="flexrow">
|
||||
<button class="roll-damage-button" data-value="{{this.total.normal}}" data-damage="{{this.damage.value}}" data-damage-type="{{this.damage.type}}" {{#if this.damage.disabled}}disabled{{/if}}><span>Roll Damage</span></button>
|
||||
<button class="duality-action" data-value="{{this.total.normal}}" data-damage="{{this.damage.value}}" data-damage-type="{{this.damage.type}}" {{#if this.damage.disabled}}disabled{{/if}}><span>Roll Damage</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -8,6 +8,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{{formGroup settingFields.schema.fields.useCoins value=settingFields._source.useCoins localize=true }}
|
||||
|
||||
<footer class="form-footer">
|
||||
<button data-action="reset">
|
||||
<i class="fa-solid fa-arrow-rotate-left"></i>
|
||||
|
|
|
|||
9
templates/sheets/actors/adversary/header.hbs
Normal file
9
templates/sheets/actors/adversary/header.hbs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<header class='item-card-header'>
|
||||
<img class='profile' src='{{source.img}}' data-action='editImage' data-edit='img' />
|
||||
<div class='item-info'>
|
||||
<h1 class='item-name'><input type='text' name='name' value='{{source.name}}' /></h1>
|
||||
<div class='item-description'>
|
||||
<h3>{{localize 'TYPES.Actor.adversary'}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
17
templates/sheets/actors/adversary/information.hbs
Normal file
17
templates/sheets/actors/adversary/information.hbs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<section
|
||||
class='tab {{tabs.information.cssClass}} {{tabs.information.id}}'
|
||||
data-tab='{{tabs.information.id}}'
|
||||
data-group='{{tabs.information.group}}'
|
||||
>
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Description" }}</legend>
|
||||
|
||||
{{formGroup systemFields.description value=source.system.description}}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.MotivesAndTactics" }}</legend>
|
||||
|
||||
{{formGroup systemFields.motivesAndTactics value=source.system.motivesAndTactics}}
|
||||
</fieldset>
|
||||
</section>
|
||||
60
templates/sheets/actors/adversary/main.hbs
Normal file
60
templates/sheets/actors/adversary/main.hbs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<section
|
||||
class='tab {{tabs.main.cssClass}} {{tabs.main.id}}'
|
||||
data-tab='{{tabs.main.id}}'
|
||||
data-group='{{tabs.main.group}}'
|
||||
>
|
||||
<div class="adversary-container">
|
||||
<fieldset class="two-columns even">
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.General"}}</legend>
|
||||
|
||||
{{formGroup systemFields.tier value=source.system.tier localize=true}}
|
||||
{{formGroup systemFields.type value=source.system.type localize=true}}
|
||||
<div class="full-width">{{formGroup systemFields.difficulty value=source.system.difficulty}}</div>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.DamageThresholds"}}</legend>
|
||||
|
||||
{{formGroup systemFields.damageThresholds.fields.major value=source.system.damageThresholds.major}}
|
||||
{{formGroup systemFields.damageThresholds.fields.severe value=source.system.damageThresholds.severe}}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.HitPoints"}}</legend>
|
||||
|
||||
{{formGroup systemFields.resources.fields.hitPoints.fields.value value=source.system.resources.hitPoints.value}}
|
||||
{{formGroup systemFields.resources.fields.hitPoints.fields.max value=source.system.resources.hitPoints.max}}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Stress"}}</legend>
|
||||
|
||||
{{formGroup systemFields.resources.fields.stress.fields.value value=source.system.resources.stress.value}}
|
||||
{{formGroup systemFields.resources.fields.stress.fields.max value=source.system.resources.stress.max}}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Experiences"}} <a><i class="fa-solid fa-plus icon-button" data-action="addExperience"></i></a></legend>
|
||||
|
||||
{{#each source.system.experiences}}
|
||||
<fieldset class="one-column">
|
||||
<legend>{{this.name}} <a><i class="fa-solid fa-trash icon-button" data-action="removeExperience" data-experience="{{@key}}"></i></a></legend>
|
||||
|
||||
{{formGroup @root.systemFields.experiences.element.fields.name name=(concat "system.experiences." @key ".name") value=this.name }}
|
||||
{{formGroup @root.systemFields.experiences.element.fields.value name=(concat "system.experiences." @key ".value") value=this.value }}
|
||||
</fieldset>
|
||||
{{/each}}
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="two-columns even">
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Attack"}}</legend>
|
||||
|
||||
{{formGroup systemFields.attack.fields.name value=source.system.attack.name}}
|
||||
<button data-action="attackRoll">Attack</button>
|
||||
{{formGroup systemFields.attack.fields.modifier value=source.system.attack.modifier}}
|
||||
{{formGroup systemFields.attack.fields.range value=source.system.attack.range localize=true}}
|
||||
{{formGroup systemFields.attack.fields.damage.fields.value value=source.system.attack.damage.value}}
|
||||
{{formGroup systemFields.attack.fields.damage.fields.type value=source.system.attack.damage.type localize=true}}
|
||||
</fieldset>
|
||||
</div>
|
||||
</section>
|
||||
9
templates/sheets/actors/environment/header.hbs
Normal file
9
templates/sheets/actors/environment/header.hbs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<header class='item-card-header'>
|
||||
<img class='profile' src='{{source.img}}' data-action='editImage' data-edit='img' />
|
||||
<div class='item-info'>
|
||||
<h1 class='item-name'><input type='text' name='name' value='{{source.name}}' /></h1>
|
||||
<div class='item-description'>
|
||||
<h3>{{localize 'TYPES.Actor.environment'}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
16
templates/sheets/actors/environment/information.hbs
Normal file
16
templates/sheets/actors/environment/information.hbs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<section
|
||||
class='tab {{tabs.information.cssClass}} {{tabs.information.id}}'
|
||||
data-tab='{{tabs.information.id}}'
|
||||
data-group='{{tabs.information.group}}'
|
||||
>
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Environment.description"}}</legend>
|
||||
|
||||
{{formInput systemFields.description value=source.system.description }}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Environment.impulses"}}</legend>
|
||||
|
||||
{{formInput systemFields.impulses value=source.system.impulses }}
|
||||
</fieldset>
|
||||
</section>
|
||||
37
templates/sheets/actors/environment/main.hbs
Normal file
37
templates/sheets/actors/environment/main.hbs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<section
|
||||
class='tab {{tabs.main.cssClass}} {{tabs.main.id}}'
|
||||
data-tab='{{tabs.main.id}}'
|
||||
data-group='{{tabs.main.group}}'
|
||||
>
|
||||
<fieldset class="two-columns even">
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Environment.general"}}</legend>
|
||||
|
||||
{{formGroup systemFields.tier value=source.system.tier localize=true }}
|
||||
{{formGroup systemFields.type value=source.system.type localize=true }}
|
||||
{{formGroup systemFields.difficulty value=source.system.difficulty localize=true }}
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="one-column">
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Environment.potentialAdversaries.label"}}<a><i class="fa-solid fa-plus icon-button" data-action="addAdversary"></i></a></legend>
|
||||
|
||||
{{#each source.system.potentialAdversaries}}
|
||||
<fieldset class="potential-adversary-container" data-potential-adversary="{{@key}}">
|
||||
<legend><input name="{{concat "system.potentialAdversaries." id ".label" }}" value="{{this.label}}" /><i class="fa-solid fa-trash" data-action="deleteProperty" data-path="system.potentialAdversaries" id={{@key}}></i></legend>
|
||||
{{#if (eq (length this.adversaries) 0)}}
|
||||
<div class="adversary-placeholder">{{localize "DAGGERHEART.Sheets.Environment.potentialAdversaries.placeholder"}}</div>
|
||||
{{else}}
|
||||
<div class="adversaries-container">
|
||||
{{#each this.adversaries as |adversary id|}}
|
||||
<div class="adversary-container" data-action="viewAdversary" data-potential-adversary="{{@../key}}" data-adversary="{{id}}">{{adversary.name}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
{{/each}}
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="one-column">
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Environment.features.label"}}<a><i class="fa-solid fa-plus icon-button" data-action="addFeature"></i></a></legend>
|
||||
|
||||
</fieldset>
|
||||
</section>
|
||||
|
|
@ -4,17 +4,17 @@
|
|||
<div class="class-info">
|
||||
<div class="flexrow">
|
||||
<img class="portrait" src="{{document.img}}" alt="{{document.name}}" data-edit="img">
|
||||
{{#if document.system.class}}
|
||||
{{#if document.system.class.value}}
|
||||
<div class="flexcol">
|
||||
<h2 class="class-title flex0" data-action="viewObject" data-value="{{document.system.class.uuid}}" data-tab="guide">
|
||||
<h2 class="class-title flex0" data-action="viewObject" data-value="{{document.system.class.value.uuid}}" data-tab="guide">
|
||||
<img class="domain-image" src="{{domains.first}}" />
|
||||
<span>{{document.system.class.name}}</span>
|
||||
<span>{{document.system.class.value.name}}</span>
|
||||
<img class="domain-image" src="{{domains.second}}" />
|
||||
</h2>
|
||||
<span class="domain-title flex0">
|
||||
<span>{{document.system.class.system.domains.[0]}}</span>
|
||||
<span>{{document.system.class.value.system.domains.[0]}}</span>
|
||||
<span>and</span>
|
||||
<span>{{document.system.class.system.domains.[1]}}</span>
|
||||
<span>{{document.system.class.value.system.domains.[1]}}</span>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="flexcol">
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
{{#if document.system.levelData.canLevelUp}}<div class="levelup-marker">*</div>{{/if}}
|
||||
</div>
|
||||
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
||||
<div class="level-title {{#if document.system.levelData.canLevelUp}}levelup{{/if}}">{{localize "DAGGERHEART.Sheets.PC.Level"}}</div>
|
||||
<div data-action="levelup" class="level-title {{#if document.system.levelData.canLevelUp}}levelup{{/if}}">{{localize "DAGGERHEART.Sheets.PC.Level"}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flexrow">
|
||||
|
|
@ -55,8 +55,8 @@
|
|||
<button data-action="selectCommunity" class="option-select deeper"><i class="fa-solid fa-house-chimney"></i></button>
|
||||
<button data-action="selectAncestry" class="option-select"><i class="fa-solid fa-user-large"></i></button>
|
||||
{{/objectSelector}}
|
||||
{{#objectSelector title="Subclass" ids=(join document.system.subclass.uuid) values=(join document.system.subclass.name) titleFontSize=14}}
|
||||
<button data-action="selectSubclass" class="option-select" {{#if (not ../document.system.class)}}disabled{{/if}}><i class="fa-solid fa-fw fa-search"></i></button>
|
||||
{{#objectSelector title="Subclass" ids=(join document.system.class.subclass.uuid) values=(join document.system.class.subclass.name) titleFontSize=14}}
|
||||
<button data-action="selectSubclass" class="option-select" {{#if (not ../document.system.class.value)}}disabled{{/if}}><i class="fa-solid fa-fw fa-search"></i></button>
|
||||
{{/objectSelector}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
</div>
|
||||
<div class="body-section flex3">
|
||||
{{> "systems/daggerheart/templates/sheets/parts/attributes.hbs" }}
|
||||
{{> "systems/daggerheart/templates/sheets/parts/weapons.hbs" weapons=document.system.equippedWeapons proficiency=document.system.proficiency.value }}
|
||||
{{> "systems/daggerheart/templates/sheets/parts/weapons.hbs" primaryWeapon=document.system.primaryWeapon secondaryWeapon=document.system.secondaryWeapon weaponBurden=document.system.getWeaponBurden proficiency=document.system.proficiency }}
|
||||
{{> "systems/daggerheart/templates/sheets/parts/armor.hbs" armor=document.system.armor }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -95,10 +95,10 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="tab domain-card-tab {{this.tabs.primary.loadout.cssClass}}" data-group="primary" data-tab="loadout">
|
||||
{{> "systems/daggerheart/templates/sheets/pc/sections/loadout.hbs" abilities=this.abilities actor=this.document config=this.config }}
|
||||
{{> "systems/daggerheart/templates/sheets/character/sections/loadout.hbs" abilities=this.abilities actor=this.document config=this.config }}
|
||||
</div>
|
||||
<div class="tab {{this.tabs.primary.inventory.cssClass}}" data-group="primary" data-tab="inventory">
|
||||
{{> "systems/daggerheart/templates/sheets/pc/sections/inventory.hbs" inventory=this.inventory }}
|
||||
{{> "systems/daggerheart/templates/sheets/character/sections/inventory.hbs" inventory=this.inventory }}
|
||||
</div>
|
||||
<div class="tab {{this.tabs.primary.story.cssClass}}" data-group="primary" data-tab="story">
|
||||
<div class="flexcol" style="height: 100%;">
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
<div class="card-row">
|
||||
<div class="heritage-card {{#if (not abilities.foundation.ancestry)}}outlined{{/if}}">
|
||||
{{#if abilities.foundation.ancestry}}
|
||||
{{> "systems/daggerheart/templates/sheets/pc/parts/heritageCard.hbs" card=abilities.foundation.ancestry }}
|
||||
{{> "systems/daggerheart/templates/sheets/character/parts/heritageCard.hbs" card=abilities.foundation.ancestry }}
|
||||
{{else}}
|
||||
<div class="empty-ability-container">
|
||||
<div class="empty-ability-inner-container">
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
</div>
|
||||
<div class="heritage-card {{#if (not abilities.foundation.community)}}outlined{{/if}}">
|
||||
{{#if abilities.foundation.community}}
|
||||
{{> "systems/daggerheart/templates/sheets/pc/parts/heritageCard.hbs" card=abilities.foundation.community }}
|
||||
{{> "systems/daggerheart/templates/sheets/character/parts/heritageCard.hbs" card=abilities.foundation.community }}
|
||||
{{else}}
|
||||
<div class="empty-ability-container">
|
||||
<div class="empty-ability-inner-container">
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
<div class="card-row">
|
||||
<div class="heritage-card {{#if (not abilities.foundation.advancement.foundation)}}outlined{{/if}}">
|
||||
{{#if abilities.foundation.advancement.foundation}}
|
||||
{{> "systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs" card=abilities.foundation.advancement.foundation }}
|
||||
{{> "systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs" card=abilities.foundation.advancement.foundation }}
|
||||
{{else}}
|
||||
<div class="empty-ability-container">
|
||||
<div class="empty-ability-inner-container">
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
</div>
|
||||
<div class="heritage-card {{#if (not abilities.foundation.advancement.first)}}outlined{{/if}}">
|
||||
{{#if abilities.foundation.advancement.first}}
|
||||
{{> "systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs" card=abilities.foundation.advancement.first }}
|
||||
{{> "systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs" card=abilities.foundation.advancement.first }}
|
||||
{{else}}
|
||||
<div class="empty-ability-container">
|
||||
<div class="empty-ability-inner-container">
|
||||
|
|
@ -68,7 +68,7 @@
|
|||
</div>
|
||||
<div class="heritage-card {{#if (not abilities.foundation.advancement.second)}}outlined{{/if}}">
|
||||
{{#if abilities.foundation.advancement.second}}
|
||||
{{> "systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs" card=abilities.foundation.advancement.second}}
|
||||
{{> "systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs" card=abilities.foundation.advancement.second}}
|
||||
{{else}}
|
||||
<div class="empty-ability-container">
|
||||
<div class="empty-ability-inner-container">
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
<a
|
||||
class='effect-control'
|
||||
data-action='editFeature'
|
||||
data-feature={{feature.uuid}}
|
||||
data-feature='{{feature.uuid}}'
|
||||
data-tooltip='{{localize "DAGGERHEART.Tooltip.openItemWorld"}}'
|
||||
>
|
||||
<i class="fa-solid fa-globe"></i>
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
<a
|
||||
class='effect-control'
|
||||
data-action='deleteFeature'
|
||||
data-feature={{feature.uuid}}
|
||||
data-feature='{{feature.uuid}}'
|
||||
data-tooltip='{{localize "DAGGERHEART.Tooltip.delete"}}'
|
||||
>
|
||||
<i class='fas fa-trash'></i>
|
||||
|
|
|
|||
|
|
@ -5,22 +5,16 @@
|
|||
>
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Subclass.Tabs.Foundation"}}</legend>
|
||||
{{#each source.system.foundationFeature.abilities as |feature key|}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=feature}}
|
||||
{{/each}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=source.system.foundationFeature}}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Subclass.Tabs.Specialization"}}</legend>
|
||||
{{#each source.system.specializationFeature.abilities as |feature key|}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=feature}}
|
||||
{{/each}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=source.system.specializationFeature}}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Subclass.Tabs.Mastery"}}</legend>
|
||||
{{#each source.system.masteryFeature.abilities as |feature key|}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=feature}}
|
||||
{{/each}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=source.system.masteryFeature}}
|
||||
</fieldset>
|
||||
</section>
|
||||
|
|
@ -12,10 +12,10 @@
|
|||
</div>
|
||||
<div class="attribute-image">
|
||||
{{#if ../editAttributes}}
|
||||
<select class="attribute-value{{#if (lt attribute.base 0)}} negative{{/if}}{{#if (and (not attribute.base) (not ../abilityScoresFinished))}} unselected{{/if}}" data-attribute="{{key}}">
|
||||
{{#if (not (eq attribute.base 0))}}<option value="">{{attribute.base}}</option>{{/if}}
|
||||
<select class="attribute-value{{#if (lt attribute.value 0)}} negative{{/if}}{{#if (and (not attribute.value) (not ../abilityScoresFinished))}} unselected{{/if}}" data-attribute="{{key}}">
|
||||
{{#if (not (eq attribute.value 0))}}<option value="">{{attribute.value}}</option>{{/if}}
|
||||
{{#each ../abilityScoreArray as |option|}}
|
||||
<option value="{{option.value}}"{{#if (eq option.value attribute.base)}} selected="selected"{{/if}}>{{option.name}}</option>
|
||||
<option value="{{option.value}}">{{option.name}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
{{else}}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<div class="defense-row">
|
||||
<div class="defense-section">
|
||||
<div class="defense-container">
|
||||
<div class="defense-value">{{document.system.evasion.value}}</div>
|
||||
<div class="defense-value">{{document.system.evasion}}</div>
|
||||
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
||||
<div class="defense-banner">{{localize "DAGGERHEART.Sheets.PC.Defense.Evasion"}}</div>
|
||||
</div>
|
||||
|
|
@ -16,16 +16,18 @@
|
|||
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
||||
<div class="defense-banner">{{localize "DAGGERHEART.Sheets.PC.Defense.Armor"}}</div>
|
||||
</div>
|
||||
<div class="armor-marks">
|
||||
{{#times (subtract 12 document.system.armorMarks.max)}}
|
||||
<input class="mark disabled-mark" type="checkbox" disabled />
|
||||
{{/times}}
|
||||
{{#times document.system.armorMarks.max}}
|
||||
{{#with (add this 1)}}
|
||||
<input class="mark" type="checkbox" data-action="toggleMarks" data-value="{{this}}" {{ checked (gte ../../document.system.armor.system.marks.value this) }} />
|
||||
{{/with}}
|
||||
{{/times}}
|
||||
</div>
|
||||
{{#if document.system.armor.system.marks}}
|
||||
<div class="armor-marks">
|
||||
{{#times (subtract 12 document.system.armor.system.marks.max)}}
|
||||
<input class="mark disabled-mark" type="checkbox" disabled />
|
||||
{{/times}}
|
||||
{{#times document.system.armor.system.marks.max}}
|
||||
{{#with (add this 1)}}
|
||||
<input class="mark" type="checkbox" data-action="toggleMarks" data-value="{{this}}" {{ checked (gte ../../document.system.armor.system.marks.value this) }} />
|
||||
{{/with}}
|
||||
{{/times}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
<fieldset class="left-main-container experience-container">
|
||||
<legend class="legend">{{localize "DAGGERHEART.Sheets.PC.Experience.Title"}}</legend>
|
||||
{{#each document.system.experiences as |experience index|}}
|
||||
{{#each document.system.experiences as |experience id|}}
|
||||
<div class="experience-row">
|
||||
<input class="experience-description" data-index={{index}} value="{{experience.description}}" type="text" />
|
||||
<div class="experience-value">{{experience.value}}</div>
|
||||
<input name="{{concat "system.experiences." id ".description"}}" data-experience={{id}} value="{{experience.description}}" type="text" />
|
||||
<div name="{{concat "system.experiences." id ".value"}}" class="experience-value">{{experience.value}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{#times (subtract 5 document.system.experiences.length)}}
|
||||
{{#times (subtract 5 (length document.system.experiences))}}
|
||||
<div class="experience-row">
|
||||
<input type="text" class="disabled-experience" disabled />
|
||||
<div class="experience-value empty"></div>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<fieldset class="left-main-container" style="flex: 1;">
|
||||
<legend class="legend">
|
||||
{{#if this.document.system.class}}
|
||||
{{#if this.document.system.multiclass}}
|
||||
{{#if this.document.system.class.value}}
|
||||
{{#if this.document.system.multiclass.value}}
|
||||
<span class="class-feature-selectable {{#if this.multiclass}}inactive{{/if}}" data-action="selectFeatureSet" data-multiclass="false">{{this.document.system.class.name}} {{localize "DAGGERHEART.General.Features"}}</span>
|
||||
/
|
||||
<span class="class-feature-selectable {{#if (not this.multiclass)}}inactive{{/if}}" data-action="selectFeatureSet" data-multiclass="true">{{this.document.system.multiclass.name}} {{localize "DAGGERHEART.General.Features"}}</span>
|
||||
{{else}}
|
||||
<span>{{this.document.system.class.name}} {{localize "DAGGERHEART.General.Features"}}</span>
|
||||
<span>{{this.document.system.class.value.name}} {{localize "DAGGERHEART.General.Features"}}</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<span>{{localize "DAGGERHEART.Sheets.PC.Features.Title"}}</span>
|
||||
|
|
|
|||
|
|
@ -11,39 +11,39 @@
|
|||
<div class="proficiency-container-visual-element"></div>
|
||||
</div>
|
||||
<div class="weapons-burden">
|
||||
<i class="fa-solid fa-hand weapons-burden-icon left {{#if (or (eq weapons.burden "oneHanded") (eq weapons.burden "twoHanded"))}}active{{/if}}"></i>
|
||||
<i class="fa-solid fa-hand weapons-burden-icon right {{#if (eq weapons.burden "twoHanded")}}active{{/if}}"></i>
|
||||
<i class="fa-solid fa-hand weapons-burden-icon left {{#if (or (eq weaponBurden "oneHanded") (eq weaponBurden "twoHanded"))}}active{{/if}}"></i>
|
||||
<i class="fa-solid fa-hand weapons-burden-icon right {{#if (eq weaponBurden "twoHanded")}}active{{/if}}"></i>
|
||||
</div>
|
||||
</legend>
|
||||
|
||||
<div class="active-item-container">
|
||||
<h2 class="weapons-label-row">
|
||||
{{localize "DAGGERHEART.Sheets.PC.Weapons.PrimaryTitle"}}
|
||||
{{#if weapons.primary}}
|
||||
<img class="damage-roll" data-action="attackRoll" data-weapon="{{weapons.primary.uuid}}" src="icons/svg/d12-grey.svg" />
|
||||
{{#if primaryWeapon}}
|
||||
<img class="damage-roll" data-action="attackRoll" data-weapon="{{primaryWeapon.uuid}}" src="icons/svg/d12-grey.svg" />
|
||||
{{/if}}
|
||||
</h2>
|
||||
<div class="flexrow">
|
||||
<input value="{{weapons.primary.name}}" type="text" />
|
||||
<input value="{{localize weapons.primary.trait}}" type="text" />
|
||||
<input value="{{localize weapons.primary.range.label}}" type="text" />
|
||||
<input value="{{weapons.primary.damage.value}} {{#if weapons.primary}}({{localize weapons.primary.damage.type.abbreviation}}){{/if}}" type="text" />
|
||||
<input value="{{primaryWeapon.name}}" type="text" />
|
||||
<input value="{{localize primaryWeapon.trait}}" type="text" />
|
||||
<input value="{{localize primaryWeapon.range.label}}" type="text" />
|
||||
<input value="{{primaryWeapon.damage.value}} {{#if primaryWeapon}}({{localize primaryWeapon.damage.type.abbreviation}}){{/if}}" type="text" />
|
||||
</div>
|
||||
<input value="{{localize weapons.primary.feature.label}} {{#if weapons.primary.feature}}({{localize weapons.primary.feature.description}}){{/if}}" type="text" />
|
||||
<input value="{{localize primaryWeapon.feature.label}} {{#if primaryWeapon.feature}}({{localize primaryWeapon.feature.description}}){{/if}}" type="text" />
|
||||
</div>
|
||||
<div class="active-item-container">
|
||||
<h2 class="weapons-label-row">
|
||||
{{localize "DAGGERHEART.Sheets.PC.Weapons.SecondaryTitle"}}
|
||||
{{#if weapons.secondary}}
|
||||
<img class="damage-roll" data-action="damageRoll" data-value="{{weapons.secondary.damage.value}}" src="icons/svg/d12-grey.svg" />
|
||||
{{#if secondaryWeapon}}
|
||||
<img class="damage-roll" data-action="damageRoll" data-value="{{secondaryWeapon.damage.value}}" src="icons/svg/d12-grey.svg" />
|
||||
{{/if}}
|
||||
</h2>
|
||||
<div class="flexrow">
|
||||
<input value="{{weapons.secondary.name}}" type="text" />
|
||||
<input value="{{localize weapons.secondary.trait}}" type="text" />
|
||||
<input value="{{localize weapons.secondary.range.label}}" type="text" />
|
||||
<input value="{{weapons.secondary.damage.label}} {{#if weapons.secondary}}({{localize weapons.secondary.damage.type.abbreviation}}){{/if}}" type="text" />
|
||||
<input value="{{secondaryWeapon.name}}" type="text" />
|
||||
<input value="{{localize secondaryWeapon.trait}}" type="text" />
|
||||
<input value="{{localize secondaryWeapon.range.label}}" type="text" />
|
||||
<input value="{{secondaryWeapon.damage.label}} {{#if secondaryWeapon}}({{localize secondaryWeapon.damage.type.abbreviation}}){{/if}}" type="text" />
|
||||
</div>
|
||||
<input value="{{localize weapons.secondary.feature.name}} {{#if weapons.secondary.feature}}({{localize weapons.secondary.feature.description}}){{/if}}" style="text-overflow: ellipsis;" type="text" />
|
||||
<input value="{{localize secondaryWeapon.feature.name}} {{#if secondaryWeapon.feature}}({{localize secondaryWeapon.feature.description}}){{/if}}" style="text-overflow: ellipsis;" type="text" />
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
@ -15,22 +15,6 @@
|
|||
<button class="disadvantage flex1 {{#if this.advantage}}selected{{/if}}" data-action="setAdvantage">Advantage</button>
|
||||
<button class="disadvantage flex1 {{#if this.disadvantage}}selected{{/if}}" data-action="setDisadvantage">Disadvantage</button>
|
||||
</div>
|
||||
{{!-- <div class="form-group">
|
||||
<label>Advantage</label>
|
||||
<div class="form-fields">
|
||||
<select name="advantage" {{#if this.disadvantage}}disabled{{/if}}>
|
||||
{{selectOptions this.diceOptions selected=this.advantage valueAttr="value" labelAttr="name" localize=true blank=""}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Disadvantage</label>
|
||||
<div class="form-fields">
|
||||
<select name="disadvantage" {{#if this.advantage}}disabled{{/if}}>
|
||||
{{selectOptions this.diceOptions selected=this.disadvantage valueAttr="value" labelAttr="name" localize=true blank=""}}
|
||||
</select>
|
||||
</div>
|
||||
</div> --}}
|
||||
{{#if (not this.isNpc)}}
|
||||
<div class="form-group">
|
||||
<label>Hope</label>
|
||||
|
|
@ -49,24 +33,6 @@
|
|||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#each this.bonusDamage as |damage index|}}
|
||||
<div class="form-group">
|
||||
<label><strong>{{damage.description}}</strong></label>
|
||||
<div class="form-fields">
|
||||
<label>Enabled</label>
|
||||
<input style="align-self: baseline;" type="checkbox" name="bonusDamage.{{index}}.initiallySelected" {{checked damage.initiallySelected}} />
|
||||
{{#if (and damage.initiallySelected damage.hopeIncrease)}}
|
||||
|
||||
<label>Hope</label>
|
||||
<div class="hope-container">
|
||||
<i data-action="decreaseHopeUse" data-index="{{index}}" class="fa-solid fa-caret-left icon-button {{#if (eq damage.hopeUses 0)}}disabled{{/if}}"></i>
|
||||
<div>{{damage.hopeUses}}</div>
|
||||
<i data-action="increaseHopeUse" data-index="{{index}}" class="fa-solid fa-caret-right icon-button {{#if (eq ../hopeUsed ../hopeResource)}}disabled{{/if}}"></i>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<footer>
|
||||
<button data-action="finish">Roll</button>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue