mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 11:41:08 +01:00
Merged with main
This commit is contained in:
commit
53be047e12
58 changed files with 4027 additions and 1962 deletions
|
|
@ -3,11 +3,10 @@ import * as applications from './module/applications/_module.mjs';
|
||||||
import * as models from './module/data/_module.mjs';
|
import * as models from './module/data/_module.mjs';
|
||||||
import * as documents from './module/documents/_module.mjs';
|
import * as documents from './module/documents/_module.mjs';
|
||||||
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
|
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
|
||||||
import DhpCombatTracker from './module/ui/combatTracker.mjs';
|
import DhCombatTracker from './module/ui/combatTracker.mjs';
|
||||||
import { GMUpdateEvent, handleSocketEvent, socketEvent } from './module/helpers/socket.mjs';
|
import { GMUpdateEvent, handleSocketEvent, socketEvent } from './module/helpers/socket.mjs';
|
||||||
import { registerDHSettings } from './module/applications/settings.mjs';
|
import { registerDHSettings } from './module/applications/settings.mjs';
|
||||||
import DhpChatLog from './module/ui/chatLog.mjs';
|
import DhpChatLog from './module/ui/chatLog.mjs';
|
||||||
import DhpPlayers from './module/ui/players.mjs';
|
|
||||||
import DhpRuler from './module/ui/ruler.mjs';
|
import DhpRuler from './module/ui/ruler.mjs';
|
||||||
import DhpTokenRuler from './module/ui/tokenRuler.mjs';
|
import DhpTokenRuler from './module/ui/tokenRuler.mjs';
|
||||||
import { dualityRollEnricher } from './module/enrichers/DualityRollEnricher.mjs';
|
import { dualityRollEnricher } from './module/enrichers/DualityRollEnricher.mjs';
|
||||||
|
|
@ -65,11 +64,11 @@ Hooks.once('init', () => {
|
||||||
Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true });
|
Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true });
|
||||||
|
|
||||||
CONFIG.Combat.dataModels = {
|
CONFIG.Combat.dataModels = {
|
||||||
base: models.DhpCombat
|
base: models.DhCombat
|
||||||
};
|
};
|
||||||
|
|
||||||
CONFIG.Combatant.dataModels = {
|
CONFIG.Combatant.dataModels = {
|
||||||
base: models.DhpCombatant
|
base: models.DhCombatant
|
||||||
};
|
};
|
||||||
|
|
||||||
CONFIG.ChatMessage.dataModels = models.messages.config;
|
CONFIG.ChatMessage.dataModels = models.messages.config;
|
||||||
|
|
@ -77,7 +76,7 @@ Hooks.once('init', () => {
|
||||||
|
|
||||||
CONFIG.Canvas.rulerClass = DhpRuler;
|
CONFIG.Canvas.rulerClass = DhpRuler;
|
||||||
CONFIG.Combat.documentClass = documents.DhpCombat;
|
CONFIG.Combat.documentClass = documents.DhpCombat;
|
||||||
CONFIG.ui.combat = DhpCombatTracker;
|
CONFIG.ui.combat = DhCombatTracker;
|
||||||
CONFIG.ui.chat = DhpChatLog;
|
CONFIG.ui.chat = DhpChatLog;
|
||||||
// CONFIG.ui.players = DhpPlayers;
|
// CONFIG.ui.players = DhpPlayers;
|
||||||
CONFIG.Token.rulerClass = DhpTokenRuler;
|
CONFIG.Token.rulerClass = DhpTokenRuler;
|
||||||
|
|
@ -97,8 +96,8 @@ Hooks.once('init', () => {
|
||||||
|
|
||||||
Hooks.on('ready', () => {
|
Hooks.on('ready', () => {
|
||||||
ui.resources = new CONFIG.ui.resources();
|
ui.resources = new CONFIG.ui.resources();
|
||||||
ui.resources.render({force: true});
|
ui.resources.render({ force: true });
|
||||||
})
|
});
|
||||||
|
|
||||||
Hooks.once('dicesoniceready', () => {});
|
Hooks.once('dicesoniceready', () => {});
|
||||||
|
|
||||||
|
|
@ -201,8 +200,8 @@ Hooks.on('chatMessage', (_, message) => {
|
||||||
|
|
||||||
const title = attributeValue
|
const title = attributeValue
|
||||||
? game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
|
? game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
|
||||||
ability: game.i18n.localize(abilities[attributeValue].label)
|
ability: game.i18n.localize(abilities[attributeValue].label)
|
||||||
})
|
})
|
||||||
: game.i18n.localize('DAGGERHEART.General.Duality');
|
: game.i18n.localize('DAGGERHEART.General.Duality');
|
||||||
|
|
||||||
const hopeAndFearRoll = `1${rollCommand.hope ?? 'd12'}+1${rollCommand.fear ?? 'd12'}`;
|
const hopeAndFearRoll = `1${rollCommand.hope ?? 'd12'}+1${rollCommand.fear ?? 'd12'}`;
|
||||||
|
|
@ -221,9 +220,9 @@ Hooks.on('chatMessage', (_, message) => {
|
||||||
roll,
|
roll,
|
||||||
attribute: attribute
|
attribute: attribute
|
||||||
? {
|
? {
|
||||||
value: attribute.data.value,
|
value: attribute.data.value,
|
||||||
label: `${game.i18n.localize(abilities[attributeValue].label)} ${attribute.data.value >= 0 ? `+` : ``}${attribute.data.value}`
|
label: `${game.i18n.localize(abilities[attributeValue].label)} ${attribute.data.value >= 0 ? `+` : ``}${attribute.data.value}`
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
title
|
title
|
||||||
});
|
});
|
||||||
|
|
@ -280,7 +279,9 @@ const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/sheets/pc/sections/loadout.hbs',
|
'systems/daggerheart/templates/sheets/pc/sections/loadout.hbs',
|
||||||
'systems/daggerheart/templates/sheets/pc/parts/heritageCard.hbs',
|
'systems/daggerheart/templates/sheets/pc/parts/heritageCard.hbs',
|
||||||
'systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs',
|
'systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs',
|
||||||
'systems/daggerheart/templates/views/parts/level.hbs',
|
'systems/daggerheart/templates/components/card-preview.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs'
|
'systems/daggerheart/templates/views/levelup/parts/selectable-card-preview.hbs',
|
||||||
|
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||||
|
'systems/daggerheart/templates/ui/combat/combatTrackerSection.hbs'
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
142
lang/en.json
142
lang/en.json
|
|
@ -61,6 +61,13 @@
|
||||||
"outline": "Outline",
|
"outline": "Outline",
|
||||||
"edge": "Edge"
|
"edge": "Edge"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"VariantRules": {
|
||||||
|
"title": "Variant Rules",
|
||||||
|
"label": "Variant Rules",
|
||||||
|
"hint": "Apply variant rules from the Daggerheart system",
|
||||||
|
"name": "Variant Rules",
|
||||||
|
"actionTokens": "Action Tokens"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Automation": {
|
"Automation": {
|
||||||
|
|
@ -101,6 +108,12 @@
|
||||||
"Hint": "Enable measuring of ranges with the ruler according to set distances."
|
"Hint": "Enable measuring of ranges with the ruler according to set distances."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"VariantRules": {
|
||||||
|
"ActionTokens": {
|
||||||
|
"Name": "Action Tokens",
|
||||||
|
"Hint": "Give each player action tokens to use in combat"
|
||||||
|
}
|
||||||
|
},
|
||||||
"DualityRollColor": {
|
"DualityRollColor": {
|
||||||
"Name": "Duality Roll Colour Scheme",
|
"Name": "Duality Roll Colour Scheme",
|
||||||
"Hint": "The display type for Duality Rolls",
|
"Hint": "The display type for Duality Rolls",
|
||||||
|
|
@ -150,6 +163,20 @@
|
||||||
"Or": "Or",
|
"Or": "Or",
|
||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
"Features": "Features",
|
"Features": "Features",
|
||||||
|
"proficiency": "Proficiency",
|
||||||
|
"unarmored": "Unarmored",
|
||||||
|
"Experience": {
|
||||||
|
"Single": "Experience",
|
||||||
|
"plural": "Experiences"
|
||||||
|
},
|
||||||
|
"Adversary": {
|
||||||
|
"Singular": "Adversary",
|
||||||
|
"Plural": "Adversaries"
|
||||||
|
},
|
||||||
|
"Character": {
|
||||||
|
"Singular": "Character",
|
||||||
|
"Plural": "Characters"
|
||||||
|
},
|
||||||
"RefreshType": {
|
"RefreshType": {
|
||||||
"Session": "Session",
|
"Session": "Session",
|
||||||
"Shortrest": "Short Rest",
|
"Shortrest": "Short Rest",
|
||||||
|
|
@ -329,40 +356,50 @@
|
||||||
"grimoire": "Grimoire"
|
"grimoire": "Grimoire"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Combat": {
|
||||||
|
"giveSpotlight": "Give The Spotlight",
|
||||||
|
"requestSpotlight": "Request The Spotlight",
|
||||||
|
"requestingSpotlight": "Requesting The Spotlight",
|
||||||
|
"combatStarted": "Active"
|
||||||
|
},
|
||||||
"LevelUp": {
|
"LevelUp": {
|
||||||
"Tier1": {
|
"Options": {
|
||||||
"Label": "Level 2-4",
|
"trait": "Gain a +1 bonus to two unmarked character traits and mark them.",
|
||||||
"InfoLabel": "At Level 2, take an additional Experience.",
|
"hitPoint": "Permanently gain one Hit Point slot.",
|
||||||
"Pretext": "When you level up, record it on your character sheet, then choose two available options from the list below and mark them.",
|
"stress": "Permanently gain one Stress slot.",
|
||||||
"Posttext": "Then increase your Severe Damage Threshold by +2 and choose a new Domain Deck card at your Level or lower."
|
"experience": "Permanently gain a +1 bonus to two experiences.",
|
||||||
|
"domainCard": "Choose an additional domain card of your level or lower from a domain you have access to (up to level {maxLevel})",
|
||||||
|
"evasion": "Permanently gain a +1 bonus to your Evasion.",
|
||||||
|
"subclass": "Take an upgraded subclass card. Then cross out the multiclass option for this tier.",
|
||||||
|
"proficiency": "Increase your Proficiency by +1.",
|
||||||
|
"multiclass": "Multiclass: Choose an additional class for your character, then cross out an unused “Take an upgraded subclass card” and the other multiclass option on this sheet."
|
||||||
},
|
},
|
||||||
"Tier2": {
|
"Tier2": {
|
||||||
"Label": "Level 5-7",
|
"Label": "Levels 2-4",
|
||||||
"InfoLabel": "At Level 5, take an additional Experience and clear all marks on Character Traits.",
|
"InfoLabel": "At Level 2, gain an additional Experience at +2 and gain a +1 bonus to your Proficiency.",
|
||||||
"Pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
|
"Pretext": "Choose two options from the list below",
|
||||||
"Posttext": "Then, increase your Damage Thresholds: Major by +1 and Severe by +3. Then choose a new Domain Deck card at your Level or lower. If your loadout is full, you may choose a card to swap."
|
"Posttext": "Take an additional domain card of your level or lower from a domain you have access to."
|
||||||
},
|
},
|
||||||
"Tier3": {
|
"Tier3": {
|
||||||
"Label": "Level 8-10",
|
"Label": "Levels 5-7",
|
||||||
|
"InfoLabel": "At Level 5, take an additional Experience and clear all marks on Character Traits.",
|
||||||
|
"Pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
|
||||||
|
"Posttext": "Take an additional domain card of your level or lower from a domain you have access to."
|
||||||
|
},
|
||||||
|
"Tier4": {
|
||||||
|
"Label": "Levels 8-10",
|
||||||
"InfoLabel": "At Level 8, take an additional Experience and clear all marks on Character Traits.",
|
"InfoLabel": "At Level 8, take an additional Experience and clear all marks on Character Traits.",
|
||||||
"Pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
|
"Pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
|
||||||
"Posttext": "Then, increase your Damage Thresholds: Minor by +1, Major by +2, and Severe by +4. Then choose a new Domain Deck card at your Level or lower. If your loadout is full, you may choose a card to swap."
|
"Posttext": "Take an additional domain card of your level or lower from a domain you have access to."
|
||||||
},
|
},
|
||||||
"ChoiceDescriptions": {
|
"ChoiceDescriptions": {
|
||||||
"Attributes": "Increase two unmarked Character Traits by +1 and mark them.",
|
"Attributes": "Gain a +1 bonus to two unmarked character traits and mark them.",
|
||||||
"HitPointSlots": "Permanently add one Hit Point Slot.",
|
"HitPointSlots": "Permanently gain one Hit Point slot.",
|
||||||
"StressSlots": "Permanently add one Stress Slot.",
|
"StressSlots": "Permanently gain one Stress slot.",
|
||||||
"Experiences": "Increase two Experiences by +1.",
|
"Experiences": "Permanently gain a +1 bonus to two experiences.",
|
||||||
"Proficiency": "Increase your Proficiency by +1",
|
"DomainCard": "Choose an additional domain card of your level or lower from a domain you have access to (up to level {maxLevel})",
|
||||||
"ArmorOrEvasionSlot": "Permanently add one Armor Slot or take +1 to your Evasion.",
|
"Evasion": "Permanently gain a +1 bonus to your Evasion.",
|
||||||
"MajorDamageThreshold2": "Increase your Major Damage Threshold by +2.",
|
"Proficiency": "Increase your Proficiency by +1.",
|
||||||
"SevereDamageThreshold2": "Increase your Severe Damage Threshold by +2.",
|
|
||||||
"MinorDamageThreshold2": "Increase your Minor Damage Threshold by +2.",
|
|
||||||
"SevereDamageThreshold3": "Increase your Severe Damage Threshold by +3.",
|
|
||||||
"Major2OrSevere4DamageThreshold": "Increase your Major Damage Threshold by +2 or Severe Damage Threshold by +4",
|
|
||||||
"Minor1OrMajor1DamageThreshold": "Increase your Minor or Major Damage Threshold by +1.",
|
|
||||||
"SevereDamageThreshold4": "Increase your Severe Damage Threshold by +4.",
|
|
||||||
"MajorDamageThreshold1": "Increase your Major Damage Threshold by +1.",
|
|
||||||
"Subclass": "Take an upgraded subclass card. Then cross out the multiclass option for this tier.",
|
"Subclass": "Take an upgraded subclass card. Then cross out the multiclass option for this tier.",
|
||||||
"Multiclass": "Multiclass: Choose an additional class for your character, then cross out an unused “Take an upgraded subclass card” and the other multiclass option on this sheet."
|
"Multiclass": "Multiclass: Choose an additional class for your character, then cross out an unused “Take an upgraded subclass card” and the other multiclass option on this sheet."
|
||||||
}
|
}
|
||||||
|
|
@ -748,8 +785,54 @@
|
||||||
"TakeDowntime": "Take Downtime"
|
"TakeDowntime": "Take Downtime"
|
||||||
},
|
},
|
||||||
"LevelUp": {
|
"LevelUp": {
|
||||||
"AdvanceLevel": "Continue To Level {level}",
|
"Title": "{actor} Level Up",
|
||||||
"TakeLevelUp": "Finish Level Up"
|
"Tabs": {
|
||||||
|
"advancement": "Level Advancement",
|
||||||
|
"selections": "Advancement Choices",
|
||||||
|
"summary": "Summary"
|
||||||
|
},
|
||||||
|
"navigateLevel": "To Level {level}",
|
||||||
|
"navigateToLevelup": "Return To Levelup",
|
||||||
|
"navigateToSummary": "To Summary",
|
||||||
|
"TakeLevelUp": "Finish Level Up",
|
||||||
|
"Delevel": {
|
||||||
|
"title": "Go back to previous level",
|
||||||
|
"content": "Returning to the previous level selection will remove all selections made for this level. Do you want to proceed?"
|
||||||
|
},
|
||||||
|
"Selections": {
|
||||||
|
"emptyDomainCardHint": "Domain Card Level {level} or below"
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"levelAchievements": "Level Achievements",
|
||||||
|
"levelAdvancements": "Level Advancements",
|
||||||
|
"proficiencyIncrease": "Proficiency Increased: {proficiency}",
|
||||||
|
"hpIncrease": "Hit Points Increased: {hitPoints}",
|
||||||
|
"stressIncrease": "Stress Increased: {stress}",
|
||||||
|
"evasionIncrease": "Evasion Increased: {evasion}",
|
||||||
|
"damageThresholdMajorIncrease": "Major: {threshold}",
|
||||||
|
"damageThresholdSevereIncrease": "Severe: {threshold}",
|
||||||
|
"newExperiences": "New Experiences",
|
||||||
|
"experiencePlaceholder": "A new experience..",
|
||||||
|
"domainCards": "Domain Cards",
|
||||||
|
"subclass": "Subclass",
|
||||||
|
"multiclass": "Multiclass",
|
||||||
|
"traits": "Increased Traits",
|
||||||
|
"experienceIncreases": "Experience Increases",
|
||||||
|
"damageThresholds": "Damage Thresholds"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"info": {
|
||||||
|
"insufficentAdvancements": "You don't have enough advancements left.",
|
||||||
|
"insufficientTierAdvancements": "You have no available advancements for this tier."
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"domainCardWrongDomain": "You don't have access to that Domain",
|
||||||
|
"domainCardToHighLevel": "The Domain Card is too high level to be selected",
|
||||||
|
"domainCardDuplicate": "You already have that domain card!",
|
||||||
|
"noSelectionsLeft": "Nothing more to select!",
|
||||||
|
"alreadySelectedClass": "You already have that class!"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"DeathMove": {
|
"DeathMove": {
|
||||||
"Title": "{actor} - Death Move",
|
"Title": "{actor} - Death Move",
|
||||||
|
|
@ -910,7 +993,10 @@
|
||||||
},
|
},
|
||||||
"NewItem": "New Item",
|
"NewItem": "New Item",
|
||||||
"NewScar": "New Scar",
|
"NewScar": "New Scar",
|
||||||
"DeleteConfirmation": "Are you sure you want to delete the item - {item}?"
|
"DeleteConfirmation": "Are you sure you want to delete the item - {item}?",
|
||||||
|
"Errors": {
|
||||||
|
"missingClassOrSubclass": "The character doesn't have a class and subclass"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Adversary": {
|
"Adversary": {
|
||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -10,100 +9,101 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class Resources extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class Resources extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
constructor(options={}) {
|
constructor(options = {}) {
|
||||||
super(options);
|
super(options);
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
static DEFAULT_OPTIONS = {
|
|
||||||
id: "resources",
|
|
||||||
classes: [],
|
|
||||||
tag: "div",
|
|
||||||
window: {
|
|
||||||
frame: true,
|
|
||||||
title: "Fear",
|
|
||||||
positioned: true,
|
|
||||||
resizable: true
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
setFear: Resources.setFear,
|
|
||||||
increaseFear: Resources.increaseFear
|
|
||||||
},
|
|
||||||
position: {
|
|
||||||
width: 222,
|
|
||||||
height: 222,
|
|
||||||
// top: "200px",
|
|
||||||
// left: "120px"
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/** @override */
|
/** @inheritDoc */
|
||||||
static PARTS = {
|
static DEFAULT_OPTIONS = {
|
||||||
resources: {
|
id: 'resources',
|
||||||
root: true,
|
classes: [],
|
||||||
template: "systems/daggerheart/templates/views/resources.hbs"
|
tag: 'div',
|
||||||
// template: "templates/ui/players.hbs"
|
window: {
|
||||||
|
frame: true,
|
||||||
|
title: 'Fear',
|
||||||
|
positioned: true,
|
||||||
|
resizable: true,
|
||||||
|
minimizable: false
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setFear: Resources.setFear,
|
||||||
|
increaseFear: Resources.increaseFear
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
width: 222,
|
||||||
|
height: 222
|
||||||
|
// top: "200px",
|
||||||
|
// left: "120px"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
resources: {
|
||||||
|
root: true,
|
||||||
|
template: 'systems/daggerheart/templates/views/resources.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
get currentFear() {
|
||||||
|
return game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
get currentFear() {
|
get maxFear() {
|
||||||
return game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
return game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.MaxFear);
|
||||||
}
|
}
|
||||||
|
|
||||||
get maxFear() {
|
/* -------------------------------------------- */
|
||||||
return game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.MaxFear);
|
/* Rendering */
|
||||||
}
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/** @override */
|
||||||
/* Rendering */
|
async _prepareContext(_options) {
|
||||||
/* -------------------------------------------- */
|
const display = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear),
|
||||||
|
current = this.currentFear,
|
||||||
|
max = this.maxFear,
|
||||||
|
percent = (current / max) * 100,
|
||||||
|
isGM = game.user.isGM;
|
||||||
|
// Return the data for rendering
|
||||||
|
return { display, current, max, percent, isGM };
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _prepareContext(_options) {
|
async _preFirstRender(context, options) {
|
||||||
const display = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear),
|
options.position = game.user.getFlag(SYSTEM.id, 'app.resources.position') ?? Resources.DEFAULT_OPTIONS.position;
|
||||||
current = this.currentFear,
|
}
|
||||||
max = this.maxFear,
|
|
||||||
percent = (current / max) * 100,
|
|
||||||
isGM = game.user.isGM;
|
|
||||||
// Return the data for rendering
|
|
||||||
return {display, current, max, percent, isGM};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _preFirstRender(context, options) {
|
async _preRender(context, options) {
|
||||||
options.position = game.user.getFlag(SYSTEM.id, 'app.resources.position') ?? Resources.DEFAULT_OPTIONS.position;
|
if (this.currentFear > this.maxFear)
|
||||||
}
|
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear, this.maxFear);
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
_onPosition(position) {
|
||||||
async _preRender(context, options) {
|
game.user.setFlag(SYSTEM.id, 'app.resources.position', position);
|
||||||
if(this.currentFear > this.maxFear) await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear, this.maxFear);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_onPosition(position) {
|
async close(options = {}) {
|
||||||
game.user.setFlag(SYSTEM.id, 'app.resources.position', position);
|
if (!options.allowed) return;
|
||||||
}
|
else super.close(options);
|
||||||
|
}
|
||||||
|
|
||||||
async close(options={}) {
|
static async setFear(event, target) {
|
||||||
if(!options.allowed) return;
|
if (!game.user.isGM) return;
|
||||||
else super.close(options);
|
const fearCount = Number(target.dataset.index ?? 0);
|
||||||
}
|
await this.updateFear(this.currentFear === fearCount + 1 ? fearCount : fearCount + 1);
|
||||||
|
}
|
||||||
|
|
||||||
static async setFear(event, target) {
|
static async increaseFear(event, target) {
|
||||||
if(!game.user.isGM) return;
|
let value = target.dataset.increment ?? 0,
|
||||||
const fearCount = Number(target.dataset.index ?? 0);
|
operator = value.split('')[0] ?? null;
|
||||||
await this.updateFear(this.currentFear === fearCount + 1 ? fearCount : fearCount + 1);
|
value = Number(value);
|
||||||
}
|
await this.updateFear(operator ? this.currentFear + value : value);
|
||||||
|
}
|
||||||
|
|
||||||
static async increaseFear(event, target) {
|
async updateFear(value) {
|
||||||
let value = target.dataset.increment ?? 0,
|
if (!game.user.isGM) return;
|
||||||
operator = value.split('')[0] ?? null;
|
value = Math.max(0, Math.min(this.maxFear, value));
|
||||||
value = Number(value);
|
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear, value);
|
||||||
await this.updateFear(operator ? this.currentFear + value : value);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateFear(value) {
|
|
||||||
if(!game.user.isGM) return;
|
|
||||||
value = Math.max(0, Math.min(this.maxFear, value));
|
|
||||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
|
import { DualityRollColor } from '../config/settingsConfig.mjs';
|
||||||
|
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
|
||||||
import DhAppearance from '../data/settings/Appearance.mjs';
|
import DhAppearance from '../data/settings/Appearance.mjs';
|
||||||
import DHAppearanceSettings from './settings/appearanceSettings.mjs';
|
import DHAppearanceSettings from './settings/appearanceSettings.mjs';
|
||||||
|
import DhVariantRules from '../data/settings/VariantRules.mjs';
|
||||||
|
import DHVariantRuleSettings from './settings/variantRuleSettings.mjs';
|
||||||
|
|
||||||
class DhpAutomationSettings extends FormApplication {
|
class DhpAutomationSettings extends FormApplication {
|
||||||
constructor(object = {}, options = {}) {
|
constructor(object = {}, options = {}) {
|
||||||
|
|
@ -181,7 +185,8 @@ export const registerDHSettings = () => {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
onChange: () => {
|
onChange: () => {
|
||||||
if(ui.resources) ui.resources.render({force: true});
|
if (ui.resources) ui.resources.render({ force: true });
|
||||||
|
ui.combat.render({ force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -193,7 +198,7 @@ export const registerDHSettings = () => {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 12,
|
default: 12,
|
||||||
onChange: () => {
|
onChange: () => {
|
||||||
if(ui.resources) ui.resources.render({force: true});
|
if (ui.resources) ui.resources.render({ force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -204,15 +209,15 @@ export const registerDHSettings = () => {
|
||||||
config: true,
|
config: true,
|
||||||
type: String,
|
type: String,
|
||||||
choices: {
|
choices: {
|
||||||
'token': 'Tokens',
|
token: 'Tokens',
|
||||||
'bar': 'Bar',
|
bar: 'Bar',
|
||||||
'hide': 'Hide'
|
hide: 'Hide'
|
||||||
},
|
},
|
||||||
default: 'token',
|
default: 'token',
|
||||||
onChange: value => {
|
onChange: value => {
|
||||||
if(ui.resources) {
|
if (ui.resources) {
|
||||||
if(value === 'hide') ui.resources.close({allowed: true});
|
if (value === 'hide') ui.resources.close({ allowed: true });
|
||||||
else ui.resources.render({force: true});
|
else ui.resources.render({ force: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -251,6 +256,13 @@ export const registerDHSettings = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.variantRules, {
|
||||||
|
scope: 'world',
|
||||||
|
config: false,
|
||||||
|
type: DhVariantRules,
|
||||||
|
default: DhVariantRules.defaultSchema
|
||||||
|
});
|
||||||
|
|
||||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, {
|
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, {
|
||||||
scope: 'client',
|
scope: 'client',
|
||||||
config: false,
|
config: false,
|
||||||
|
|
@ -258,6 +270,23 @@ export const registerDHSettings = () => {
|
||||||
default: DhAppearance.defaultSchema
|
default: DhAppearance.defaultSchema
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.DualityRollColor, {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Name'),
|
||||||
|
hint: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Hint'),
|
||||||
|
scope: 'world',
|
||||||
|
config: true,
|
||||||
|
type: Number,
|
||||||
|
choices: Object.values(DualityRollColor),
|
||||||
|
default: DualityRollColor.colorful.value
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers, {
|
||||||
|
scope: 'world',
|
||||||
|
config: false,
|
||||||
|
type: DhLevelTiers,
|
||||||
|
default: defaultLevelTiers
|
||||||
|
});
|
||||||
|
|
||||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Automation.Name, {
|
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Automation.Name, {
|
||||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'),
|
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'),
|
||||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Label'),
|
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Label'),
|
||||||
|
|
@ -291,4 +320,13 @@ export const registerDHSettings = () => {
|
||||||
type: DHAppearanceSettings,
|
type: DHAppearanceSettings,
|
||||||
restricted: false
|
restricted: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.VariantRules.Name, {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.title'),
|
||||||
|
label: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.label'),
|
||||||
|
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.hint'),
|
||||||
|
icon: SYSTEM.SETTINGS.menu.VariantRules.Icon,
|
||||||
|
type: DHVariantRuleSettings,
|
||||||
|
restricted: false
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
59
module/applications/settings/variantRuleSettings.mjs
Normal file
59
module/applications/settings/variantRuleSettings.mjs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import DhVariantRules from '../../data/settings/VariantRules.mjs';
|
||||||
|
|
||||||
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class DHVariantRuleSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor() {
|
||||||
|
super({});
|
||||||
|
|
||||||
|
this.settings = new DhVariantRules(
|
||||||
|
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.variantRules).toObject()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.name');
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
id: 'daggerheart-appearance-settings',
|
||||||
|
classes: ['daggerheart', 'setting', 'dh-style'],
|
||||||
|
position: { width: '600', height: 'auto' },
|
||||||
|
actions: {
|
||||||
|
reset: this.reset,
|
||||||
|
save: this.save
|
||||||
|
},
|
||||||
|
form: { handler: this.updateData, submitOnChange: true }
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: 'systems/daggerheart/templates/settings/variant-rules.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.settingFields = this.settings;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateData(event, element, formData) {
|
||||||
|
const updatedSettings = foundry.utils.expandObject(formData.object);
|
||||||
|
|
||||||
|
await this.settings.updateSource(updatedSettings);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async reset() {
|
||||||
|
this.settings = new DhVariantRules();
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async save() {
|
||||||
|
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.variantRules, this.settings.toObject());
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -362,7 +362,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
name: x.actor.name,
|
name: x.actor.name,
|
||||||
img: x.actor.img,
|
img: x.actor.img,
|
||||||
difficulty: x.actor.system.difficulty,
|
difficulty: x.actor.system.difficulty,
|
||||||
evasion: x.actor.system.evasion
|
evasion: x.actor.system.evasion.value
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { capitalize } from '../../helpers/utils.mjs';
|
import { capitalize } from '../../helpers/utils.mjs';
|
||||||
import DhpDeathMove from '../deathMove.mjs';
|
import DhpDeathMove from '../deathMove.mjs';
|
||||||
import DhpDowntime from '../downtime.mjs';
|
import DhpDowntime from '../downtime.mjs';
|
||||||
import DhpLevelup from '../levelup.mjs';
|
|
||||||
import AncestrySelectionDialog from '../ancestrySelectionDialog.mjs';
|
import AncestrySelectionDialog from '../ancestrySelectionDialog.mjs';
|
||||||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||||
import { abilities } from '../../config/actorConfig.mjs';
|
import { abilities } from '../../config/actorConfig.mjs';
|
||||||
|
import DhlevelUp from '../levelup.mjs';
|
||||||
|
|
||||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||||
const { TextEditor } = foundry.applications.ux;
|
const { TextEditor } = foundry.applications.ux;
|
||||||
|
|
@ -167,13 +167,23 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
|
|
||||||
_attachPartListeners(partId, htmlElement, options) {
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
$(htmlElement).find('.attribute-value').on('change', this.attributeChange.bind(this));
|
htmlElement
|
||||||
$(htmlElement).find('.tab-selector').on('click', this.tabSwitch.bind(this));
|
.querySelectorAll('.attribute-value')
|
||||||
$(htmlElement).find('.level-title.levelup').on('click', this.openLevelUp.bind(this));
|
.forEach(element => element.addEventListener('change', this.attributeChange.bind(this)));
|
||||||
$(htmlElement).find('.feature-input').on('change', this.onFeatureInputBlur.bind(this));
|
htmlElement
|
||||||
$(htmlElement).find('.experience-description').on('change', this.experienceDescriptionChange.bind(this));
|
.querySelectorAll('.tab-selector')
|
||||||
$(htmlElement).find('.experience-value').on('change', this.experienceValueChange.bind(this));
|
.forEach(element => element.addEventListener('click', this.tabSwitch.bind(this)));
|
||||||
$(htmlElement).find('[data-item]').on('change', this.itemUpdate.bind(this));
|
htmlElement.querySelector('.level-title.levelup')?.addEventListener('click', this.openLevelUp.bind(this));
|
||||||
|
htmlElement
|
||||||
|
.querySelectorAll('.feature-input')
|
||||||
|
.forEach(element => element.addEventListener('change', this.onFeatureInputBlur.bind(this)));
|
||||||
|
htmlElement
|
||||||
|
.querySelectorAll('.experience-description')
|
||||||
|
.forEach(element => element.addEventListener('change', this.experienceDescriptionChange.bind(this)));
|
||||||
|
htmlElement
|
||||||
|
.querySelectorAll('.experience-value')
|
||||||
|
.forEach(element => element.addEventListener('change', this.experienceValueChange.bind(this)));
|
||||||
|
htmlElement.querySelector('.level-value').addEventListener('change', this.onLevelChange.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
|
|
@ -188,7 +198,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
context.storyEditor = this.storyEditor;
|
context.storyEditor = this.storyEditor;
|
||||||
context.multiclassFeatureSetSelected = this.multiclassFeatureSetSelected;
|
context.multiclassFeatureSetSelected = this.multiclassFeatureSetSelected;
|
||||||
|
|
||||||
const selectedAttributes = Object.values(this.document.system.attributes).map(x => x.data.base);
|
const selectedAttributes = Object.values(this.document.system.traits).map(x => x.base);
|
||||||
context.abilityScoreArray = JSON.parse(
|
context.abilityScoreArray = JSON.parse(
|
||||||
await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray)
|
await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray)
|
||||||
).reduce((acc, x) => {
|
).reduce((acc, x) => {
|
||||||
|
|
@ -204,7 +214,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
if (!context.abilityScoreArray.includes(0)) context.abilityScoreArray.push({ name: 0, value: 0 });
|
if (!context.abilityScoreArray.includes(0)) context.abilityScoreArray.push({ name: 0, value: 0 });
|
||||||
context.abilityScoresFinished = context.abilityScoreArray.every(x => x.value === 0);
|
context.abilityScoresFinished = context.abilityScoreArray.every(x => x.value === 0);
|
||||||
|
|
||||||
//FIXME:
|
//FIXME:
|
||||||
context.domains = this.document.system.class
|
context.domains = this.document.system.class
|
||||||
? {
|
? {
|
||||||
first: this.document.system.class.system.domains[0]
|
first: this.document.system.class.system.domains[0]
|
||||||
|
|
@ -216,9 +226,9 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
context.attributes = Object.keys(this.document.system.attributes).reduce((acc, key) => {
|
context.attributes = Object.keys(this.document.system.traits).reduce((acc, key) => {
|
||||||
acc[key] = {
|
acc[key] = {
|
||||||
...this.document.system.attributes[key],
|
...this.document.system.traits[key],
|
||||||
name: game.i18n.localize(SYSTEM.ACTOR.abilities[key].name),
|
name: game.i18n.localize(SYSTEM.ACTOR.abilities[key].name),
|
||||||
verbs: SYSTEM.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x))
|
verbs: SYSTEM.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x))
|
||||||
};
|
};
|
||||||
|
|
@ -480,7 +490,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async attributeChange(event) {
|
async attributeChange(event) {
|
||||||
const path = `system.attributes.${event.currentTarget.dataset.attribute}.data.base`;
|
const path = `system.traits.${event.currentTarget.dataset.attribute}.base`;
|
||||||
await this.document.update({ [path]: event.currentTarget.value });
|
await this.document.update({ [path]: event.currentTarget.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -532,21 +542,21 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async toggleAttributeMark(_, button) {
|
static async toggleAttributeMark(_, button) {
|
||||||
const attribute = this.document.system.attributes[button.dataset.attribute];
|
const attribute = this.document.system.traits[button.dataset.attribute];
|
||||||
const newMark = this.document.system.availableAttributeMarks
|
const newMark = this.document.system.availableAttributeMarks
|
||||||
.filter(x => x > Math.max.apply(null, this.document.system.attributes[button.dataset.attribute].levelMarks))
|
.filter(x => x > Math.max.apply(null, this.document.system.traits[button.dataset.attribute].levelMarks))
|
||||||
.sort((a, b) => (a > b ? 1 : -1))[0];
|
.sort((a, b) => (a > b ? 1 : -1))[0];
|
||||||
|
|
||||||
if (attribute.levelMark || !newMark) return;
|
if (attribute.levelMark || !newMark) return;
|
||||||
|
|
||||||
const path = `system.attributes.${button.dataset.attribute}.levelMarks`;
|
const path = `system.traits.${button.dataset.attribute}.levelMarks`;
|
||||||
await this.document.update({ [path]: [...attribute.levelMarks, newMark] });
|
await this.document.update({ [path]: [...attribute.levelMarks, newMark] });
|
||||||
}
|
}
|
||||||
|
|
||||||
static async toggleHP(_, button) {
|
static async toggleHP(_, button) {
|
||||||
const healthValue = Number.parseInt(button.dataset.value);
|
const healthValue = Number.parseInt(button.dataset.value);
|
||||||
const newValue = this.document.system.resources.health.value >= healthValue ? healthValue - 1 : healthValue;
|
const newValue = this.document.system.resources.hitPoints.value >= healthValue ? healthValue - 1 : healthValue;
|
||||||
await this.document.update({ 'system.resources.health.value': newValue });
|
await this.document.update({ 'system.resources.hitPoints.value': newValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
static async toggleStress(_, button) {
|
static async toggleStress(_, button) {
|
||||||
|
|
@ -577,7 +587,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
type: weapon.system.damage.type,
|
type: weapon.system.damage.type,
|
||||||
bonusDamage: this.document.system.bonuses.damage
|
bonusDamage: this.document.system.bonuses.damage
|
||||||
};
|
};
|
||||||
const modifier = this.document.system.attributes[weapon.system.trait].data.value;
|
const modifier = this.document.system.traits[weapon.system.trait].value;
|
||||||
|
|
||||||
const { roll, hope, fear, advantage, disadvantage, modifiers, bonusDamageString } =
|
const { roll, hope, fear, advantage, disadvantage, modifiers, bonusDamageString } =
|
||||||
await this.document.dualityRoll(
|
await this.document.dualityRoll(
|
||||||
|
|
@ -593,7 +603,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
name: x.actor.name,
|
name: x.actor.name,
|
||||||
img: x.actor.img,
|
img: x.actor.img,
|
||||||
difficulty: x.actor.system.difficulty,
|
difficulty: x.actor.system.difficulty,
|
||||||
evasion: x.actor.system.evasion
|
evasion: x.actor.system.evasion.value
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const systemData = {
|
const systemData = {
|
||||||
|
|
@ -634,7 +644,12 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
openLevelUp() {
|
openLevelUp() {
|
||||||
new DhpLevelup(this.document).render(true);
|
if (!this.document.system.class || !this.document.system.subclass) {
|
||||||
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.Sheets.PC.Errors.missingClassOrSubclass'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new DhlevelUp(this.document).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static domainCardsTab(toVault) {
|
static domainCardsTab(toVault) {
|
||||||
|
|
@ -782,7 +797,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async makeDeathMove() {
|
static async makeDeathMove() {
|
||||||
if (this.document.system.resources.health.value === this.document.system.resources.health.max) {
|
if (this.document.system.resources.hitPoints.value === this.document.system.resources.hitPoints.max) {
|
||||||
await new DhpDeathMove(this.document).render(true);
|
await new DhpDeathMove(this.document).render(true);
|
||||||
await this.minimize();
|
await this.minimize();
|
||||||
}
|
}
|
||||||
|
|
@ -867,6 +882,11 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
await item.update({ [name]: event.currentTarget.value });
|
await item.update({ [name]: event.currentTarget.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onLevelChange(event) {
|
||||||
|
await this.document.updateLevel(Number(event.currentTarget.value));
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
static async deleteItem(_, button) {
|
static async deleteItem(_, button) {
|
||||||
const item = await fromUuid($(button).closest('[data-item-id]')[0].dataset.itemId);
|
const item = await fromUuid($(button).closest('[data-item-id]')[0].dataset.itemId);
|
||||||
await item.delete();
|
await item.delete();
|
||||||
|
|
@ -1090,7 +1110,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
await itemObject.update({ 'system.active': true });
|
await itemObject.update({ 'system.active': true });
|
||||||
break;
|
break;
|
||||||
case 'inventory-weapon-section':
|
case 'inventory-weapon-section':
|
||||||
/* FIXME inventoryWeapon is no longer a field
|
/* FIXME inventoryWeapon is no longer a field
|
||||||
const existingInventoryWeapon = this.document.items.find(x => x.system.inventoryWeapon);
|
const existingInventoryWeapon = this.document.items.find(x => x.system.inventoryWeapon);
|
||||||
await existingInventoryWeapon?.update({ 'system.inventoryWeapon': false });
|
await existingInventoryWeapon?.update({ 'system.inventoryWeapon': false });
|
||||||
await itemObject.update({ 'system.inventoryWeapon': true });
|
await itemObject.update({ 'system.inventoryWeapon': true });
|
||||||
|
|
|
||||||
|
|
@ -52,31 +52,31 @@ export const abilities = {
|
||||||
export const featureProperties = {
|
export const featureProperties = {
|
||||||
agility: {
|
agility: {
|
||||||
name: 'DAGGERHEART.Abilities.agility.name',
|
name: 'DAGGERHEART.Abilities.agility.name',
|
||||||
path: actor => actor.system.attributes.agility.data.value
|
path: actor => actor.system.traits.agility.data.value
|
||||||
},
|
},
|
||||||
strength: {
|
strength: {
|
||||||
name: 'DAGGERHEART.Abilities.strength.name',
|
name: 'DAGGERHEART.Abilities.strength.name',
|
||||||
path: actor => actor.system.attributes.strength.data.value
|
path: actor => actor.system.traits.strength.data.value
|
||||||
},
|
},
|
||||||
finesse: {
|
finesse: {
|
||||||
name: 'DAGGERHEART.Abilities.finesse.name',
|
name: 'DAGGERHEART.Abilities.finesse.name',
|
||||||
path: actor => actor.system.attributes.finesse.data.value
|
path: actor => actor.system.traits.finesse.data.value
|
||||||
},
|
},
|
||||||
instinct: {
|
instinct: {
|
||||||
name: 'DAGGERHEART.Abilities.instinct.name',
|
name: 'DAGGERHEART.Abilities.instinct.name',
|
||||||
path: actor => actor.system.attributes.instinct.data.value
|
path: actor => actor.system.traits.instinct.data.value
|
||||||
},
|
},
|
||||||
presence: {
|
presence: {
|
||||||
name: 'DAGGERHEART.Abilities.presence.name',
|
name: 'DAGGERHEART.Abilities.presence.name',
|
||||||
path: actor => actor.system.attributes.presence.data.value
|
path: actor => actor.system.traits.presence.data.value
|
||||||
},
|
},
|
||||||
knowledge: {
|
knowledge: {
|
||||||
name: 'DAGGERHEART.Abilities.knowledge.name',
|
name: 'DAGGERHEART.Abilities.knowledge.name',
|
||||||
path: actor => actor.system.attributes.knowledge.data.value
|
path: actor => actor.system.traits.knowledge.data.value
|
||||||
},
|
},
|
||||||
spellcastingTrait: {
|
spellcastingTrait: {
|
||||||
name: 'DAGGERHEART.FeatureProperty.SpellcastingTrait',
|
name: 'DAGGERHEART.FeatureProperty.SpellcastingTrait',
|
||||||
path: actor => actor.system.attributes[actor.system.subclass.system.spellcastingTrait].data.value
|
path: actor => actor.system.traits[actor.system.subclass.system.spellcastingTrait].data.value
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@ export const menu = {
|
||||||
Range: {
|
Range: {
|
||||||
Name: 'GameSettingsRange',
|
Name: 'GameSettingsRange',
|
||||||
Icon: 'fa-solid fa-ruler'
|
Icon: 'fa-solid fa-ruler'
|
||||||
|
},
|
||||||
|
VariantRules: {
|
||||||
|
Name: 'GameSettingsVariantrules',
|
||||||
|
Icon: 'fa-solid fa-scale-balanced'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -27,5 +31,19 @@ export const gameSettings = {
|
||||||
AbilityArray: 'AbilityArray',
|
AbilityArray: 'AbilityArray',
|
||||||
RangeMeasurement: 'RangeMeasurement'
|
RangeMeasurement: 'RangeMeasurement'
|
||||||
},
|
},
|
||||||
appearance: 'Appearance'
|
DualityRollColor: 'DualityRollColor',
|
||||||
|
LevelTiers: 'LevelTiers',
|
||||||
|
appearance: 'Appearance',
|
||||||
|
variantRules: 'VariantRules'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DualityRollColor = {
|
||||||
|
colorful: {
|
||||||
|
value: 0,
|
||||||
|
label: 'DAGGERHEART.Settings.DualityRollColor.Options.Colorful'
|
||||||
|
},
|
||||||
|
normal: {
|
||||||
|
value: 1,
|
||||||
|
label: 'DAGGERHEART.Settings.DualityRollColor.Options.Normal'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
export { default as DhpPC } from './pc.mjs';
|
export { default as DhpPC } from './pc.mjs';
|
||||||
export { default as DhpCombat } from './combat.mjs';
|
export { default as DhClass } from './item/class.mjs';
|
||||||
export { default as DhpCombatant } from './combatant.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 DhpAdversary } from './adversary.mjs';
|
||||||
export { default as DhpEnvironment } from './environment.mjs';
|
export { default as DhpEnvironment } from './environment.mjs';
|
||||||
|
|
||||||
export * as items from "./item/_module.mjs";
|
export * as items from './item/_module.mjs';
|
||||||
export * as messages from "./chat-message/_modules.mjs";
|
export * as messages from './chat-message/_modules.mjs';
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
export default class DhpCombat extends foundry.abstract.TypeDataModel {
|
export default class DhCombat extends foundry.abstract.TypeDataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {};
|
||||||
actions: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
activeCombatant: new fields.StringField({})
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
export default class DhpCombatant extends foundry.abstract.TypeDataModel {
|
export default class DhCombatant extends foundry.abstract.TypeDataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
active: new fields.BooleanField({ initial: false })
|
spotlight: new fields.SchemaField({
|
||||||
|
requesting: new fields.BooleanField({ required: true, initial: false })
|
||||||
|
}),
|
||||||
|
actionTokens: new fields.NumberField({ required: true, integer: true, initial: 3 })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
340
module/data/levelTier.mjs
Normal file
340
module/data/levelTier.mjs
Normal file
|
|
@ -0,0 +1,340 @@
|
||||||
|
export class DhLevelTiers extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
tiers: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelTier))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get availableChoicesPerLevel() {
|
||||||
|
return Object.values(this.tiers).reduce((acc, tier) => {
|
||||||
|
for (var level = tier.levels.start; level < tier.levels.end + 1; level++) {
|
||||||
|
acc[level] = tier.availableOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DhLevelTier extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
tier: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
name: new fields.StringField({ required: true }),
|
||||||
|
levels: new fields.SchemaField({
|
||||||
|
start: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
end: new fields.NumberField({ required: true, integer: true })
|
||||||
|
}),
|
||||||
|
initialAchievements: new fields.SchemaField({
|
||||||
|
experience: new fields.SchemaField({
|
||||||
|
nr: new fields.NumberField({ required: true, initial: 1 }),
|
||||||
|
modifier: new fields.NumberField({ required: true, initial: 2 })
|
||||||
|
}),
|
||||||
|
proficiency: new fields.NumberField({ integer: true, initial: 1 })
|
||||||
|
}),
|
||||||
|
availableOptions: new fields.NumberField({ required: true, initial: 2 }),
|
||||||
|
domainCardByLevel: new fields.NumberField({ initial: 1 }),
|
||||||
|
options: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelOption))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DhLevelOption extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: new fields.StringField({ required: true }),
|
||||||
|
checkboxSelections: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||||
|
minCost: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||||
|
type: new fields.StringField({ required: true, choices: LevelOptionType }),
|
||||||
|
value: new fields.NumberField({ integer: true }),
|
||||||
|
amount: new fields.NumberField({ integer: true })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LevelOptionType = {
|
||||||
|
trait: {
|
||||||
|
id: 'trait',
|
||||||
|
label: 'Character Trait',
|
||||||
|
dataPath: ''
|
||||||
|
},
|
||||||
|
hitPoint: {
|
||||||
|
id: 'hitPoint',
|
||||||
|
label: 'Hit Points',
|
||||||
|
dataPath: 'resources.hitPoints',
|
||||||
|
dataPathData: {
|
||||||
|
property: 'max',
|
||||||
|
dependencies: ['value']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stress: {
|
||||||
|
id: 'stress',
|
||||||
|
label: 'Stress',
|
||||||
|
dataPath: 'resources.stress',
|
||||||
|
dataPathData: {
|
||||||
|
property: 'max',
|
||||||
|
dependencies: ['value']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
evasion: {
|
||||||
|
id: 'evasion',
|
||||||
|
label: 'Evasion',
|
||||||
|
dataPath: 'evasion'
|
||||||
|
},
|
||||||
|
proficiency: {
|
||||||
|
id: 'proficiency',
|
||||||
|
label: 'Proficiency'
|
||||||
|
},
|
||||||
|
experience: {
|
||||||
|
id: 'experience',
|
||||||
|
label: 'Experience'
|
||||||
|
},
|
||||||
|
domainCard: {
|
||||||
|
id: 'domainCard',
|
||||||
|
label: 'Domain Card'
|
||||||
|
},
|
||||||
|
subclass: {
|
||||||
|
id: 'subclass',
|
||||||
|
label: 'Subclass'
|
||||||
|
},
|
||||||
|
multiclass: {
|
||||||
|
id: 'multiclass',
|
||||||
|
label: 'Multiclass'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const defaultLevelTiers = {
|
||||||
|
tiers: {
|
||||||
|
2: {
|
||||||
|
tier: 2,
|
||||||
|
name: 'Tier 2',
|
||||||
|
levels: {
|
||||||
|
start: 2,
|
||||||
|
end: 4
|
||||||
|
},
|
||||||
|
initialAchievements: {
|
||||||
|
experience: {
|
||||||
|
nr: 1,
|
||||||
|
modifier: 2
|
||||||
|
},
|
||||||
|
proficiency: 1
|
||||||
|
},
|
||||||
|
availableOptions: 2,
|
||||||
|
domainCardByLevel: 1,
|
||||||
|
options: {
|
||||||
|
trait: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.trait',
|
||||||
|
checkboxSelections: 3,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.trait.id,
|
||||||
|
amount: 2
|
||||||
|
},
|
||||||
|
hitPoint: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.hitPoint',
|
||||||
|
checkboxSelections: 2,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.hitPoint.id,
|
||||||
|
value: 1,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
stress: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.stress',
|
||||||
|
checkboxSelections: 2,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.stress.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
experience: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.experience',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.experience.id,
|
||||||
|
value: 1,
|
||||||
|
amount: 2
|
||||||
|
},
|
||||||
|
domainCard: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.domainCard',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.domainCard.id,
|
||||||
|
amount: 1
|
||||||
|
},
|
||||||
|
evasion: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.evasion',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.evasion.id,
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
tier: 3,
|
||||||
|
name: 'Tier 3',
|
||||||
|
levels: {
|
||||||
|
start: 5,
|
||||||
|
end: 7
|
||||||
|
},
|
||||||
|
initialAchievements: {
|
||||||
|
experience: {
|
||||||
|
nr: 1,
|
||||||
|
modifier: 2
|
||||||
|
},
|
||||||
|
proficiency: 1
|
||||||
|
},
|
||||||
|
availableOptions: 2,
|
||||||
|
domainCardByLevel: 1,
|
||||||
|
options: {
|
||||||
|
trait: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.trait',
|
||||||
|
checkboxSelections: 3,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.trait.id,
|
||||||
|
amount: 2
|
||||||
|
},
|
||||||
|
hitPoint: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.hitPoint',
|
||||||
|
checkboxSelections: 2,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.hitPoint.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
stress: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.stress',
|
||||||
|
checkboxSelections: 2,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.stress.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
experience: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.experience',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.experience.id,
|
||||||
|
value: 1,
|
||||||
|
amount: 2
|
||||||
|
},
|
||||||
|
domainCard: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.domainCard',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.domainCard.id,
|
||||||
|
amount: 1
|
||||||
|
},
|
||||||
|
evasion: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.evasion',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.evasion.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
subclass: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.subclass',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.subclass.id
|
||||||
|
},
|
||||||
|
proficiency: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.proficiency',
|
||||||
|
checkboxSelections: 2,
|
||||||
|
minCost: 2,
|
||||||
|
type: LevelOptionType.proficiency.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
multiclass: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.multiclass',
|
||||||
|
checkboxSelections: 2,
|
||||||
|
minCost: 2,
|
||||||
|
type: LevelOptionType.multiclass.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
tier: 4,
|
||||||
|
name: 'Tier 4',
|
||||||
|
levels: {
|
||||||
|
start: 8,
|
||||||
|
end: 10
|
||||||
|
},
|
||||||
|
initialAchievements: {
|
||||||
|
experience: {
|
||||||
|
nr: 1,
|
||||||
|
modifier: 2
|
||||||
|
},
|
||||||
|
proficiency: 1
|
||||||
|
},
|
||||||
|
availableOptions: 2,
|
||||||
|
domainCardByLevel: 1,
|
||||||
|
options: {
|
||||||
|
trait: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.trait',
|
||||||
|
checkboxSelections: 3,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.trait.id,
|
||||||
|
amount: 2
|
||||||
|
},
|
||||||
|
hitPoint: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.hitPoint',
|
||||||
|
checkboxSelections: 2,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.hitPoint.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
stress: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.stress',
|
||||||
|
checkboxSelections: 2,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.stress.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
experience: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.experience',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.experience.id,
|
||||||
|
value: 1,
|
||||||
|
amount: 2
|
||||||
|
},
|
||||||
|
domainCard: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.domainCard',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.domainCard.id,
|
||||||
|
amount: 1
|
||||||
|
},
|
||||||
|
evasion: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.evasion',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.evasion.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
subclass: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.subclass',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.subclass.id
|
||||||
|
},
|
||||||
|
proficiency: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.proficiency',
|
||||||
|
checkboxSelections: 2,
|
||||||
|
minCost: 2,
|
||||||
|
type: LevelOptionType.proficiency.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
multiclass: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.multiclass',
|
||||||
|
checkboxSelections: 2,
|
||||||
|
minCost: 2,
|
||||||
|
type: LevelOptionType.multiclass.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
311
module/data/levelup.mjs
Normal file
311
module/data/levelup.mjs
Normal file
|
|
@ -0,0 +1,311 @@
|
||||||
|
import { chunkify } from '../helpers/utils.mjs';
|
||||||
|
import { LevelOptionType } from './levelTier.mjs';
|
||||||
|
|
||||||
|
export class DhLevelup extends foundry.abstract.DataModel {
|
||||||
|
static initializeData(levelTierData, pcLevelData) {
|
||||||
|
const startLevel = pcLevelData.level.current + 1;
|
||||||
|
const currentLevel = pcLevelData.level.current + 1;
|
||||||
|
const endLevel = pcLevelData.level.changed;
|
||||||
|
|
||||||
|
const tiers = {};
|
||||||
|
const levels = {};
|
||||||
|
const tierKeys = Object.keys(levelTierData.tiers);
|
||||||
|
tierKeys.forEach(key => {
|
||||||
|
const tier = levelTierData.tiers[key];
|
||||||
|
const belongingLevels = [];
|
||||||
|
for (var i = tier.levels.start; i <= tier.levels.end; i++) {
|
||||||
|
if (i <= endLevel) {
|
||||||
|
const initialAchievements = i === tier.levels.start ? tier.initialAchievements : {};
|
||||||
|
const experiences = initialAchievements.experience
|
||||||
|
? [...Array(initialAchievements.experience.nr).keys()].reduce((acc, _) => {
|
||||||
|
acc[foundry.utils.randomID()] = {
|
||||||
|
name: '',
|
||||||
|
modifier: initialAchievements.experience.modifier
|
||||||
|
};
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
: {};
|
||||||
|
const domainCards = [...Array(tier.domainCardByLevel).keys()].reduce((acc, _) => {
|
||||||
|
const id = foundry.utils.randomID();
|
||||||
|
acc[id] = { uuid: null, itemUuid: null, level: i };
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
levels[i] = DhLevelupLevel.initializeData(pcLevelData.levelups[i], tier.availableOptions, {
|
||||||
|
...initialAchievements,
|
||||||
|
experiences,
|
||||||
|
domainCards
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
belongingLevels.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
tiers[key] = {
|
||||||
|
name: tier.name,
|
||||||
|
belongingLevels: belongingLevels,
|
||||||
|
options: Object.keys(tier.options).reduce((acc, key) => {
|
||||||
|
acc[key] = tier.options[key].toObject();
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
tiers,
|
||||||
|
levels,
|
||||||
|
startLevel,
|
||||||
|
currentLevel,
|
||||||
|
endLevel
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
tiers: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
name: new fields.StringField({ required: true }),
|
||||||
|
belongingLevels: new fields.ArrayField(new fields.NumberField({ required: true, integer: true })),
|
||||||
|
options: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ required: true }),
|
||||||
|
checkboxSelections: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
minCost: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
type: new fields.StringField({ required: true, choices: LevelOptionType }),
|
||||||
|
value: new fields.NumberField({ integer: true }),
|
||||||
|
amount: new fields.NumberField({ integer: true })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
),
|
||||||
|
levels: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupLevel)),
|
||||||
|
startLevel: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
currentLevel: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
endLevel: new fields.NumberField({ required: true, integer: true })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#levelFinished(levelKey) {
|
||||||
|
const allSelectionsMade = this.levels[levelKey].nrSelections.available === 0;
|
||||||
|
const allChoicesMade = Object.keys(this.levels[levelKey].choices).every(choiceKey => {
|
||||||
|
const choice = this.levels[levelKey].choices[choiceKey];
|
||||||
|
return Object.values(choice).every(checkbox => {
|
||||||
|
switch (choiceKey) {
|
||||||
|
case 'trait':
|
||||||
|
case 'experience':
|
||||||
|
case 'domainCard':
|
||||||
|
case 'subclass':
|
||||||
|
return checkbox.amount ? checkbox.data.length === checkbox.amount : checkbox.data.length === 1;
|
||||||
|
case 'multiclass':
|
||||||
|
const classSelected = checkbox.data.length === 1;
|
||||||
|
const domainSelected = checkbox.secondaryData;
|
||||||
|
return classSelected && domainSelected;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const experiencesSelected = !this.levels[levelKey].achievements.experiences
|
||||||
|
? true
|
||||||
|
: Object.values(this.levels[levelKey].achievements.experiences).every(exp => exp.name);
|
||||||
|
const domainCardsSelected = Object.values(this.levels[levelKey].achievements.domainCards)
|
||||||
|
.filter(x => x.level <= this.endLevel)
|
||||||
|
.every(card => card.uuid);
|
||||||
|
const allAchievementsSelected = experiencesSelected && domainCardsSelected;
|
||||||
|
|
||||||
|
return allSelectionsMade && allChoicesMade && allAchievementsSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentLevelFinished() {
|
||||||
|
return this.#levelFinished(this.currentLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
get allLevelsFinished() {
|
||||||
|
return Object.keys(this.levels)
|
||||||
|
.filter(level => Number(level) >= this.startLevel)
|
||||||
|
.every(this.#levelFinished.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
get classUpgradeChoices() {
|
||||||
|
let subclass = null;
|
||||||
|
let multiclass = null;
|
||||||
|
Object.keys(this.levels).forEach(levelKey => {
|
||||||
|
const level = this.levels[levelKey];
|
||||||
|
Object.values(level.choices).forEach(choice => {
|
||||||
|
Object.values(choice).forEach(checkbox => {
|
||||||
|
if (checkbox.type === 'multiclass') {
|
||||||
|
multiclass = {
|
||||||
|
class: checkbox.data.length > 0 ? checkbox.data[0] : null,
|
||||||
|
domain: checkbox.secondaryData ?? null,
|
||||||
|
tier: checkbox.tier,
|
||||||
|
level: levelKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (checkbox.type === 'subclass') {
|
||||||
|
subclass = {
|
||||||
|
tier: checkbox.tier,
|
||||||
|
level: levelKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return { subclass, multiclass };
|
||||||
|
}
|
||||||
|
|
||||||
|
get tiersForRendering() {
|
||||||
|
const tierKeys = Object.keys(this.tiers);
|
||||||
|
const selections = Object.keys(this.levels).reduce(
|
||||||
|
(acc, key) => {
|
||||||
|
const level = this.levels[key];
|
||||||
|
Object.keys(level.choices).forEach(optionKey => {
|
||||||
|
const choice = level.choices[optionKey];
|
||||||
|
Object.keys(choice).forEach(checkboxNr => {
|
||||||
|
const checkbox = choice[checkboxNr];
|
||||||
|
if (!acc[checkbox.tier][optionKey]) acc[checkbox.tier][optionKey] = {};
|
||||||
|
Object.keys(choice).forEach(checkboxNr => {
|
||||||
|
acc[checkbox.tier][optionKey][checkboxNr] = { ...checkbox, level: Number(key) };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
tierKeys.reduce((acc, key) => {
|
||||||
|
acc[key] = {};
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
);
|
||||||
|
|
||||||
|
const { multiclass, subclass } = this.classUpgradeChoices;
|
||||||
|
return tierKeys.map(tierKey => {
|
||||||
|
const tier = this.tiers[tierKey];
|
||||||
|
const multiclassInTier = multiclass?.tier === Number(tierKey);
|
||||||
|
const subclassInTier = subclass?.tier === Number(tierKey);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: tier.name,
|
||||||
|
active: this.currentLevel >= Math.min(...tier.belongingLevels),
|
||||||
|
groups: Object.keys(tier.options).map(optionKey => {
|
||||||
|
const option = tier.options[optionKey];
|
||||||
|
|
||||||
|
const checkboxes = [...Array(option.checkboxSelections).keys()].flatMap(index => {
|
||||||
|
const checkboxNr = index + 1;
|
||||||
|
const checkboxData = selections[tierKey]?.[optionKey]?.[checkboxNr];
|
||||||
|
const checkbox = { ...option, checkboxNr, tier: tierKey };
|
||||||
|
|
||||||
|
if (checkboxData) {
|
||||||
|
checkbox.level = checkboxData.level;
|
||||||
|
checkbox.selected = true;
|
||||||
|
checkbox.disabled = checkbox.level !== this.currentLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionKey === 'multiclass') {
|
||||||
|
if ((multiclass && !multiclassInTier) || subclassInTier) {
|
||||||
|
checkbox.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionKey === 'subclass' && multiclassInTier) {
|
||||||
|
checkbox.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkbox;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
label: game.i18n.localize(option.label),
|
||||||
|
checkboxGroups: chunkify(checkboxes, option.minCost, chunkedBoxes => {
|
||||||
|
const anySelected = chunkedBoxes.some(x => x.selected);
|
||||||
|
const anyDisabled = chunkedBoxes.some(x => x.disabled);
|
||||||
|
return {
|
||||||
|
multi: option.minCost > 1,
|
||||||
|
checkboxes: chunkedBoxes.map(x => ({
|
||||||
|
...x,
|
||||||
|
selected: anySelected,
|
||||||
|
disabled: anyDisabled
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DhLevelupLevel extends foundry.abstract.DataModel {
|
||||||
|
static initializeData(levelData = { selections: [] }, maxSelections, achievements) {
|
||||||
|
return {
|
||||||
|
maxSelections: maxSelections,
|
||||||
|
achievements: {
|
||||||
|
experiences: levelData.achievements?.experiences ?? achievements.experiences ?? {},
|
||||||
|
domainCards: levelData.achievements?.domainCards
|
||||||
|
? levelData.achievements.domainCards.reduce((acc, card, index) => {
|
||||||
|
acc[index] = { ...card };
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
: (achievements.domainCards ?? {}),
|
||||||
|
proficiency: levelData.achievements?.proficiency ?? achievements.proficiency ?? null
|
||||||
|
},
|
||||||
|
choices: levelData.selections.reduce((acc, data) => {
|
||||||
|
if (!acc[data.optionKey]) acc[data.optionKey] = {};
|
||||||
|
acc[data.optionKey][data.checkboxNr] = { ...data };
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
maxSelections: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
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.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
uuid: new fields.StringField({ required: true, nullable: true, initial: null }),
|
||||||
|
itemUuid: new fields.StringField({ required: true }),
|
||||||
|
level: new fields.NumberField({ required: true, integer: true })
|
||||||
|
})
|
||||||
|
),
|
||||||
|
proficiency: new fields.NumberField({ integer: true })
|
||||||
|
}),
|
||||||
|
choices: new fields.TypedObjectField(
|
||||||
|
new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
tier: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
minCost: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
amount: new fields.NumberField({ integer: true }),
|
||||||
|
value: new fields.StringField(),
|
||||||
|
data: new fields.ArrayField(new fields.StringField()),
|
||||||
|
secondaryData: new fields.StringField(),
|
||||||
|
type: new fields.StringField({ required: true })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get nrSelections() {
|
||||||
|
const selections = Object.keys(this.choices).reduce((acc, choiceKey) => {
|
||||||
|
const choice = this.choices[choiceKey];
|
||||||
|
acc += Object.values(choice).reduce((acc, x) => acc + x.minCost, 0);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
selections: selections,
|
||||||
|
available: this.maxSelections - selections
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,50 +1,29 @@
|
||||||
import { getPathValue, getTier } from '../helpers/utils.mjs';
|
import { getPathValue } from '../helpers/utils.mjs';
|
||||||
|
import { LevelOptionType } from './levelTier.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
const attributeField = () =>
|
const attributeField = () =>
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
data: new fields.SchemaField({
|
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
base: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
base: new fields.NumberField({ initial: 0, integer: true }),
|
tierMarked: new fields.BooleanField({ required: true, initial: false })
|
||||||
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
actualValue: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
overrideValue: new fields.NumberField({ initial: 0, integer: true })
|
|
||||||
}),
|
|
||||||
levelMarks: new fields.ArrayField(new fields.NumberField({ nullable: true, initial: null, integer: true })),
|
|
||||||
levelMark: new fields.NumberField({ nullable: true, initial: null, integer: true })
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const levelUpTier = () => ({
|
const resourceField = max =>
|
||||||
attributes: new fields.TypedObjectField(new fields.BooleanField()),
|
new fields.SchemaField({
|
||||||
hitPointSlots: new fields.TypedObjectField(new fields.BooleanField()),
|
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
stressSlots: new fields.TypedObjectField(new fields.BooleanField()),
|
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
experiences: new fields.TypedObjectField(new fields.ArrayField(new fields.StringField({}))),
|
min: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
proficiency: new fields.TypedObjectField(new fields.BooleanField()),
|
baseMax: new fields.NumberField({ initial: max, integer: true })
|
||||||
armorOrEvasionSlot: new fields.TypedObjectField(new fields.StringField({})),
|
});
|
||||||
subclass: new fields.TypedObjectField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
multiclass: new fields.BooleanField(),
|
|
||||||
feature: new fields.StringField({})
|
|
||||||
})
|
|
||||||
),
|
|
||||||
multiclass: new fields.TypedObjectField(new fields.BooleanField())
|
|
||||||
});
|
|
||||||
|
|
||||||
export default class DhpPC extends foundry.abstract.TypeDataModel {
|
export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
resources: new fields.SchemaField({
|
resources: new fields.SchemaField({
|
||||||
health: new fields.SchemaField({
|
hitPoints: resourceField(6),
|
||||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
stress: resourceField(6),
|
||||||
min: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
max: new fields.NumberField({ initial: 6, 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: 6, integer: true })
|
|
||||||
}),
|
|
||||||
hope: new fields.SchemaField({
|
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
|
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 })
|
min: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
|
@ -61,7 +40,7 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
attributes: new fields.SchemaField({
|
traits: new fields.SchemaField({
|
||||||
agility: attributeField(),
|
agility: attributeField(),
|
||||||
strength: attributeField(),
|
strength: attributeField(),
|
||||||
finesse: attributeField(),
|
finesse: attributeField(),
|
||||||
|
|
@ -70,22 +49,22 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
knowledge: attributeField()
|
knowledge: attributeField()
|
||||||
}),
|
}),
|
||||||
proficiency: new fields.SchemaField({
|
proficiency: new fields.SchemaField({
|
||||||
value: new fields.NumberField({ initial: 1, integer: true }),
|
base: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
||||||
min: new fields.NumberField({ initial: 1, integer: true }),
|
bonus: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||||
max: new fields.NumberField({ initial: 6, integer: true })
|
}),
|
||||||
|
evasion: new fields.SchemaField({
|
||||||
|
bonus: new fields.NumberField({ initial: 0, integer: true })
|
||||||
}),
|
}),
|
||||||
evasion: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
experiences: new fields.ArrayField(
|
experiences: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
id: new fields.StringField({ required: true }),
|
id: new fields.StringField({ required: true }),
|
||||||
level: new fields.NumberField({ required: true, integer: true }),
|
|
||||||
description: new fields.StringField({}),
|
description: new fields.StringField({}),
|
||||||
value: new fields.NumberField({ integer: true, nullable: true, initial: null })
|
value: new fields.NumberField({ integer: true, nullable: true, initial: null })
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
initial: [
|
initial: [
|
||||||
{ id: foundry.utils.randomID(), level: 1, description: '', value: 2 },
|
{ id: foundry.utils.randomID(), description: '', value: 2 },
|
||||||
{ id: foundry.utils.randomID(), level: 1, description: '', value: 2 }
|
{ id: foundry.utils.randomID(), description: '', value: 2 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
@ -100,30 +79,6 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
maxLoadout: new fields.NumberField({ initial: 2, integer: true }),
|
maxLoadout: new fields.NumberField({ initial: 2, integer: true }),
|
||||||
maxCards: new fields.NumberField({ initial: 2, integer: true })
|
maxCards: new fields.NumberField({ initial: 2, integer: true })
|
||||||
}),
|
}),
|
||||||
levelData: new fields.SchemaField({
|
|
||||||
currentLevel: new fields.NumberField({ initial: 1, integer: true }),
|
|
||||||
changedLevel: new fields.NumberField({ initial: 1, integer: true }),
|
|
||||||
levelups: new fields.TypedObjectField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
level: new fields.NumberField({ required: true, integer: true }),
|
|
||||||
tier1: new fields.SchemaField({
|
|
||||||
...levelUpTier()
|
|
||||||
}),
|
|
||||||
tier2: new fields.SchemaField(
|
|
||||||
{
|
|
||||||
...levelUpTier()
|
|
||||||
},
|
|
||||||
{ nullable: true, initial: null }
|
|
||||||
),
|
|
||||||
tier3: new fields.SchemaField(
|
|
||||||
{
|
|
||||||
...levelUpTier()
|
|
||||||
},
|
|
||||||
{ nullable: true, initial: null }
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
story: new fields.SchemaField({
|
story: new fields.SchemaField({
|
||||||
background: new fields.HTMLField(),
|
background: new fields.HTMLField(),
|
||||||
appearance: new fields.HTMLField(),
|
appearance: new fields.HTMLField(),
|
||||||
|
|
@ -140,15 +95,11 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
armorMarks: new fields.SchemaField({
|
armorMarks: new fields.SchemaField({
|
||||||
max: new fields.NumberField({ initial: 6, integer: true }),
|
max: new fields.NumberField({ initial: 6, integer: true }),
|
||||||
value: new fields.NumberField({ initial: 0, integer: true })
|
value: new fields.NumberField({ initial: 0, integer: true })
|
||||||
})
|
}),
|
||||||
|
levelData: new fields.EmbeddedDataField(DhPCLevelData)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get canLevelUp() {
|
|
||||||
// return Object.values(this.levels.data).some(x => !x.completed);
|
|
||||||
return this.levelData.currentLevel !== this.levelData.changedLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
get tier() {
|
get tier() {
|
||||||
return this.#getTier(this.levelData.currentLevel);
|
return this.#getTier(this.levelData.currentLevel);
|
||||||
}
|
}
|
||||||
|
|
@ -281,31 +232,6 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get totalAttributeMarks() {
|
|
||||||
return Object.keys(this.levelData.levelups).reduce((nr, level) => {
|
|
||||||
const nrAttributeMarks = Object.keys(this.levelData.levelups[level]).reduce((nr, tier) => {
|
|
||||||
nr += Object.keys(this.levelData.levelups[level][tier]?.attributes ?? {}).length * 2;
|
|
||||||
|
|
||||||
return nr;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
nr.push(...Array(nrAttributeMarks).fill(Number.parseInt(level)));
|
|
||||||
|
|
||||||
return nr;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
get availableAttributeMarks() {
|
|
||||||
const attributeMarks = Object.keys(this.attributes).flatMap(y => this.attributes[y].levelMarks);
|
|
||||||
return this.totalAttributeMarks.reduce((acc, attribute) => {
|
|
||||||
if (!attributeMarks.findSplice(x => x === attribute)) {
|
|
||||||
acc.push(attribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
get effects() {
|
get effects() {
|
||||||
return this.parent.items.reduce((acc, item) => {
|
return this.parent.items.reduce((acc, item) => {
|
||||||
const effects = item.system.effectData;
|
const effects = item.system.effectData;
|
||||||
|
|
@ -357,141 +283,37 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
: null;
|
: 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() {
|
prepareDerivedData() {
|
||||||
this.resources.hope.max = 6 - this.story.scars.length;
|
this.resources.hope.max = 6 - this.story.scars.length;
|
||||||
if (this.resources.hope.value >= this.resources.hope.max) {
|
if (this.resources.hope.value >= this.resources.hope.max) {
|
||||||
this.resources.hope.value = Math.max(this.resources.hope.max - 1, 0);
|
this.resources.hope.value = Math.max(this.resources.hope.max - 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var attributeKey in this.attributes) {
|
const armor = this.armor;
|
||||||
const attribute = this.attributes[attributeKey];
|
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
|
||||||
|
};
|
||||||
|
|
||||||
attribute.levelMark = attribute.levelMarks.find(x => this.isSameTier(x)) ?? null;
|
|
||||||
|
|
||||||
const actualValue = attribute.data.base + attribute.levelMarks.length + attribute.data.bonus;
|
|
||||||
attribute.data.actualValue = actualValue;
|
|
||||||
attribute.data.value = attribute.data.overrideValue
|
|
||||||
? attribute.data.overrideValue
|
|
||||||
: attribute.data.actualValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.evasion = this.class?.system?.evasion ?? 0;
|
|
||||||
// this.armor.value = this.activeArmor?.baseScore ?? 0;
|
|
||||||
this.damageThresholds = this.computeDamageThresholds();
|
|
||||||
|
|
||||||
this.applyLevels();
|
|
||||||
this.applyEffects();
|
this.applyEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
computeDamageThresholds() {
|
|
||||||
// TODO: missing weapon features and domain cards calculation
|
|
||||||
if (!this.armor) {
|
|
||||||
return {
|
|
||||||
major: this.levelData.currentLevel,
|
|
||||||
severe: this.levelData.currentLevel * 2
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
baseThresholds: { major = 0, severe = 0 }
|
|
||||||
} = this.armor.system;
|
|
||||||
return {
|
|
||||||
major: major + this.levelData.currentLevel,
|
|
||||||
severe: severe + this.levelData.currentLevel
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
applyLevels() {
|
|
||||||
let healthBonus = 0,
|
|
||||||
stressBonus = 0,
|
|
||||||
proficiencyBonus = 0,
|
|
||||||
evasionBonus = 0,
|
|
||||||
armorBonus = 0;
|
|
||||||
let experienceBonuses = {};
|
|
||||||
let advancementFirst = null,
|
|
||||||
advancementSecond = null;
|
|
||||||
for (var level in this.levelData.levelups) {
|
|
||||||
var levelData = this.levelData.levelups[level];
|
|
||||||
for (var tier in levelData) {
|
|
||||||
var tierData = levelData[tier];
|
|
||||||
if (tierData) {
|
|
||||||
healthBonus += Object.keys(tierData.hitPointSlots).length;
|
|
||||||
stressBonus += Object.keys(tierData.stressSlots).length;
|
|
||||||
proficiencyBonus += Object.keys(tierData.proficiency).length;
|
|
||||||
advancementFirst =
|
|
||||||
Object.keys(tierData.subclass).length > 0 && level >= 5 && level <= 7
|
|
||||||
? { ...tierData.subclass[0], tier: getTier(Number.parseInt(level), true) }
|
|
||||||
: advancementFirst;
|
|
||||||
advancementSecond =
|
|
||||||
Object.keys(tierData.subclass).length > 0 && level >= 8 && level <= 10
|
|
||||||
? { ...tierData.subclass[0], tier: getTier(Number.parseInt(level), true) }
|
|
||||||
: advancementSecond;
|
|
||||||
|
|
||||||
for (var index in Object.keys(tierData.experiences)) {
|
|
||||||
for (var experienceKey in tierData.experiences[index]) {
|
|
||||||
var experience = tierData.experiences[index][experienceKey];
|
|
||||||
experienceBonuses[experience] = experienceBonuses[experience]
|
|
||||||
? experienceBonuses[experience] + 1
|
|
||||||
: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
evasionBonus += Object.keys(tierData.armorOrEvasionSlot).filter(
|
|
||||||
x => tierData.armorOrEvasionSlot[x] === 'evasion'
|
|
||||||
).length;
|
|
||||||
armorBonus += Object.keys(tierData.armorOrEvasionSlot).filter(
|
|
||||||
x => tierData.armorOrEvasionSlot[x] === 'armor'
|
|
||||||
).length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.resources.health.max += healthBonus;
|
|
||||||
this.resources.stress.max += stressBonus;
|
|
||||||
this.proficiency.value += proficiencyBonus;
|
|
||||||
this.evasion += evasionBonus;
|
|
||||||
this.armorMarks = {
|
|
||||||
max: this.armor ? this.armor.system.marks.max + armorBonus : 0,
|
|
||||||
value: this.armor ? this.armor.system.marks.value : 0
|
|
||||||
};
|
|
||||||
|
|
||||||
this.experiences = this.experiences.map(x => ({ ...x, value: x.value + (experienceBonuses[x.id] ?? 0) }));
|
|
||||||
|
|
||||||
const subclassFeatures = this.subclassFeatures;
|
|
||||||
if (advancementFirst) {
|
|
||||||
if (advancementFirst.multiclass) {
|
|
||||||
this.multiclassSubclass.system[`${advancementFirst.feature}Feature`].unlocked = true;
|
|
||||||
this.multiclassSubclass.system[`${advancementFirst.feature}Feature`].tier = advancementFirst.tier;
|
|
||||||
subclassFeatures.multiclassSubclass[advancementFirst.feature].forEach(x => (x.system.disabled = false));
|
|
||||||
} else {
|
|
||||||
this.subclass.system[`${advancementFirst.feature}Feature`].unlocked = true;
|
|
||||||
this.subclass.system[`${advancementFirst.feature}Feature`].tier = advancementFirst.tier;
|
|
||||||
subclassFeatures.subclass[advancementFirst.feature].forEach(x => (x.system.disabled = false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (advancementSecond) {
|
|
||||||
if (advancementSecond.multiclass) {
|
|
||||||
this.multiclassSubclass.system[`${advancementSecond.feature}Feature`].unlocked = true;
|
|
||||||
this.multiclassSubclass.system[`${advancementSecond.feature}Feature`].tier = advancementSecond.tier;
|
|
||||||
subclassFeatures.multiclassSubclass[advancementSecond.feature].forEach(
|
|
||||||
x => (x.system.disabled = false)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.subclass.system[`${advancementSecond.feature}Feature`].unlocked = true;
|
|
||||||
this.subclass.system[`${advancementSecond.feature}Feature`].tier = advancementSecond.tier;
|
|
||||||
subclassFeatures.subclass[advancementSecond.feature].forEach(x => (x.system.disabled = false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//General progression
|
|
||||||
for (var i = 0; i < this.levelData.currentLevel; i++) {
|
|
||||||
const tier = getTier(i + 1);
|
|
||||||
if (tier !== 'tier0') {
|
|
||||||
this.domainData.maxLoadout = Math.min(this.domainData.maxLoadout + 1, 5);
|
|
||||||
this.domainData.maxCards += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applyEffects() {
|
applyEffects() {
|
||||||
const effects = this.effects;
|
const effects = this.effects;
|
||||||
for (var key in effects) {
|
for (var key in effects) {
|
||||||
|
|
@ -499,10 +321,10 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
for (var effect of effectType) {
|
for (var effect of effectType) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SYSTEM.EFFECTS.effectTypes.health.id:
|
case SYSTEM.EFFECTS.effectTypes.health.id:
|
||||||
this.resources.health.max += effect.value.valueData.value;
|
this.resources.hitPoints.bonus += effect.value.valueData.value;
|
||||||
break;
|
break;
|
||||||
case SYSTEM.EFFECTS.effectTypes.stress.id:
|
case SYSTEM.EFFECTS.effectTypes.stress.id:
|
||||||
this.resources.stress.max += effect.value.valueData.value;
|
this.resources.stress.bonus += effect.value.valueData.value;
|
||||||
break;
|
break;
|
||||||
case SYSTEM.EFFECTS.effectTypes.damage.id:
|
case SYSTEM.EFFECTS.effectTypes.damage.id:
|
||||||
this.bonuses.damage.push({
|
this.bonuses.damage.push({
|
||||||
|
|
@ -529,10 +351,6 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
return twoHanded ? 'twoHanded' : oneHanded ? 'oneHanded' : null;
|
return twoHanded ? 'twoHanded' : oneHanded ? 'oneHanded' : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
isSameTier(level) {
|
|
||||||
return this.#getTier(this.levelData.currentLevel) === this.#getTier(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
#getTier(level) {
|
#getTier(level) {
|
||||||
if (level >= 8) return 3;
|
if (level >= 8) return 3;
|
||||||
else if (level >= 5) return 2;
|
else if (level >= 5) return 2;
|
||||||
|
|
@ -540,3 +358,55 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
else return 0;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
13
module/data/settings/VariantRules.mjs
Normal file
13
module/data/settings/VariantRules.mjs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
export default class DhVariantRules extends foundry.abstract.DataModel {
|
||||||
|
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 })
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultSchema = {};
|
||||||
|
}
|
||||||
|
|
@ -6,13 +6,16 @@ import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||||
|
|
||||||
export default class DhpActor extends Actor {
|
export default class DhpActor extends Actor {
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
if ( (await super._preCreate(data, options, user)) === false ) return false;
|
if ((await super._preCreate(data, options, user)) === false) return false;
|
||||||
|
|
||||||
// Configure prototype token settings
|
// Configure prototype token settings
|
||||||
const prototypeToken = {};
|
const prototypeToken = {};
|
||||||
if ( this.type === "pc" ) Object.assign(prototypeToken, {
|
if (this.type === 'pc')
|
||||||
sight: { enabled: true }, actorLink: true, disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
Object.assign(prototypeToken, {
|
||||||
});
|
sight: { enabled: true },
|
||||||
|
actorLink: true,
|
||||||
|
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
||||||
|
});
|
||||||
this.updateSource({ prototypeToken });
|
this.updateSource({ prototypeToken });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,46 +24,103 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preUpdate(changed, options, user) {
|
async _preUpdate(changed, options, user) {
|
||||||
//Level Down
|
super._preUpdate(changed, options, user);
|
||||||
if (
|
}
|
||||||
changed.system?.levelData?.changedLevel &&
|
|
||||||
this.system.levelData.currentLevel > changed.system.levelData.changedLevel
|
async updateLevel(newLevel) {
|
||||||
) {
|
if (this.type !== 'pc' || newLevel === this.system.levelData.level.changed) return;
|
||||||
changed.system.levelData.currentLevel = changed.system.levelData.changedLevel;
|
|
||||||
changed.system.levelData.levelups = Object.keys(this.system.levelData.levelups).reduce((acc, x) => {
|
if (newLevel > this.system.levelData.level.current) {
|
||||||
if (x > changed.system.levelData.currentLevel) {
|
await this.update({ 'system.levelData.level.changed': newLevel });
|
||||||
acc[`-=${x}`] = null;
|
} else {
|
||||||
|
const updatedLevelups = Object.keys(this.system.levelData.levelups).reduce((acc, level) => {
|
||||||
|
if (Number(level) > newLevel) acc[`-=${level}`] = null;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const domainCards = Object.keys(this.system.levelData.levelups)
|
||||||
|
.filter(x => x > newLevel)
|
||||||
|
.flatMap(levelKey => {
|
||||||
|
const level = this.system.levelData.levelups[levelKey];
|
||||||
|
const achievementCards = level.achievements.domainCards.map(x => x.itemUuid);
|
||||||
|
const advancementCards = level.selections.filter(x => x.type === 'domainCard').map(x => x.itemUuid);
|
||||||
|
return [...achievementCards, ...advancementCards];
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var domainCard of domainCards) {
|
||||||
|
const itemCard = await this.items.find(x => x.uuid === domainCard);
|
||||||
|
itemCard.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.update({
|
||||||
|
system: {
|
||||||
|
levelData: {
|
||||||
|
level: {
|
||||||
|
current: newLevel,
|
||||||
|
changed: newLevel
|
||||||
|
},
|
||||||
|
levelups: updatedLevelups
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return acc;
|
async levelUp(levelupData) {
|
||||||
}, {});
|
const levelups = {};
|
||||||
|
for (var levelKey of Object.keys(levelupData)) {
|
||||||
|
const level = levelupData[levelKey];
|
||||||
|
const achievementDomainCards = [];
|
||||||
|
for (var card of Object.values(level.achievements.domainCards)) {
|
||||||
|
const item = await foundry.utils.fromUuid(card.uuid);
|
||||||
|
const embeddedItem = await this.createEmbeddedDocuments('Item', [item.toObject()]);
|
||||||
|
card.itemUuid = embeddedItem[0].uuid;
|
||||||
|
achievementDomainCards.push(card);
|
||||||
|
}
|
||||||
|
|
||||||
changed.system.attributes = Object.keys(this.system.attributes).reduce((acc, key) => {
|
const selections = [];
|
||||||
acc[key] = {
|
for (var optionKey of Object.keys(level.choices)) {
|
||||||
levelMarks: this.system.attributes[key].levelMarks.filter(
|
const selection = level.choices[optionKey];
|
||||||
x => x <= changed.system.levelData.currentLevel
|
for (var checkboxNr of Object.keys(selection)) {
|
||||||
)
|
const checkbox = selection[checkboxNr];
|
||||||
};
|
let itemUuid = null;
|
||||||
|
|
||||||
return acc;
|
if (checkbox.type === 'domainCard') {
|
||||||
}, {});
|
const item = await foundry.utils.fromUuid(checkbox.data[0]);
|
||||||
|
const embeddedItem = await this.createEmbeddedDocuments('Item', [item.toObject()]);
|
||||||
|
itemUuid = embeddedItem[0].uuid;
|
||||||
|
}
|
||||||
|
|
||||||
changed.system.experiences = this.system.experiences.filter(
|
selections.push({
|
||||||
x => x.level <= changed.system.levelData.currentLevel
|
...checkbox,
|
||||||
);
|
level: Number(levelKey),
|
||||||
|
optionKey: optionKey,
|
||||||
if (
|
checkboxNr: Number(checkboxNr),
|
||||||
this.system.multiclass &&
|
itemUuid
|
||||||
this.system.multiclass.system.multiclass > changed.system.levelData.changedLevel
|
});
|
||||||
) {
|
|
||||||
const multiclassFeatures = this.items.filter(x => x.system.multiclass);
|
|
||||||
for (var feature of multiclassFeatures) {
|
|
||||||
await feature.delete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
levelups[levelKey] = {
|
||||||
|
achievements: {
|
||||||
|
...level.achievements,
|
||||||
|
domainCards: achievementDomainCards
|
||||||
|
},
|
||||||
|
selections: selections
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
super._preUpdate(changed, options, user);
|
await this.update({
|
||||||
|
system: {
|
||||||
|
levelData: {
|
||||||
|
level: {
|
||||||
|
current: this.system.levelData.level.changed
|
||||||
|
},
|
||||||
|
levelups: levelups
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async diceRoll(modifier, shiftKey) {
|
async diceRoll(modifier, shiftKey) {
|
||||||
|
|
@ -286,9 +346,9 @@ export default class DhpActor extends Actor {
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
const update = {
|
const update = {
|
||||||
'system.resources.health.value': Math.min(
|
'system.resources.hitPoints.value': Math.min(
|
||||||
this.system.resources.health.value + hpDamage,
|
this.system.resources.hitPoints.value + hpDamage,
|
||||||
this.system.resources.health.max
|
this.system.resources.hitPoints.max
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -311,9 +371,9 @@ export default class DhpActor extends Actor {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SYSTEM.GENERAL.healingTypes.health.id:
|
case SYSTEM.GENERAL.healingTypes.health.id:
|
||||||
update = {
|
update = {
|
||||||
'system.resources.health.value': Math.min(
|
'system.resources.hitPoints.value': Math.min(
|
||||||
this.system.resources.health.value + healing,
|
this.system.resources.hitPoints.value + healing,
|
||||||
this.system.resources.health.max
|
this.system.resources.hitPoints.max
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,19 @@
|
||||||
import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs';
|
|
||||||
|
|
||||||
export default class DhpCombat extends Combat {
|
export default class DhpCombat extends Combat {
|
||||||
_sortCombatants(a, b) {
|
async startCombat() {
|
||||||
if (a.isNPC !== b.isNPC) {
|
this._playCombatSound('startEncounter');
|
||||||
const aVal = a.isNPC ? 0 : 1;
|
const updateData = { round: 1, turn: null };
|
||||||
const bVal = b.isNPC ? 0 : 1;
|
Hooks.callAll('combatStart', this, updateData);
|
||||||
|
await this.update(updateData);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
return aVal - bVal;
|
_sortCombatants(a, b) {
|
||||||
|
const aNPC = Number(a.isNPC);
|
||||||
|
const bNPC = Number(b.isNPC);
|
||||||
|
if (aNPC !== bNPC) {
|
||||||
|
return aNPC - bNPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.name.localeCompare(b.name);
|
return a.name.localeCompare(b.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
async useActionToken(combatantId) {
|
|
||||||
const automateActionPoints = await game.settings.get(
|
|
||||||
SYSTEM.id,
|
|
||||||
SYSTEM.SETTINGS.gameSettings.Automation.ActionPoints
|
|
||||||
);
|
|
||||||
|
|
||||||
if (game.user.isGM) {
|
|
||||||
if (this.system.actions < 1) return;
|
|
||||||
|
|
||||||
const update = automateActionPoints
|
|
||||||
? { 'system.activeCombatant': combatantId, 'system.actions': Math.max(this.system.actions - 1, 0) }
|
|
||||||
: { 'system.activeCombatant': combatantId };
|
|
||||||
|
|
||||||
await this.update(update);
|
|
||||||
} else {
|
|
||||||
const update = automateActionPoints
|
|
||||||
? { 'system.activeCombatant': combatantId, 'system.actions': this.system.actions + 1 }
|
|
||||||
: { 'system.activeCombatant': combatantId };
|
|
||||||
|
|
||||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
|
||||||
action: socketEvent.GMUpdate,
|
|
||||||
data: {
|
|
||||||
action: GMUpdateEvent.UpdateDocument,
|
|
||||||
uuid: this.uuid,
|
|
||||||
update: update
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,19 @@ import { getWidthOfText } from './utils.mjs';
|
||||||
export default class RegisterHandlebarsHelpers {
|
export default class RegisterHandlebarsHelpers {
|
||||||
static registerHelpers() {
|
static registerHelpers() {
|
||||||
Handlebars.registerHelper({
|
Handlebars.registerHelper({
|
||||||
looseEq: this.looseEq,
|
|
||||||
times: this.times,
|
times: this.times,
|
||||||
join: this.join,
|
join: this.join,
|
||||||
add: this.add,
|
add: this.add,
|
||||||
subtract: this.subtract,
|
subtract: this.subtract,
|
||||||
objectSelector: this.objectSelector,
|
objectSelector: this.objectSelector,
|
||||||
includes: this.includes,
|
includes: this.includes,
|
||||||
simpleEditor: this.simpleEditor,
|
debug: this.debug,
|
||||||
debug: this.debug
|
signedNumber: this.signedNumber,
|
||||||
|
switch: this.switch,
|
||||||
|
case: this.case
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static looseEq(a, b) {
|
|
||||||
return a == b;
|
|
||||||
}
|
|
||||||
|
|
||||||
static times(nr, block) {
|
static times(nr, block) {
|
||||||
var accum = '';
|
var accum = '';
|
||||||
for (var i = 0; i < nr; ++i) accum += block.fn(i);
|
for (var i = 0; i < nr; ++i) accum += block.fn(i);
|
||||||
|
|
@ -77,33 +74,25 @@ export default class RegisterHandlebarsHelpers {
|
||||||
return new Handlebars.SafeString(html);
|
return new Handlebars.SafeString(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
static rangePicker(options) {
|
|
||||||
let { name, value, min, max, step } = options.hash;
|
|
||||||
name = name || 'range';
|
|
||||||
value = value ?? '';
|
|
||||||
if (Number.isNaN(value)) value = '';
|
|
||||||
const html = `<input type="range" name="${name}" value="${value}" min="${min}" max="${max}" step="${step}"/>
|
|
||||||
<span class="range-value">${value}</span>`;
|
|
||||||
return new Handlebars.SafeString(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
static includes(list, item) {
|
static includes(list, item) {
|
||||||
return list.includes(item);
|
return list.includes(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
static simpleEditor(content, options) {
|
static signedNumber(number) {
|
||||||
const {
|
return number >= 0 ? `+${number}` : number;
|
||||||
target,
|
}
|
||||||
editable = true,
|
|
||||||
button,
|
static switch(value, options) {
|
||||||
engine = 'tinymce',
|
this.switch_value = value;
|
||||||
collaborate = false,
|
this.switch_break = false;
|
||||||
class: cssClass
|
return options.fn(this);
|
||||||
} = options.hash;
|
}
|
||||||
const config = { name: target, value: content, button, collaborate, editable, engine };
|
|
||||||
const element = foundry.applications.fields.createEditorInput(config);
|
static case(value, options) {
|
||||||
if (cssClass) element.querySelector('.editor-content').classList.add(cssClass);
|
if (value == this.switch_value) {
|
||||||
return new Handlebars.SafeString(element.outerHTML);
|
this.switch_break = true;
|
||||||
|
return options.fn(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static debug(a) {
|
static debug(a) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
|
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
|
||||||
|
import Tagify from '@yaireo/tagify';
|
||||||
|
|
||||||
export const loadCompendiumOptions = async compendiums => {
|
export const loadCompendiumOptions = async compendiums => {
|
||||||
const compendiumValues = [];
|
const compendiumValues = [];
|
||||||
|
|
@ -131,3 +132,93 @@ export const setDiceSoNiceForDualityRoll = (rollResult, advantage, disadvantage)
|
||||||
rollResult.dice[2].options.appearance = diceSoNicePresets.disadvantage;
|
rollResult.dice[2].options.appearance = diceSoNicePresets.disadvantage;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const chunkify = (array, chunkSize, mappingFunc) => {
|
||||||
|
var chunkifiedArray = [];
|
||||||
|
for (let i = 0; i < array.length; i += chunkSize) {
|
||||||
|
const chunk = array.slice(i, i + chunkSize);
|
||||||
|
if (mappingFunc) {
|
||||||
|
chunkifiedArray.push(mappingFunc(chunk));
|
||||||
|
} else {
|
||||||
|
chunkifiedArray.push(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunkifiedArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const tagifyElement = (element, options, onChange, tagifyOptions = {}) => {
|
||||||
|
const { maxTags } = tagifyOptions;
|
||||||
|
const tagifyElement = new Tagify(element, {
|
||||||
|
tagTextProp: 'name',
|
||||||
|
enforceWhitelist: true,
|
||||||
|
whitelist: Object.keys(options).map(key => {
|
||||||
|
const option = options[key];
|
||||||
|
return {
|
||||||
|
value: key,
|
||||||
|
name: game.i18n.localize(option.label),
|
||||||
|
src: option.src
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
maxTags: maxTags,
|
||||||
|
dropdown: {
|
||||||
|
mapValueTo: 'name',
|
||||||
|
searchKeys: ['name'],
|
||||||
|
enabled: 0,
|
||||||
|
maxItems: 20,
|
||||||
|
closeOnSelect: true,
|
||||||
|
highlightFirst: false
|
||||||
|
},
|
||||||
|
templates: {
|
||||||
|
tag(tagData) {
|
||||||
|
return `<tag title="${tagData.title || tagData.value}"
|
||||||
|
contenteditable='false'
|
||||||
|
spellcheck='false'
|
||||||
|
tabIndex="${this.settings.a11y.focusableTags ? 0 : -1}"
|
||||||
|
class="${this.settings.classNames.tag} ${tagData.class ? tagData.class : ''}"
|
||||||
|
${this.getAttributes(tagData)}>
|
||||||
|
<x class="${this.settings.classNames.tagX}" role='button' aria-label='remove tag'></x>
|
||||||
|
<div>
|
||||||
|
<span class="${this.settings.classNames.tagText}">${tagData[this.settings.tagTextProp] || tagData.value}</span>
|
||||||
|
${tagData.src ? `<img src="${tagData.src}"></i>` : ''}
|
||||||
|
</div>
|
||||||
|
</tag>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSelect = async event => {
|
||||||
|
const inputElement = event.detail.tagify.DOM.originalInput;
|
||||||
|
const selectedOptions = event.detail?.value ? JSON.parse(event.detail.value) : [];
|
||||||
|
|
||||||
|
const unusedDropDownItems = event.detail.tagify.suggestedListItems;
|
||||||
|
const missingOptions = Object.keys(options).filter(x => !unusedDropDownItems.find(item => item.value === x));
|
||||||
|
const removedItem = missingOptions.find(x => !selectedOptions.find(item => item.value === x));
|
||||||
|
const addedItem = removedItem
|
||||||
|
? null
|
||||||
|
: selectedOptions.find(x => !missingOptions.find(item => item === x.value));
|
||||||
|
|
||||||
|
const changedItem = { option: removedItem ?? addedItem.value, removed: Boolean(removedItem) };
|
||||||
|
|
||||||
|
onChange(selectedOptions, changedItem, inputElement);
|
||||||
|
};
|
||||||
|
tagifyElement.on('change', onSelect);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue) => {
|
||||||
|
return Object.keys(property).reduce((acc, key) => {
|
||||||
|
if (innerProperty) {
|
||||||
|
if (innerPropertyDefaultValue !== undefined) {
|
||||||
|
acc[`${key}`] = {
|
||||||
|
[innerProperty]: innerPropertyDefaultValue
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
acc[`${key}.-=${innerProperty}`] = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc[`-=${key}`] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,199 +1,100 @@
|
||||||
import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs';
|
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
export default class DhpCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
actions: {
|
||||||
constructor(data, context) {
|
requestSpotlight: this.requestSpotlight,
|
||||||
super(data, context);
|
toggleSpotlight: this.toggleSpotlight,
|
||||||
|
setActionTokens: this.setActionTokens
|
||||||
Hooks.on(socketEvent.DhpFearUpdate, this.onFearUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
get template() {
|
|
||||||
return 'systems/daggerheart/templates/ui/combatTracker.hbs';
|
|
||||||
}
|
|
||||||
|
|
||||||
activateListeners(html) {
|
|
||||||
super.activateListeners(html);
|
|
||||||
html.on('click', '.token-action-tokens .use-action-token', this.useActionToken.bind(this));
|
|
||||||
html.on('click', '.encounter-gm-resources .trade-actions', this.tradeActions.bind(this));
|
|
||||||
html.on('click', '.encounter-gm-resources .trade-fear', this.tradeFear.bind(this));
|
|
||||||
html.on('click', '.encounter-gm-resources .icon-button.up', this.increaseResource.bind(this));
|
|
||||||
html.on('click', '.encounter-gm-resources .icon-button.down', this.decreaseResource.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
async useActionToken(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
const combatant = event.currentTarget.dataset.combatant;
|
|
||||||
await game.combat.useActionToken(combatant);
|
|
||||||
}
|
|
||||||
|
|
||||||
async tradeActions(event) {
|
|
||||||
if (event.currentTarget.classList.contains('disabled')) return;
|
|
||||||
|
|
||||||
const currentFear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
|
||||||
const value = currentFear + 1;
|
|
||||||
|
|
||||||
if (value <= 6) {
|
|
||||||
Hooks.callAll(socketEvent.GMUpdate, GMUpdateEvent.UpdateFear, null, value);
|
|
||||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
|
||||||
action: socketEvent.GMUpdate,
|
|
||||||
data: { action: GMUpdateEvent.UpdateFear, update: value }
|
|
||||||
});
|
|
||||||
await game.combat.update({ 'system.actions': game.combat.system.actions - 2 });
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async tradeFear() {
|
|
||||||
if (event.currentTarget.classList.contains('disabled')) return;
|
|
||||||
|
|
||||||
const currentFear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
|
||||||
const value = currentFear - 1;
|
|
||||||
if (value >= 0) {
|
|
||||||
Hooks.callAll(socketEvent.GMUpdate, GMUpdateEvent.UpdateFear, null, value);
|
|
||||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
|
||||||
action: socketEvent.GMUpdate,
|
|
||||||
data: { action: GMUpdateEvent.UpdateFear, update: value }
|
|
||||||
});
|
|
||||||
await game.combat.update({ 'system.actions': game.combat.system.actions + 2 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async increaseResource(event) {
|
|
||||||
if (event.currentTarget.dataset.type === 'action') {
|
|
||||||
await game.combat.update({ 'system.actions': game.combat.system.actions + 1 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentFear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
|
||||||
const value = currentFear + 1;
|
|
||||||
if (event.currentTarget.dataset.type === 'fear' && value <= 6) {
|
|
||||||
Hooks.callAll(socketEvent.GMUpdate, GMUpdateEvent.UpdateFear, null, value);
|
|
||||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
|
||||||
action: socketEvent.GMUpdate,
|
|
||||||
data: { action: GMUpdateEvent.UpdateFear, update: value }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
async decreaseResource(event) {
|
|
||||||
if (event.currentTarget.dataset.type === 'action' && game.combat.system.actions - 1 >= 0) {
|
|
||||||
await game.combat.update({ 'system.actions': game.combat.system.actions - 1 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentFear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
|
||||||
const value = currentFear - 1;
|
|
||||||
if (event.currentTarget.dataset.type === 'fear' && value >= 0) {
|
|
||||||
Hooks.callAll(socketEvent.GMUpdate, GMUpdateEvent.UpdateFear, null, value);
|
|
||||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
|
||||||
action: socketEvent.GMUpdate,
|
|
||||||
data: { action: GMUpdateEvent.UpdateFear, update: value }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
async getData(options = {}) {
|
|
||||||
let context = await super.getData(options);
|
|
||||||
|
|
||||||
// Get the combat encounters possible for the viewed Scene
|
|
||||||
const combat = this.viewed;
|
|
||||||
const hasCombat = combat !== null;
|
|
||||||
const combats = this.combats;
|
|
||||||
const currentIdx = combats.findIndex(c => c === combat);
|
|
||||||
const previousId = currentIdx > 0 ? combats[currentIdx - 1].id : null;
|
|
||||||
const nextId = currentIdx < combats.length - 1 ? combats[currentIdx + 1].id : null;
|
|
||||||
const settings = game.settings.get('core', Combat.CONFIG_SETTING);
|
|
||||||
|
|
||||||
// Prepare rendering data
|
|
||||||
context = foundry.utils.mergeObject(context, {
|
|
||||||
combats: combats,
|
|
||||||
currentIndex: currentIdx + 1,
|
|
||||||
combatCount: combats.length,
|
|
||||||
hasCombat: hasCombat,
|
|
||||||
combat,
|
|
||||||
turns: [],
|
|
||||||
previousId,
|
|
||||||
nextId,
|
|
||||||
started: this.started,
|
|
||||||
control: false,
|
|
||||||
settings,
|
|
||||||
linked: combat?.scene !== null,
|
|
||||||
labels: {}
|
|
||||||
});
|
|
||||||
context.labels.scope = game.i18n.localize(`COMBAT.${context.linked ? 'Linked' : 'Unlinked'}`);
|
|
||||||
if (!hasCombat) return context;
|
|
||||||
|
|
||||||
// Format information about each combatant in the encounter
|
|
||||||
let hasDecimals = false;
|
|
||||||
const turns = [];
|
|
||||||
for (let [i, combatant] of combat.turns.entries()) {
|
|
||||||
if (!combatant.visible) continue;
|
|
||||||
|
|
||||||
// Prepare turn data
|
|
||||||
const resource =
|
|
||||||
combatant.permission >= CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER ? combatant.resource : null;
|
|
||||||
const turn = {
|
|
||||||
id: combatant.id,
|
|
||||||
name: combatant.name,
|
|
||||||
img: await this._getCombatantThumbnail(combatant),
|
|
||||||
active: combatant.id === combat.system.activeCombatant,
|
|
||||||
owner: combatant.isOwner,
|
|
||||||
defeated: combatant.isDefeated,
|
|
||||||
hidden: combatant.hidden,
|
|
||||||
initiative: combatant.initiative,
|
|
||||||
hasRolled: combatant.initiative !== null,
|
|
||||||
hasResource: resource !== null,
|
|
||||||
resource: resource,
|
|
||||||
canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'),
|
|
||||||
playerCharacter: game.user?.character?.id === combatant.actor.id,
|
|
||||||
ownedByPlayer: combatant.hasPlayerOwner
|
|
||||||
};
|
|
||||||
if (turn.initiative !== null && !Number.isInteger(turn.initiative)) hasDecimals = true;
|
|
||||||
turn.css = [turn.active ? 'active' : '', turn.hidden ? 'hidden' : '', turn.defeated ? 'defeated' : '']
|
|
||||||
.join(' ')
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
// Actor and Token status effects
|
|
||||||
turn.effects = new Set();
|
|
||||||
if (combatant.token) {
|
|
||||||
combatant.token.effects.forEach(e => turn.effects.add(e));
|
|
||||||
if (combatant.token.overlayEffect) turn.effects.add(combatant.token.overlayEffect);
|
|
||||||
}
|
|
||||||
if (combatant.actor) {
|
|
||||||
for (const effect of combatant.actor.temporaryEffects) {
|
|
||||||
if (effect.statuses.has(CONFIG.specialStatusEffects.DEFEATED)) turn.defeated = true;
|
|
||||||
else if (effect.icon) turn.effects.add(effect.icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
turns.push(turn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format initiative numeric precision
|
|
||||||
const precision = CONFIG.Combat.initiative.decimals;
|
|
||||||
turns.forEach(t => {
|
|
||||||
if (t.initiative !== null) t.initiative = t.initiative.toFixed(hasDecimals ? precision : 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
const fear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
|
||||||
|
|
||||||
// Merge update data for rendering
|
|
||||||
return foundry.utils.mergeObject(context, {
|
|
||||||
round: combat.round,
|
|
||||||
turn: combat.turn,
|
|
||||||
turns: turns,
|
|
||||||
control: combat.combatant?.players?.includes(game.user),
|
|
||||||
fear: fear
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onFearUpdate = async () => {
|
|
||||||
this.render(true);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async close(options) {
|
static PARTS = {
|
||||||
Hooks.off(socketEvent.DhpFearUpdate, this.onFearUpdate);
|
header: {
|
||||||
|
template: 'systems/daggerheart/templates/ui/combat/combatTrackerHeader.hbs'
|
||||||
|
},
|
||||||
|
tracker: {
|
||||||
|
template: 'systems/daggerheart/templates/ui/combat/combatTracker.hbs'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
template: 'systems/daggerheart/templates/ui/combat/combatTrackerFooter.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return super.close(options);
|
async _prepareCombatContext(context, options) {
|
||||||
|
await super._prepareCombatContext(context, options);
|
||||||
|
|
||||||
|
Object.assign(context, {
|
||||||
|
fear: game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareTrackerContext(context, options) {
|
||||||
|
await super._prepareTrackerContext(context, options);
|
||||||
|
|
||||||
|
const adversaries = context.turns?.filter(x => x.isNPC) ?? [];
|
||||||
|
const characters = context.turns?.filter(x => !x.isNPC) ?? [];
|
||||||
|
|
||||||
|
Object.assign(context, {
|
||||||
|
actionTokens: game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.variantRules).actionTokens,
|
||||||
|
adversaries,
|
||||||
|
characters
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareTurnContext(combat, combatant, index) {
|
||||||
|
const turn = await super._prepareTurnContext(combat, combatant, index);
|
||||||
|
return { ...turn, isNPC: combatant.isNPC, system: combatant.system.toObject() };
|
||||||
|
}
|
||||||
|
|
||||||
|
_getCombatContextOptions() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'COMBAT.ClearMovementHistories',
|
||||||
|
icon: '<i class="fa-solid fa-shoe-prints"></i>',
|
||||||
|
condition: () => game.user.isGM && this.viewed?.combatants.size > 0,
|
||||||
|
callback: () => this.viewed.clearMovementHistories()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'COMBAT.Delete',
|
||||||
|
icon: '<i class="fa-solid fa-trash"></i>',
|
||||||
|
condition: () => game.user.isGM && !!this.viewed,
|
||||||
|
callback: () => this.viewed.endCombat()
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static async requestSpotlight(_, target) {
|
||||||
|
const { combatantId } = target.closest('[data-combatant-id]')?.dataset ?? {};
|
||||||
|
const combatant = this.viewed.combatants.get(combatantId);
|
||||||
|
await combatant.update({
|
||||||
|
'system.spotlight': {
|
||||||
|
requesting: !combatant.system.spotlight.requesting
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async toggleSpotlight(_, target) {
|
||||||
|
const { combatantId } = target.closest('[data-combatant-id]')?.dataset ?? {};
|
||||||
|
const combatant = this.viewed.combatants.get(combatantId);
|
||||||
|
|
||||||
|
const toggleTurn = this.viewed.combatants.contents
|
||||||
|
.sort(this.viewed._sortCombatants)
|
||||||
|
.map(x => x.id)
|
||||||
|
.indexOf(combatantId);
|
||||||
|
|
||||||
|
await this.viewed.update({ turn: this.viewed.turn === toggleTurn ? null : toggleTurn });
|
||||||
|
await combatant.update({ 'system.spotlight.requesting': false });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async setActionTokens(_, target) {
|
||||||
|
const { combatantId, tokenIndex } = target.closest('[data-combatant-id]')?.dataset ?? {};
|
||||||
|
|
||||||
|
const combatant = this.viewed.combatants.get(combatantId);
|
||||||
|
const changeIndex = Number(tokenIndex);
|
||||||
|
const newIndex = combatant.system.actionTokens > changeIndex ? changeIndex : changeIndex + 1;
|
||||||
|
|
||||||
|
await combatant.update({ 'system.actionTokens': newIndex });
|
||||||
|
this.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs';
|
|
||||||
|
|
||||||
export default class DhpPlayers extends foundry.applications.ui.Players {
|
|
||||||
constructor(data, context) {
|
|
||||||
super(data, context);
|
|
||||||
|
|
||||||
Hooks.on(socketEvent.DhpFearUpdate, this.onFearUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
get template() {
|
|
||||||
return 'systems/daggerheart/templates/ui/players.hbs';
|
|
||||||
}
|
|
||||||
|
|
||||||
async getData(options = {}) {
|
|
||||||
const context = super.getData(options);
|
|
||||||
context.fear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
|
||||||
context.user = game.user;
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
activateListeners(html) {
|
|
||||||
// Toggle online/offline
|
|
||||||
html.find('.players-mode').click(this._onToggleOfflinePlayers.bind(this));
|
|
||||||
html.find('.fear-control.up').click(async event => await this.updateFear(event, 1));
|
|
||||||
html.find('.fear-control.down').click(async event => await this.updateFear(event, -1));
|
|
||||||
|
|
||||||
// Context menu
|
|
||||||
const contextOptions = this._getUserContextOptions();
|
|
||||||
Hooks.call('getUserContextOptions', html, contextOptions);
|
|
||||||
new ContextMenu(html, '.player', contextOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateFear(_, change) {
|
|
||||||
const fear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
|
||||||
const value = Math.max(Math.min(fear + change, 6), 0);
|
|
||||||
Hooks.callAll(socketEvent.GMUpdate, GMUpdateEvent.UpdateFear, null, value);
|
|
||||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
|
||||||
action: socketEvent.GMUpdate,
|
|
||||||
data: { action: GMUpdateEvent.UpdateFear, update: value }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onFearUpdate = async () => {
|
|
||||||
this.render(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
async close(options) {
|
|
||||||
Hooks.off(socketEvent.DhpFearUpdate, this.onFearUpdate);
|
|
||||||
|
|
||||||
return super.close(options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
/* Drop Shadows */
|
/* Drop Shadows */
|
||||||
/* Background */
|
/* Background */
|
||||||
/* Duality */
|
/* Duality */
|
||||||
|
/* Fear */
|
||||||
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
||||||
.daggerheart.sheet.class .editor {
|
.daggerheart.sheet.class .editor {
|
||||||
height: 500px;
|
height: 500px;
|
||||||
|
|
@ -1293,60 +1294,92 @@
|
||||||
.daggerheart.sheet.pc div[data-application-part] .sheet-body .inventory-container .inventory-item-list .inventory-row img {
|
.daggerheart.sheet.pc div[data-application-part] .sheet-body .inventory-container .inventory-item-list .inventory-row img {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
}
|
}
|
||||||
.combat-sidebar .encounter-gm-resources {
|
.combat-sidebar .encounter-controls.combat {
|
||||||
flex: 0;
|
justify-content: space-between;
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
}
|
||||||
.combat-sidebar .encounter-gm-resources .gm-resource-controls {
|
.combat-sidebar .encounter-controls.combat .encounter-control-fear-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
position: relative;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 4px;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
color: black;
|
||||||
}
|
}
|
||||||
.combat-sidebar .encounter-gm-resources .gm-resource-tools {
|
.combat-sidebar .encounter-controls.combat .encounter-control-fear-container .dice {
|
||||||
display: flex;
|
height: 24px;
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 0 5px 0 4px;
|
|
||||||
}
|
}
|
||||||
.combat-sidebar .encounter-gm-resources .gm-resource-tools i {
|
.combat-sidebar .encounter-controls.combat .encounter-control-fear-container .encounter-control-fear {
|
||||||
margin: 0 2px;
|
position: absolute;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
.combat-sidebar .encounter-gm-resources .gm-resource-tools i.disabled {
|
.combat-sidebar .encounter-controls.combat .encounter-control-fear-container .encounter-control-counter {
|
||||||
opacity: 0.6;
|
position: absolute;
|
||||||
|
right: -10px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
}
|
}
|
||||||
.combat-sidebar .encounter-gm-resources .gm-resource-tools i:hover:not(.disabled) {
|
.combat-sidebar .encounter-controls.combat .control-buttons {
|
||||||
cursor: pointer;
|
width: min-content;
|
||||||
filter: drop-shadow(0 0 3px red);
|
|
||||||
}
|
}
|
||||||
.combat-sidebar .encounter-gm-resources .gm-resource {
|
.combat-sidebar .combatant-controls {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
flex: 0;
|
||||||
padding: 4px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 2px solid black;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
}
|
||||||
.combat-sidebar .token-action-tokens {
|
.combat-sidebar .token-actions {
|
||||||
flex: 0 0 48px;
|
align-self: stretch;
|
||||||
|
display: flex;
|
||||||
|
align-items: top;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.combat-sidebar .token-actions .action-tokens {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.combat-sidebar .token-actions .action-tokens .action-token {
|
||||||
|
height: 22px;
|
||||||
|
width: 22px;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 8px;
|
||||||
|
--button-size: 0;
|
||||||
|
}
|
||||||
|
.combat-sidebar .token-actions .action-tokens .action-token.used {
|
||||||
|
opacity: 0.5;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.combat-sidebar .token-actions button {
|
||||||
|
font-size: 22px;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
.combat-sidebar .token-actions button.main {
|
||||||
|
background: var(--button-hover-background-color);
|
||||||
|
color: var(--button-hover-text-color);
|
||||||
|
border-color: var(--button-hover-border-color);
|
||||||
|
}
|
||||||
|
.combat-sidebar .token-actions button.main:hover {
|
||||||
|
filter: drop-shadow(0 0 3px var(--button-hover-text-color));
|
||||||
|
}
|
||||||
|
.combat-sidebar .spotlight-control {
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
.combat-sidebar .spotlight-control:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.combat-sidebar .spotlight-control.discrete:hover {
|
||||||
|
background: inherit;
|
||||||
|
}
|
||||||
|
.combat-sidebar .spotlight-control.requesting {
|
||||||
|
filter: drop-shadow(0 0 3px gold);
|
||||||
|
color: var(--button-hover-text-color);
|
||||||
|
}
|
||||||
|
.combat-sidebar h4 {
|
||||||
|
margin: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.combat-sidebar .token-action-tokens .use-action-token.disabled {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
.combat-sidebar .icon-button.spaced {
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
.combat-sidebar .icon-button.disabled {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
.combat-sidebar .icon-button:hover:not(.disabled) {
|
|
||||||
cursor: pointer;
|
|
||||||
filter: drop-shadow(0 0 3px red);
|
|
||||||
}
|
|
||||||
.chat-message.duality {
|
.chat-message.duality {
|
||||||
border-color: black;
|
border-color: black;
|
||||||
padding: 8px 0 0 0;
|
padding: 8px 0 0 0;
|
||||||
|
|
@ -2718,15 +2751,233 @@ div.daggerheart.views.multiclass {
|
||||||
.item-button .item-icon.checked {
|
.item-button .item-icon.checked {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
.theme-light .daggerheart.levelup .tiers-container .tier-container {
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .window-content {
|
||||||
|
max-height: 960px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup div[data-application-part='form'] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup section .section-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-navigation-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 22px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-navigation-container nav {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-navigation-container nav .levelup-tab-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-navigation-container .levelup-navigation-actions {
|
||||||
|
width: 306px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
gap: 16px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-navigation-container .levelup-navigation-actions * {
|
||||||
|
width: calc(50% - 8px);
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container.inactive {
|
||||||
|
opacity: 0.4;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container legend {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 3fr;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container .checkboxes-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container .checkboxes-container .checkbox-grouping-coontainer {
|
||||||
|
display: flex;
|
||||||
|
height: min-content;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container .checkboxes-container .checkbox-grouping-coontainer.multi {
|
||||||
|
border: 2px solid grey;
|
||||||
|
padding: 2.4px 2.5px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container .checkboxes-container .checkbox-grouping-coontainer.multi .selection-checkbox {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container .checkboxes-container .checkbox-grouping-coontainer .selection-checkbox {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container .checkbox-group-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .achievement-experience-cards {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .achievement-experience-cards .achievement-experience-card {
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
font-size: 18px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .achievement-experience-cards .achievement-experience-card .achievement-experience-marker {
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .levelup-card-selection {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .card-preview-container {
|
||||||
|
width: calc(100% * (1 / 5));
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container .levelup-domain-label {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
top: 4px;
|
||||||
|
background: grey;
|
||||||
|
padding: 0 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container img {
|
||||||
|
height: 124px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container .levelup-domain-selected {
|
||||||
|
position: absolute;
|
||||||
|
height: 54px;
|
||||||
|
width: 54px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid;
|
||||||
|
font-size: 48px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||||
|
color: var(--color-dark-5);
|
||||||
|
top: calc(50% - 29px);
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container .levelup-domain-selected i {
|
||||||
|
position: relative;
|
||||||
|
right: 2px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-selections-container .levelup-selections-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-summary-container .level-achievements-container,
|
||||||
|
.daggerheart.levelup .levelup-summary-container .level-advancements-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-summary-container .level-achievements-container h2,
|
||||||
|
.daggerheart.levelup .levelup-summary-container .level-advancements-container h2,
|
||||||
|
.daggerheart.levelup .levelup-summary-container .level-achievements-container h3,
|
||||||
|
.daggerheart.levelup .levelup-summary-container .level-advancements-container h3,
|
||||||
|
.daggerheart.levelup .levelup-summary-container .level-achievements-container h4,
|
||||||
|
.daggerheart.levelup .levelup-summary-container .level-advancements-container h4,
|
||||||
|
.daggerheart.levelup .levelup-summary-container .level-achievements-container h5,
|
||||||
|
.daggerheart.levelup .levelup-summary-container .level-advancements-container h5 {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-summary-container .increase-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-summary-container .summary-selection-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-summary-container .summary-selection-container .summary-selection {
|
||||||
|
border: 2px solid;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 4px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-footer {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
:root {
|
:root {
|
||||||
--primary-color-fear: rgba(9, 71, 179, 0.75);
|
|
||||||
--secondary-color-fear: rgba(9, 71, 179, 0.75);
|
|
||||||
--shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
--shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
||||||
|
--fear-animation: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease, opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
#resources {
|
#resources {
|
||||||
min-height: calc(var(--header-height) + 4rem);
|
min-height: calc(var(--header-height) + 4rem);
|
||||||
min-width: 4rem;
|
min-width: 4rem;
|
||||||
color: #d3d3d3;
|
color: #d3d3d3;
|
||||||
|
transition: var(--fear-animation);
|
||||||
|
}
|
||||||
|
#resources header,
|
||||||
|
#resources .controls,
|
||||||
|
#resources .window-resize-handle {
|
||||||
|
transition: var(--fear-animation);
|
||||||
}
|
}
|
||||||
#resources .window-content {
|
#resources .window-content {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
|
@ -2746,7 +2997,7 @@ div.daggerheart.views.multiclass {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 3rem;
|
width: 3rem;
|
||||||
background-color: var(--primary-color-fear);
|
background-color: var(rgba(9, 71, 179, 0.75));
|
||||||
-webkit-box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.75);
|
-webkit-box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.75);
|
||||||
box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.75);
|
box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.75);
|
||||||
color: #d3d3d3;
|
color: #d3d3d3;
|
||||||
|
|
@ -2811,7 +3062,7 @@ div.daggerheart.views.multiclass {
|
||||||
#resources .window-content #resource-fear.isGM i:hover {
|
#resources .window-content #resource-fear.isGM i:hover {
|
||||||
font-size: var(--font-size-20);
|
font-size: var(--font-size-20);
|
||||||
}
|
}
|
||||||
#resources button[data-action="close"] {
|
#resources button[data-action='close'] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#resources:not(:hover):not(.minimized) {
|
#resources:not(:hover):not(.minimized) {
|
||||||
|
|
@ -2822,7 +3073,7 @@ div.daggerheart.views.multiclass {
|
||||||
#resources:not(:hover):not(.minimized) header,
|
#resources:not(:hover):not(.minimized) header,
|
||||||
#resources:not(:hover):not(.minimized) .controls,
|
#resources:not(:hover):not(.minimized) .controls,
|
||||||
#resources:not(:hover):not(.minimized) .window-resize-handle {
|
#resources:not(:hover):not(.minimized) .window-resize-handle {
|
||||||
visibility: hidden;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
#resources:has(.fear-bar) {
|
#resources:has(.fear-bar) {
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
|
|
@ -2903,7 +3154,7 @@ div.daggerheart.views.multiclass {
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(https://fonts.gstatic.com/s/cinzeldecorative/v17/daaHSScvJGqLYhG8nNt8KPPswUAPniZoaelD.ttf) format('truetype');
|
src: url(https://fonts.gstatic.com/s/cinzeldecorative/v18/daaHSScvJGqLYhG8nNt8KPPswUAPniZoaelD.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Montserrat';
|
font-family: 'Montserrat';
|
||||||
|
|
@ -3186,6 +3437,19 @@ div.daggerheart.views.multiclass {
|
||||||
.application.setting.dh-style footer button {
|
.application.setting.dh-style footer button {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
.application.setting.dh-style .form-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.application.setting.dh-style .form-group label {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.application.setting.dh-style .form-group .form-fields {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
.system-daggerheart .tagify {
|
.system-daggerheart .tagify {
|
||||||
background: light-dark(transparent, transparent);
|
background: light-dark(transparent, transparent);
|
||||||
border: 1px solid light-dark(#222, #efe6d8);
|
border: 1px solid light-dark(#222, #efe6d8);
|
||||||
|
|
@ -3228,6 +3492,93 @@ div.daggerheart.views.multiclass {
|
||||||
.system-daggerheart.theme-light .tagify__dropdown .tagify__dropdown__item--active {
|
.system-daggerheart.theme-light .tagify__dropdown .tagify__dropdown__item--active {
|
||||||
color: #efe6d8;
|
color: #efe6d8;
|
||||||
}
|
}
|
||||||
|
.theme-light .application .component.dh-style.card-preview-container {
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||||
|
}
|
||||||
|
.theme-light .application .component.dh-style.card-preview-container .preview-text-container {
|
||||||
|
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
||||||
|
}
|
||||||
|
.theme-light .application .component.dh-style.card-preview-container .preview-selected-icon-container {
|
||||||
|
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
||||||
|
color: var(--color-light-5);
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 2px solid var(--color-tabs-border);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
aspect-ratio: 0.75;
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container.selectable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container .preview-image-outer-container {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container .preview-image-container {
|
||||||
|
flex: 1;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container .preview-text-container {
|
||||||
|
flex: 1;
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 18px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--color-text-selection-bg);
|
||||||
|
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container .preview-empty-container {
|
||||||
|
pointer-events: none;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container .preview-empty-container .preview-empty-inner-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container .preview-empty-container .preview-empty-inner-container .preview-add-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container .preview-empty-container .preview-empty-inner-container .preview-empty-subtext {
|
||||||
|
position: absolute;
|
||||||
|
top: 10%;
|
||||||
|
font-size: 18px;
|
||||||
|
font-variant: small-caps;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container .preview-selected-icon-container {
|
||||||
|
position: absolute;
|
||||||
|
height: 54px;
|
||||||
|
width: 54px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid;
|
||||||
|
font-size: 48px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||||
|
color: var(--color-dark-5);
|
||||||
|
}
|
||||||
|
.application .component.dh-style.card-preview-container .preview-selected-icon-container i {
|
||||||
|
position: relative;
|
||||||
|
right: 2px;
|
||||||
|
}
|
||||||
.sheet.daggerheart.dh-style .tab-navigation {
|
.sheet.daggerheart.dh-style .tab-navigation {
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
@import './application.less';
|
@import './application.less';
|
||||||
@import './sheets/sheets.less';
|
@import './sheets/sheets.less';
|
||||||
@import './dialog.less';
|
@import './dialog.less';
|
||||||
|
@import './levelup.less';
|
||||||
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
||||||
@import './resources.less';
|
@import './resources.less';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,22 @@
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-fields {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.system-daggerheart {
|
.system-daggerheart {
|
||||||
|
|
@ -272,3 +288,105 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.theme-light .application .component.dh-style.card-preview-container {
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||||
|
|
||||||
|
.preview-text-container {
|
||||||
|
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-selected-icon-container {
|
||||||
|
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
||||||
|
color: var(--color-light-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.application .component.dh-style.card-preview-container {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 2px solid var(--color-tabs-border);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
aspect-ratio: 0.75;
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
|
|
||||||
|
&.selectable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image-outer-container {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image-container {
|
||||||
|
flex: 1;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-text-container {
|
||||||
|
flex: 1;
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 18px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--color-text-selection-bg);
|
||||||
|
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-empty-container {
|
||||||
|
pointer-events: none;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.preview-empty-inner-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.preview-add-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-empty-subtext {
|
||||||
|
position: absolute;
|
||||||
|
top: 10%;
|
||||||
|
font-size: 18px;
|
||||||
|
font-variant: small-caps;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-selected-icon-container {
|
||||||
|
position: absolute;
|
||||||
|
height: 54px;
|
||||||
|
width: 54px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid;
|
||||||
|
font-size: 48px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||||
|
color: var(--color-dark-5);
|
||||||
|
|
||||||
|
i {
|
||||||
|
position: relative;
|
||||||
|
right: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,51 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.sheet.daggerheart.dh-style.item {
|
.sheet.daggerheart.dh-style.item {
|
||||||
.tab.features {
|
.tab.features {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
max-height: 265px;
|
max-height: 265px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
.feature-list {
|
.feature-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
.feature-item {
|
.feature-item {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
.feature-line {
|
.feature-line {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: 1fr 4fr 1fr;
|
grid-template-columns: 1fr 4fr 1fr;
|
||||||
h4 {
|
h4 {
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
color: light-dark(@dark, @beige);
|
color: light-dark(@dark, @beige);
|
||||||
}
|
}
|
||||||
.image {
|
.image {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
.controls {
|
.controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
a {
|
a {
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,152 +1,152 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.dh-style {
|
.application.sheet.daggerheart.dh-style {
|
||||||
.item-sheet-header {
|
.item-sheet-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
.profile {
|
.profile {
|
||||||
height: 150px;
|
height: 150px;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-right: 1px solid light-dark(@dark-blue, @golden);
|
border-right: 1px solid light-dark(@dark-blue, @golden);
|
||||||
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-info {
|
.item-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
margin-top: 36px;
|
margin-top: 36px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
|
|
||||||
.item-name input[type='text'] {
|
.item-name input[type='text'] {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
outline: 2px solid transparent;
|
outline: 2px solid transparent;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
|
||||||
&:hover[type='text'],
|
&:hover[type='text'],
|
||||||
&:focus[type='text'] {
|
&:focus[type='text'] {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
outline: 2px solid light-dark(@dark-blue, @golden);
|
outline: 2px solid light-dark(@dark-blue, @golden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-description {
|
.item-description {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-card-header {
|
.item-card-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: start;
|
justify-content: start;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
.profile {
|
.profile {
|
||||||
height: 300px;
|
height: 300px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
mask-image: linear-gradient(0deg, transparent 0%, black 10%);
|
mask-image: linear-gradient(0deg, transparent 0%, black 10%);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-icons-list {
|
.item-icons-list {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
|
|
||||||
.item-icon {
|
.item-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
max-width: 50px;
|
max-width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
background: light-dark(@light-black, @semi-transparent-dark-blue);
|
background: light-dark(@light-black, @semi-transparent-dark-blue);
|
||||||
border: 4px double light-dark(@beige, @golden);
|
border: 4px double light-dark(@beige, @golden);
|
||||||
color: light-dark(@beige, @golden);
|
color: light-dark(@beige, @golden);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
.recall-label {
|
.recall-label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
margin-right: 0.3rem;
|
margin-right: 0.3rem;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
border-radius: 60px;
|
border-radius: 60px;
|
||||||
|
|
||||||
.recall-label {
|
.recall-label {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-info {
|
.item-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -25px;
|
top: -25px;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
margin-bottom: -20px;
|
margin-bottom: -20px;
|
||||||
|
|
||||||
.item-name {
|
.item-name {
|
||||||
input[type='text'] {
|
input[type='text'] {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
outline: 2px solid transparent;
|
outline: 2px solid transparent;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
|
||||||
&:hover[type='text'],
|
&:hover[type='text'],
|
||||||
&:focus[type='text'] {
|
&:focus[type='text'] {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
outline: 2px solid light-dark(@dark-blue, @golden);
|
outline: 2px solid light-dark(@dark-blue, @golden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-description {
|
.item-description {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,80 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.dh-style .window-header {
|
.application.sheet.dh-style .window-header {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
color: light-dark(@dark-blue, @beige);
|
color: light-dark(@dark-blue, @beige);
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background: light-dark(transparent, @deep-black);
|
background: light-dark(transparent, @deep-black);
|
||||||
color: light-dark(@dark-blue, @beige);
|
color: light-dark(@dark-blue, @beige);
|
||||||
border: 1px solid light-dark(@dark-blue, transparent);
|
border: 1px solid light-dark(@dark-blue, transparent);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border: 1px solid light-dark(@dark-blue, @golden);
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
color: light-dark(@dark-blue, @golden);
|
color: light-dark(@dark-blue, @golden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.application.sheet.dh-style:not(.minimized) {
|
.application.sheet.dh-style:not(.minimized) {
|
||||||
.window-title,
|
.window-title,
|
||||||
.window-icon {
|
.window-icon {
|
||||||
display: none;
|
display: none;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.application.sheet.dh-style.minimized {
|
.application.sheet.dh-style.minimized {
|
||||||
.window-content {
|
.window-content {
|
||||||
display: none;
|
display: none;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.1s ease;
|
transition: opacity 0.1s ease;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.application.sheet.dh-style:not(.minimized) {
|
.application.sheet.dh-style:not(.minimized) {
|
||||||
.window-content {
|
.window-content {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.application.sheet.dh-style .window-content {
|
.application.sheet.dh-style .window-content {
|
||||||
overflow: initial;
|
overflow: initial;
|
||||||
backdrop-filter: none;
|
backdrop-filter: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-dark {
|
.theme-dark {
|
||||||
.application.sheet.dh-style {
|
.application.sheet.dh-style {
|
||||||
backdrop-filter: blur(4px);
|
backdrop-filter: blur(4px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-light {
|
.theme-light {
|
||||||
.application.sheet.dh-style {
|
.application.sheet.dh-style {
|
||||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.application.sheet.daggerheart.dh-style {
|
.application.sheet.daggerheart.dh-style {
|
||||||
.window-content {
|
.window-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -36px;
|
top: -36px;
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,46 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.sheet.daggerheart.dh-style {
|
.sheet.daggerheart.dh-style {
|
||||||
.tab.actions {
|
.tab.actions {
|
||||||
.actions-list {
|
.actions-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
|
||||||
.action-item {
|
.action-item {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: 1fr 4fr 1fr;
|
grid-template-columns: 1fr 4fr 1fr;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
color: @beige;
|
color: @beige;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
a {
|
a {
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,131 +1,132 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Cinzel';
|
font-family: 'Cinzel';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(https://fonts.gstatic.com/s/cinzel/v23/8vIU7ww63mVu7gtR-kwKxNvkNOjw-tbnTYo.ttf) format('truetype');
|
src: url(https://fonts.gstatic.com/s/cinzel/v23/8vIU7ww63mVu7gtR-kwKxNvkNOjw-tbnTYo.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Cinzel';
|
font-family: 'Cinzel';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(https://fonts.gstatic.com/s/cinzel/v23/8vIU7ww63mVu7gtR-kwKxNvkNOjw-jHgTYo.ttf) format('truetype');
|
src: url(https://fonts.gstatic.com/s/cinzel/v23/8vIU7ww63mVu7gtR-kwKxNvkNOjw-jHgTYo.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Cinzel Decorative';
|
font-family: 'Cinzel Decorative';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(https://fonts.gstatic.com/s/cinzeldecorative/v17/daaHSScvJGqLYhG8nNt8KPPswUAPniZoaelD.ttf) format('truetype');
|
src: url(https://fonts.gstatic.com/s/cinzeldecorative/v17/daaHSScvJGqLYhG8nNt8KPPswUAPniZoaelD.ttf)
|
||||||
|
format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Montserrat';
|
font-family: 'Montserrat';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(https://fonts.gstatic.com/s/montserrat/v30/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Ew-.ttf) format('truetype');
|
src: url(https://fonts.gstatic.com/s/montserrat/v30/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Ew-.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Montserrat';
|
font-family: 'Montserrat';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(https://fonts.gstatic.com/s/montserrat/v30/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCu170w-.ttf) format('truetype');
|
src: url(https://fonts.gstatic.com/s/montserrat/v30/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCu170w-.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style h1 {
|
.application.sheet.daggerheart.dh-style h1 {
|
||||||
font-family: 'Cinzel Decorative', serif;
|
font-family: 'Cinzel Decorative', serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: none;
|
border: none;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style h2,
|
.application.sheet.daggerheart.dh-style h2,
|
||||||
.application.sheet.daggerheart.dh-style h3 {
|
.application.sheet.daggerheart.dh-style h3 {
|
||||||
font-family: 'Cinzel', serif;
|
font-family: 'Cinzel', serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: none;
|
border: none;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style h4 {
|
.application.sheet.daggerheart.dh-style h4 {
|
||||||
font-family: 'Montserrat', sans-serif;
|
font-family: 'Montserrat', sans-serif;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
border: none;
|
border: none;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
color: #f3c267;
|
color: #f3c267;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style h5 {
|
.application.sheet.daggerheart.dh-style h5 {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #f3c267;
|
color: #f3c267;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style p,
|
.application.sheet.daggerheart.dh-style p,
|
||||||
.application.sheet.daggerheart.dh-style span {
|
.application.sheet.daggerheart.dh-style span {
|
||||||
font-family: 'Montserrat', sans-serif;
|
font-family: 'Montserrat', sans-serif;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style small {
|
.application.sheet.daggerheart.dh-style small {
|
||||||
font-family: 'Montserrat', sans-serif;
|
font-family: 'Montserrat', sans-serif;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tagify {
|
.application.sheet.daggerheart.dh-style.class .tagify {
|
||||||
background: light-dark(transparent, transparent);
|
background: light-dark(transparent, transparent);
|
||||||
border: 1px solid light-dark(#222, #efe6d8);
|
border: 1px solid light-dark(#222, #efe6d8);
|
||||||
height: 34px;
|
height: 34px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
margin-right: 1px;
|
margin-right: 1px;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tagify tag div {
|
.application.sheet.daggerheart.dh-style.class .tagify tag div {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tagify tag div span {
|
.application.sheet.daggerheart.dh-style.class .tagify tag div span {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tagify tag div img {
|
.application.sheet.daggerheart.dh-style.class .tagify tag div img {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tab.settings .fieldsets-section {
|
.application.sheet.daggerheart.dh-style.class .tab.settings .fieldsets-section {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
grid-template-columns: 1fr 1.5fr 1.5fr;
|
grid-template-columns: 1fr 1.5fr 1.5fr;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items {
|
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items:last-child {
|
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items:last-child {
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items .item-line {
|
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items .item-line {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
grid-template-columns: 1fr 3fr 1fr;
|
grid-template-columns: 1fr 3fr 1fr;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items .item-line h4 {
|
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items .item-line h4 {
|
||||||
font-family: 'Montserrat', sans-serif;
|
font-family: 'Montserrat', sans-serif;
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
color: light-dark(#222, #efe6d8);
|
color: light-dark(#222, #efe6d8);
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items .item-line .image {
|
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items .item-line .image {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items .item-line .controls {
|
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items .item-line .controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items .item-line .controls a {
|
.application.sheet.daggerheart.dh-style.class .tab.settings .list-items .item-line .controls a {
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.dh-style.domain-card {
|
.application.sheet.daggerheart.dh-style.domain-card {
|
||||||
|
section.tab {
|
||||||
section.tab {
|
height: 400px;
|
||||||
height: 400px;
|
overflow-y: auto;
|
||||||
overflow-y: auto;
|
scrollbar-width: thin;
|
||||||
scrollbar-width: thin;
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.dh-style.feature {
|
.application.sheet.daggerheart.dh-style.feature {
|
||||||
.item-sheet-header {
|
.item-sheet-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
.profile {
|
.profile {
|
||||||
height: 130px;
|
height: 130px;
|
||||||
width: 130px;
|
width: 130px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section.tab {
|
section.tab {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
261
styles/levelup.less
Normal file
261
styles/levelup.less
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
.theme-light {
|
||||||
|
.daggerheart.levelup {
|
||||||
|
.tiers-container {
|
||||||
|
.tier-container {
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.daggerheart.levelup {
|
||||||
|
.window-content {
|
||||||
|
max-height: 960px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[data-application-part='form'] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
.section-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-navigation-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 22px;
|
||||||
|
height: 36px;
|
||||||
|
|
||||||
|
nav {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.levelup-tab-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-navigation-actions {
|
||||||
|
width: 306px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
gap: 16px;
|
||||||
|
margin-right: 4px;
|
||||||
|
|
||||||
|
* {
|
||||||
|
width: calc(50% - 8px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiers-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.tier-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
|
|
||||||
|
&.inactive {
|
||||||
|
opacity: 0.4;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 3fr;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.checkboxes-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.checkbox-grouping-coontainer {
|
||||||
|
display: flex;
|
||||||
|
height: min-content;
|
||||||
|
|
||||||
|
&.multi {
|
||||||
|
border: 2px solid grey;
|
||||||
|
padding: 2.4px 2.5px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.selection-checkbox {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selection-checkbox {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-selections-container {
|
||||||
|
.achievement-experience-cards {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.achievement-experience-card {
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
font-size: 18px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.achievement-experience-marker {
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-card-selection {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 40px;
|
||||||
|
|
||||||
|
.card-preview-container {
|
||||||
|
width: calc(100% * (1 / 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-domains-selection-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.levelup-domain-selection-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-domain-label {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
top: 4px;
|
||||||
|
background: grey;
|
||||||
|
padding: 0 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 124px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-domain-selected {
|
||||||
|
position: absolute;
|
||||||
|
height: 54px;
|
||||||
|
width: 54px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid;
|
||||||
|
font-size: 48px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||||
|
color: var(--color-dark-5);
|
||||||
|
top: calc(50% - 29px);
|
||||||
|
|
||||||
|
i {
|
||||||
|
position: relative;
|
||||||
|
right: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-selections-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-summary-container {
|
||||||
|
.level-achievements-container,
|
||||||
|
.level-advancements-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.increase-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-selection-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.summary-selection {
|
||||||
|
border: 2px solid;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 4px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-footer {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,41 +1,46 @@
|
||||||
:root {
|
:root {
|
||||||
--primary-color-fear: rgba(9, 71, 179, .75);
|
|
||||||
--secondary-color-fear: rgba(9, 71, 179, .75);
|
|
||||||
--shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
--shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
||||||
|
--fear-animation: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease, opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
#resources {
|
#resources {
|
||||||
min-height: calc(var(--header-height) + 4rem);
|
min-height: calc(var(--header-height) + 4rem);
|
||||||
min-width: 4rem;
|
min-width: 4rem;
|
||||||
color: #d3d3d3;
|
color: #d3d3d3;
|
||||||
|
transition: var(--fear-animation);
|
||||||
|
header,
|
||||||
|
.controls,
|
||||||
|
.window-resize-handle {
|
||||||
|
transition: var(--fear-animation);
|
||||||
|
}
|
||||||
.window-content {
|
.window-content {
|
||||||
padding: .5rem;
|
padding: 0.5rem;
|
||||||
#resource-fear {
|
#resource-fear {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: .5rem 0.25rem;
|
gap: 0.5rem 0.25rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
i {
|
i {
|
||||||
font-size: var(--font-size-18);
|
font-size: var(--font-size-18);
|
||||||
// flex: 1 1 calc(25% - 0.25rem);
|
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||||
border: 1px solid rgba(0,0,0,.5);
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 3rem;
|
width: 3rem;
|
||||||
background-color: var(--primary-color-fear);
|
background-color: var(@primary-color-fear);
|
||||||
-webkit-box-shadow: 0px 0px 5px 1px rgba(0,0,0,.75);
|
-webkit-box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.75);
|
||||||
box-shadow: 0px 0px 5px 1px rgba(0,0,0,.75);
|
box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.75);
|
||||||
color: #d3d3d3;
|
color: #d3d3d3;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
&.inactive{
|
&.inactive {
|
||||||
filter: grayscale(1) !important;
|
filter: grayscale(1) !important;
|
||||||
opacity: .5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.controls, .resource-bar {
|
.controls,
|
||||||
|
.resource-bar {
|
||||||
border: 2px solid rgb(153 122 79);
|
border: 2px solid rgb(153 122 79);
|
||||||
background-color: rgb(24 22 46);
|
background-color: rgb(24 22 46);
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +58,7 @@
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
&.disabled {
|
&.disabled {
|
||||||
opacity: .5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.resource-bar {
|
.resource-bar {
|
||||||
|
|
@ -63,7 +68,7 @@
|
||||||
font-size: var(--font-size-20);
|
font-size: var(--font-size-20);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: .25rem .5rem;
|
padding: 0.25rem 0.5rem;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
text-shadow: var(--shadow-text-stroke);
|
text-shadow: var(--shadow-text-stroke);
|
||||||
&:before {
|
&:before {
|
||||||
|
|
@ -74,7 +79,7 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
width: var(--fear-percent);
|
width: var(--fear-percent);
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
background: linear-gradient(90deg,rgba(2, 0, 38, 1) 0%, rgba(199, 1, 252, 1) 100%);
|
background: linear-gradient(90deg, rgba(2, 0, 38, 1) 0%, rgba(199, 1, 252, 1) 100%);
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +88,6 @@
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
&.fear {
|
&.fear {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.isGM {
|
&.isGM {
|
||||||
|
|
@ -96,18 +100,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button[data-action="close"] {
|
button[data-action='close'] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
&:not(:hover):not(.minimized) {
|
&:not(:hover):not(.minimized) {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: unset;
|
box-shadow: unset;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
header, .controls, .window-resize-handle {
|
header,
|
||||||
visibility: hidden;
|
.controls,
|
||||||
|
.window-resize-handle {
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:has(.fear-bar) {
|
&:has(.fear-bar) {
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
137
styles/ui.less
137
styles/ui.less
|
|
@ -1,71 +1,106 @@
|
||||||
.combat-sidebar {
|
.combat-sidebar {
|
||||||
.encounter-gm-resources {
|
.encounter-controls.combat {
|
||||||
flex: 0;
|
justify-content: space-between;
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
padding: @largePadding 0;
|
|
||||||
|
|
||||||
.gm-resource-controls {
|
.encounter-control-fear-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
position: relative;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 4px;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
color: black;
|
||||||
|
|
||||||
.gm-resource-tools {
|
.dice {
|
||||||
display: flex;
|
height: 24px;
|
||||||
flex-direction: column;
|
}
|
||||||
justify-content: center;
|
|
||||||
padding: 0 5px 0 @fullPadding;
|
|
||||||
|
|
||||||
i {
|
.encounter-control-fear {
|
||||||
margin: 0 @tinyMargin;
|
position: absolute;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
&.disabled {
|
.encounter-control-counter {
|
||||||
opacity: 0.6;
|
position: absolute;
|
||||||
}
|
right: -10px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover:not(.disabled) {
|
.control-buttons {
|
||||||
cursor: pointer;
|
width: min-content;
|
||||||
filter: drop-shadow(0 0 3px @mainShadow);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.combatant-controls {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-actions {
|
||||||
|
align-self: stretch;
|
||||||
|
display: flex;
|
||||||
|
align-items: top;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.action-tokens {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.action-token {
|
||||||
|
height: 22px;
|
||||||
|
width: 22px;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 8px;
|
||||||
|
--button-size: 0;
|
||||||
|
|
||||||
|
&.used {
|
||||||
|
opacity: 0.5;
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.gm-resource {
|
button {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
font-size: 22px;
|
||||||
padding: @fullPadding;
|
height: 24px;
|
||||||
border-radius: 8px;
|
width: 24px;
|
||||||
border: @normalBorder solid black;
|
|
||||||
font-size: 20px;
|
&.main {
|
||||||
|
background: var(--button-hover-background-color);
|
||||||
|
color: var(--button-hover-text-color);
|
||||||
|
border-color: var(--button-hover-border-color);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: drop-shadow(0 0 3px var(--button-hover-text-color));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.token-action-tokens {
|
.spotlight-control {
|
||||||
flex: 0 0 48px;
|
font-size: 26px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.discrete:hover {
|
||||||
|
background: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.requesting {
|
||||||
|
filter: drop-shadow(0 0 3px gold);
|
||||||
|
color: var(--button-hover-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
.use-action-token {
|
|
||||||
&.disabled {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-button {
|
|
||||||
&.spaced {
|
|
||||||
margin-left: @halfMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover:not(.disabled) {
|
|
||||||
cursor: pointer;
|
|
||||||
filter: drop-shadow(0 0 3px @mainShadow);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,6 @@
|
||||||
@criticalAccent: #66159c;
|
@criticalAccent: #66159c;
|
||||||
@criticalBackgroundStart: rgba(37, 8, 37, 0.6);
|
@criticalBackgroundStart: rgba(37, 8, 37, 0.6);
|
||||||
@criticalBackgroundEnd: rgba(128, 0, 128, 0.6);
|
@criticalBackgroundEnd: rgba(128, 0, 128, 0.6);
|
||||||
|
|
||||||
|
/* Fear */
|
||||||
|
@primary-color-fear: rgba(9, 71, 179, 0.75);
|
||||||
|
|
|
||||||
13
templates/components/card-preview.hbs
Normal file
13
templates/components/card-preview.hbs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<div class="component dh-style card-preview-container {{#if (and this.compendium (not this.img))}}selectable{{/if}}" data-action="viewCompendium" data-compendium="{{this.compendium}}" data-path="{{this.path}}" data-limit="{{this.limit}}">
|
||||||
|
{{#if this.img}}
|
||||||
|
<img class="preview-image-container" src="{{this.img}}" />
|
||||||
|
<div class="preview-text-container">{{this.name}}</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="preview-empty-container">
|
||||||
|
<div class="preview-empty-inner-container">
|
||||||
|
<i class="preview-add-icon fa-solid fa-plus"></i>
|
||||||
|
<div class="preview-empty-subtext">{{this.emptySubtext}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
22
templates/settings/variant-rules.hbs
Normal file
22
templates/settings/variant-rules.hbs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "DAGGERHEART.Settings.Menu.VariantRules.actionTokens"}}</label>
|
||||||
|
|
||||||
|
<div class="form-fields">
|
||||||
|
{{formInput settingFields.schema.fields.actionTokens.fields.enabled value=settingFields._source.actionTokens.enabled}}
|
||||||
|
{{formInput settingFields.schema.fields.actionTokens.fields.tokens value=settingFields._source.actionTokens.tokens disabled=(not settingFields._source.actionTokens.enabled)}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="form-footer">
|
||||||
|
<button data-action="reset">
|
||||||
|
<i class="fa-solid fa-arrow-rotate-left"></i>
|
||||||
|
<span>{{localize "Reset"}}</span>
|
||||||
|
</button>
|
||||||
|
<button data-action="save" >
|
||||||
|
<i class="fa-solid fa-floppy-disk"></i>
|
||||||
|
<span>{{localize "Save Changes"}}</span>
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{localize "DAGGERHEART.Sheets.Adversary.HP"}}</label>
|
<label>{{localize "DAGGERHEART.Sheets.Adversary.HP"}}</label>
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
<input type="text" name="system.resources.health.max" value="{{source.system.resources.health.max}}" data-dtype="Number" />
|
<input type="text" name="system.resources.hitPoints.max" value="{{source.system.resources.hitPoints.max}}" data-dtype="Number" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,19 @@
|
||||||
{{#each this.attributes as |attribute key|}}
|
{{#each this.attributes as |attribute key|}}
|
||||||
<div class="attribute">
|
<div class="attribute">
|
||||||
<div class="attribute-banner">
|
<div class="attribute-banner">
|
||||||
<img class="attribute-roll" data-action="attributeRoll" data-attribute="{{key}}" data-value="{{attribute.data.value}}" src="icons/svg/d12-grey.svg" />
|
<img class="attribute-roll" data-action="attributeRoll" data-attribute="{{key}}" data-value="{{attribute.value}}" src="icons/svg/d12-grey.svg" />
|
||||||
<div class="attribute-text">{{key}}</div>
|
<div class="attribute-text">{{key}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="attribute-mark {{#if (and (not attribute.levelMark) (and (not (includes attribute.levelMarks ../document.system.levelData.currentLevel)) (gt ../document.system.availableAttributeMarks.length 0)))}}selectable{{/if}}" data-action="toggleAttributeMark" data-attribute="{{key}}">
|
|
||||||
<i class="fa-solid fa-check {{#if attribute.levelMark}}selected{{/if}}"></i>
|
|
||||||
</div>
|
|
||||||
<div class="attribute-image">
|
<div class="attribute-image">
|
||||||
{{#if ../editAttributes}}
|
{{#if ../editAttributes}}
|
||||||
<select class="attribute-value{{#if (lt attribute.data.base 0)}} negative{{/if}}{{#if (and (not attribute.data.base) (not ../abilityScoresFinished))}} unselected{{/if}}" data-attribute="{{key}}">
|
<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.data.base 0))}}<option value="">{{attribute.data.base}}</option>{{/if}}
|
{{#if (not (eq attribute.base 0))}}<option value="">{{attribute.base}}</option>{{/if}}
|
||||||
{{#each ../abilityScoreArray as |option|}}
|
{{#each ../abilityScoreArray as |option|}}
|
||||||
<option value="{{option.value}}"{{#if (eq option.value attribute.data.base)}} selected="selected"{{/if}}>{{option.name}}</option>
|
<option value="{{option.value}}"{{#if (eq option.value attribute.base)}} selected="selected"{{/if}}>{{option.name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="attribute-text {{#if (lt attribute.data.value 0)}}negative{{/if}}">{{attribute.data.value}}</div>
|
<div class="attribute-text {{#if (lt attribute.value 0)}}negative{{/if}}">{{attribute.value}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<div class="defense-row">
|
<div class="defense-row">
|
||||||
<div class="defense-section">
|
<div class="defense-section">
|
||||||
<div class="defense-container">
|
<div class="defense-container">
|
||||||
<div class="defense-value">{{document.system.evasion}}</div>
|
<div class="defense-value">{{document.system.evasion.value}}</div>
|
||||||
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
||||||
<div class="defense-banner">{{localize "DAGGERHEART.Sheets.PC.Defense.Evasion"}}</div>
|
<div class="defense-banner">{{localize "DAGGERHEART.Sheets.PC.Defense.Evasion"}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
<i class="fa-solid fa-caret-left"></i>
|
<i class="fa-solid fa-caret-left"></i>
|
||||||
<div class="health-category">{{localize "DAGGERHEART.Sheets.PC.Health.Severe"}}</div>
|
<div class="health-category">{{localize "DAGGERHEART.Sheets.PC.Health.Severe"}}</div>
|
||||||
</div>
|
</div>
|
||||||
<i data-action="makeDeathMove" class="fas fa-skull death-save {{#if (lt resources.health.value document.system.resources.health.max)}}disabled{{/if}}" title="{{localize "DAGGERHEART.Sheets.PC.Health.DeathMoveTooltip"}}"></i>
|
<i data-action="makeDeathMove" class="fas fa-skull death-save {{#if (lt resources.hitPoints.value document.system.resources.hitPoints.max)}}disabled{{/if}}" title="{{localize "DAGGERHEART.Sheets.PC.Health.DeathMoveTooltip"}}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="flexrow" style="flex-wrap: nowrap; align-items: center;">
|
<div class="flexrow" style="flex-wrap: nowrap; align-items: center;">
|
||||||
<div class="flexcol flex0">
|
<div class="flexcol flex0">
|
||||||
|
|
@ -30,12 +30,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="flexcol">
|
<div class="flexcol">
|
||||||
<div class="flexrow" style="flex-wrap: nowrap;">
|
<div class="flexrow" style="flex-wrap: nowrap;">
|
||||||
{{#times document.system.resources.health.max}}
|
{{#times document.system.resources.hitPoints.max}}
|
||||||
{{#with (add this 1)}}
|
{{#with (add this 1)}}
|
||||||
<input class="resource-box" type="checkbox" data-action="toggleHP" data-value="{{this}}" {{ checked (gte ../../document.system.resources.health.value this) }} />
|
<input class="resource-box" type="checkbox" data-action="toggleHP" data-value="{{this}}" {{ checked (gte ../../document.system.resources.hitPoints.value this) }} />
|
||||||
{{/with}}
|
{{/with}}
|
||||||
{{/times}}
|
{{/times}}
|
||||||
{{#times (subtract 12 document.system.resources.health.max)}}
|
{{#times (subtract 12 document.system.resources.hitPoints.max)}}
|
||||||
<input class="resource-box disabled" type="checkbox" disabled />
|
<input class="resource-box disabled" type="checkbox" disabled />
|
||||||
{{/times}}
|
{{/times}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -41,13 +41,13 @@
|
||||||
<button data-action="takeLongRest" title="{{localize "DAGGERHEART.Sheets.PC.LongRest"}}"><i class="fa-solid fa-bed"></i></button>
|
<button data-action="takeLongRest" title="{{localize "DAGGERHEART.Sheets.PC.LongRest"}}"><i class="fa-solid fa-bed"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-container {{#if document.system.canLevelUp}}levelup{{/if}}">
|
<div class="level-container {{#if document.system.levelData.canLevelUp}}levelup{{/if}}">
|
||||||
<div class="level-value-container">
|
<div class="level-value-container">
|
||||||
<input class="level-value {{#if document.system.canLevelUp}}levelup{{/if}}" name="system.levelData.changedLevel" value="{{document.system.levelData.changedLevel}}" type="text" data-dtype="Number" />
|
<input class="level-value {{#if document.system.levelData.canLevelUp}}levelup{{/if}}" value="{{document.system.levelData.level.changed}}" type="text" data-dtype="Number" />
|
||||||
{{#if document.system.canLevelUp}}<div class="levelup-marker {{#if (gte document.system.levels.current 10)}}double-digit{{/if}}">*</div>{{/if}}
|
{{#if document.system.levelData.canLevelUp}}<div class="levelup-marker">*</div>{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
||||||
<div class="level-title {{#if document.system.canLevelUp}}levelup{{/if}}">{{localize "DAGGERHEART.Sheets.PC.Level"}}</div>
|
<div class="level-title {{#if document.system.levelData.canLevelUp}}levelup{{/if}}">{{localize "DAGGERHEART.Sheets.PC.Level"}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
|
|
|
||||||
8
templates/ui/combat/combatTracker.hbs
Normal file
8
templates/ui/combat/combatTracker.hbs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<div class="combat-tracker">
|
||||||
|
{{#if (gt this.characters.length 0)}}
|
||||||
|
{{> 'systems/daggerheart/templates/ui/combat/combatTrackerSection.hbs' this title=(localize "DAGGERHEART.General.Character.Plural") turns=this.characters}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if (gt this.adversaries.length 0)}}
|
||||||
|
{{> 'systems/daggerheart/templates/ui/combat/combatTrackerSection.hbs' this title=(localize "DAGGERHEART.General.Adversary.Plural") turns=this.adversaries}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
17
templates/ui/combat/combatTrackerFooter.hbs
Normal file
17
templates/ui/combat/combatTrackerFooter.hbs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<nav class="combat-controls" data-tooltip-direction="UP">
|
||||||
|
{{~#if hasCombat~}}
|
||||||
|
{{#if user.isGM}}
|
||||||
|
{{#if combat.round}}
|
||||||
|
<button type="button" class="combat-control combat-control-lg" data-action="endCombat">
|
||||||
|
<i class="fa-solid fa-xmark" inert></i>
|
||||||
|
<span>{{ localize "COMBAT.End" }}</span>
|
||||||
|
</button>
|
||||||
|
{{else}}
|
||||||
|
<button type="button" class="combat-control combat-control-lg" data-action="startCombat">
|
||||||
|
<i class="fa-solid fa-swords" inert></i>
|
||||||
|
<span>{{ localize "COMBAT.Begin" }}</span>
|
||||||
|
</button>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</nav>
|
||||||
86
templates/ui/combat/combatTrackerHeader.hbs
Normal file
86
templates/ui/combat/combatTrackerHeader.hbs
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
<header class="combat-tracker-header">
|
||||||
|
|
||||||
|
{{!-- Encounter Controls --}}
|
||||||
|
{{#if user.isGM}}
|
||||||
|
<nav class="encounters {{ css }}" aria-label="{{ localize "COMBAT.NavLabel" }}">
|
||||||
|
|
||||||
|
{{!-- Cycle Display --}}
|
||||||
|
{{#if displayCycle}}
|
||||||
|
<button type="button" class="inline-control icon fa-solid fa-plus" data-action="createCombat"
|
||||||
|
data-tooltip aria-label="{{ localize "COMBAT.Create" }}"></button>
|
||||||
|
|
||||||
|
<div class="cycle-combats">
|
||||||
|
<button type="button" class="inline-control icon fa-solid fa-caret-left" data-action="cycleCombat"
|
||||||
|
{{#if previousId}}data-combat-id="{{ previousId }}" {{else}}disabled{{/if}}
|
||||||
|
data-tooltip aria-label="{{ localize "COMBAT.EncounterPrevious" }}"></button>
|
||||||
|
<div class="encounter-count">
|
||||||
|
<span class="value">{{ currentIndex }}</span>
|
||||||
|
<span class="separator">/</span>
|
||||||
|
<span class="max">{{ combats.length }}</span>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="inline-control icon fa-solid fa-caret-right" data-action="cycleCombat"
|
||||||
|
{{#if nextId}}data-combat-id="{{ nextId }}" {{else}}disabled{{/if}}
|
||||||
|
data-tooltip aria-label="{{ localize "COMBAT.EncounterNext" }}"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" class="inline-control icon fa-solid fa-gear" data-action="trackerSettings"
|
||||||
|
data-tooltip aria-label="{{ localize "COMBAT.Settings" }}"></button>
|
||||||
|
|
||||||
|
{{!-- Tabbed Display --}}
|
||||||
|
{{else if combats.length}}
|
||||||
|
<button type="button" class="inline-control icon fa-solid fa-plus" data-action="createCombat"
|
||||||
|
data-tooltip aria-label="{{ localize "COMBAT.Create" }}"></button>
|
||||||
|
{{#each combats}}
|
||||||
|
<button type="button" class="inline-control {{#if active}}active{{/if}}" data-action="cycleCombat"
|
||||||
|
data-combat-id="{{ id }}">
|
||||||
|
{{ label }}
|
||||||
|
</button>
|
||||||
|
{{/each}}
|
||||||
|
<button type="button" class="inline-control icon fa-solid fa-gear" data-action="trackerSettings"
|
||||||
|
data-tooltip aria-label="{{ localize "COMBAT.Settings" }}"></button>
|
||||||
|
|
||||||
|
{{!-- No Combats --}}
|
||||||
|
{{else}}
|
||||||
|
<button type="button" class="combat-control-lg" data-action="createCombat">
|
||||||
|
<i class="fa-solid fa-plus" inert></i>
|
||||||
|
<span>{{ localize "COMBAT.Create" }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
|
</nav>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="encounter-controls {{#if hasCombat}}combat{{/if}}">
|
||||||
|
{{#if hasCombat}}
|
||||||
|
<div class="encounter-control-fear-container">
|
||||||
|
<img class="dice " src="../icons/svg/d12-grey.svg"/>
|
||||||
|
<i class="fas fa-skull encounter-control-fear"></i>
|
||||||
|
<div class="encounter-control-counter">{{fear}}</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Combat Status --}}
|
||||||
|
<strong class="encounter-title">
|
||||||
|
{{#if combats.length}}
|
||||||
|
{{#if combat.round}}
|
||||||
|
{{ localize "DAGGERHEART.Combat.combatStarted" }}
|
||||||
|
{{else}}
|
||||||
|
{{ localize "COMBAT.NotStarted" }}
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{ localize "COMBAT.None" }}
|
||||||
|
{{/if}}
|
||||||
|
</strong>
|
||||||
|
|
||||||
|
{{!-- Combat Controls --}}
|
||||||
|
{{#if hasCombat}}
|
||||||
|
<div class="control-buttons right flexrow">
|
||||||
|
<div class="spacer"></div>
|
||||||
|
<button type="button" class="encounter-context-menu inline-control combat-control icon fa-solid fa-ellipsis-vertical"
|
||||||
|
{{#unless (and user.isGM hasCombat)}}disabled{{/unless}}></button>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</header>
|
||||||
64
templates/ui/combat/combatTrackerSection.hbs
Normal file
64
templates/ui/combat/combatTrackerSection.hbs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
<div>
|
||||||
|
<h4 class="divider">{{title}}</h4>
|
||||||
|
<ol class="combat-tracker plain">
|
||||||
|
{{#each turns}}
|
||||||
|
<li class="combatant {{ css }}" data-combatant-id="{{ id }}" data-action="activateCombatant">
|
||||||
|
{{!-- Image --}}
|
||||||
|
<img class="token-image" src="{{ img }}" alt="{{ name }}" loading="lazy">
|
||||||
|
|
||||||
|
{{!-- Name & Controls --}}
|
||||||
|
<div class="token-name">
|
||||||
|
<strong class="name">{{ name }}</strong>
|
||||||
|
<div class="flexrow">
|
||||||
|
<div class="combatant-controls flex-0">
|
||||||
|
{{#if @root.user.isGM}}
|
||||||
|
<button type="button" class="inline-control combatant-control icon fa-solid fa-eye-slash {{#if hidden}}active{{/if}}"
|
||||||
|
data-action="toggleHidden" data-tooltip aria-label="{{ localize "COMBAT.ToggleVis" }}"></button>
|
||||||
|
<button type="button" class="inline-control combatant-control icon fa-solid fa-skull {{#if isDefeated}}active{{/if}}"
|
||||||
|
data-action="toggleDefeated" data-tooltip
|
||||||
|
aria-label="{{ localize "COMBAT.ToggleDead" }}"></button>
|
||||||
|
{{/if}}
|
||||||
|
{{#if canPing}}
|
||||||
|
<button type="button" class="inline-control combatant-control icon fa-solid fa-bullseye-arrow"
|
||||||
|
data-action="pingCombatant" data-tooltip
|
||||||
|
aria-label="{{ localize "COMBAT.PingCombatant" }}"></button>
|
||||||
|
{{/if}}
|
||||||
|
{{#unless @root.user.isGM}}
|
||||||
|
<button type="button" class="inline-control combatant-control icon fa-solid fa-arrows-to-eye"
|
||||||
|
data-action="panToCombatant" data-tooltip
|
||||||
|
aria-label="{{ localize "COMBAT.PanToCombatant" }}"></button>
|
||||||
|
{{/unless}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if ../combat.round}}
|
||||||
|
<div class="token-actions">
|
||||||
|
{{#if isOwner}}
|
||||||
|
{{#if (and (not isNPC) ../actionTokens.enabled)}}
|
||||||
|
<div class="action-tokens">
|
||||||
|
{{#times ../actionTokens.tokens}}
|
||||||
|
<button type="button" class="inline-control main icon fa-solid fa-bolt-lightning action-token {{#if (lte ../system/actionTokens this)}}used{{/if}}" data-action="setActionTokens" data-combatant-id="{{../id}}" data-token-index="{{this}}">
|
||||||
|
</button>
|
||||||
|
{{/times}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if @root.user.isGM}}
|
||||||
|
<button
|
||||||
|
type="button" class="inline-control spotlight-control discrete icon fa-solid {{#if system.spotlight.requesting}}fa-hand-sparkles requesting{{else}}fa-hand{{/if}}"
|
||||||
|
data-action="toggleSpotlight" data-combatant-id="{{id}}" data-tooltip aria-label="{{localize "DAGGERHEART.Combat.giveSpotlight"}}"
|
||||||
|
></button>
|
||||||
|
{{else}}
|
||||||
|
<button
|
||||||
|
type="button" class="inline-control spotlight-control discrete icon fa-solid {{#if system.spotlight.requesting}}fa-hand-sparkles requesting{{else}}fa-hand{{/if}}"
|
||||||
|
data-action="requestSpotlight" data-combatant-id="{{id}}" data-tooltip aria-label="{{#if system.spotlight.requesting}}{{localize "DAGGERHEART.Combat.requestingSpotlight"}}{{else}}{{localize "DAGGERHEART.Combat.requestSpotlight"}}{{/if}}"
|
||||||
|
></button>
|
||||||
|
{{/if}}
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
<section class="{{cssClass}} directory flexcol" id="{{cssId}}" data-tab="{{tabName}}">
|
|
||||||
<header class="combat-tracker-header">
|
|
||||||
{{#if user.isGM}}
|
|
||||||
<nav class="encounters flexrow" aria-label="{{localize 'COMBAT.NavLabel'}}">
|
|
||||||
<a class="combat-button combat-create" aria-label="{{localize 'COMBAT.Create'}}" role="button" data-tooltip="COMBAT.Create">
|
|
||||||
<i class="fas fa-plus"></i>
|
|
||||||
</a>
|
|
||||||
{{#if combatCount}}
|
|
||||||
<a class="combat-button combat-cycle" aria-label="{{localize 'COMBAT.EncounterPrevious'}}" role="button" data-tooltip="COMBAT.EncounterPrevious"
|
|
||||||
{{#if previousId}}data-document-id="{{previousId}}"{{else}}disabled{{/if}}>
|
|
||||||
<i class="fas fa-caret-left"></i>
|
|
||||||
</a>
|
|
||||||
<h4 class="encounter">{{localize "COMBAT.Encounter"}} {{currentIndex}} / {{combatCount}}</h4>
|
|
||||||
<a class="combat-button combat-cycle" aria-label="{{localize 'COMBAT.EncounterNext'}}" role="button" data-tooltip="COMBAT.EncounterNext"
|
|
||||||
{{#if nextId}}data-document-id="{{nextId}}"{{else}}disabled{{/if}}>
|
|
||||||
<i class="fas fa-caret-right"></i>
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
<a class="combat-button combat-control" aria-label="{{localize 'COMBAT.Delete'}}" role="button" data-tooltip="COMBAT.Delete" data-control="endCombat" {{#unless combatCount}}disabled{{/unless}}>
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
</nav>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="encounter-controls flexrow {{#if hasCombat}}combat{{/if}}">
|
|
||||||
{{#if user.isGM}}
|
|
||||||
<a class="combat-button combat-control" aria-label="{{localize 'COMBAT.RollAll'}}" role="button" data-tooltip="COMBAT.RollAll" data-control="rollAll" {{#unless turns}}disabled{{/unless}}>
|
|
||||||
<i class="fas fa-users"></i>
|
|
||||||
</a>
|
|
||||||
<a class="combat-button combat-control" aria-label="{{localize 'COMBAT.RollNPC'}}" role="button" data-tooltip="COMBAT.RollNPC" data-control="rollNPC" {{#unless turns}}disabled{{/unless}}>
|
|
||||||
<i class="fas fa-users-cog"></i>
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if combatCount}}
|
|
||||||
{{#if combat.round}}
|
|
||||||
<h3 class="encounter-title noborder">{{localize 'COMBAT.Round'}} {{combat.round}}</h3>
|
|
||||||
{{else}}
|
|
||||||
<h3 class="encounter-title noborder">{{localize 'COMBAT.NotStarted'}}</h3>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
<h3 class="encounter-title noborder">{{localize "COMBAT.None"}}</h3>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if user.isGM}}
|
|
||||||
<a class="combat-button combat-control" aria-label="{{localize 'COMBAT.InitiativeReset'}}" role="button" data-tooltip="COMBAT.InitiativeReset" data-control="resetAll"
|
|
||||||
{{#unless hasCombat}}disabled{{/unless}}>
|
|
||||||
<i class="fas fa-undo"></i>
|
|
||||||
</a>
|
|
||||||
<a class="combat-button combat-control" aria-label="{{localize 'labels.scope'}}" role="button" data-tooltip="{{labels.scope}}"
|
|
||||||
data-control="toggleSceneLink" {{#unless hasCombat}}disabled{{/unless}}>
|
|
||||||
<i class="fas fa-{{#unless linked}}un{{/unless}}link"></i>
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
<a class="combat-button combat-settings" aria-label="{{localize 'COMBAT.Settings'}}" role="button" data-tooltip="COMBAT.Settings" data-control="trackerSettings">
|
|
||||||
<i class="fas fa-cog"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="encounter-gm-resources">
|
|
||||||
{{#if combat.system}}
|
|
||||||
<div class="gm-resource-controls">
|
|
||||||
<i class="fa-solid fa-caret-up icon-button up" data-type="action"></i>
|
|
||||||
<i class="fa-solid fa-caret-down icon-button down {{#if (lte combat.system.actions 0)}}disabled{{/if}}" data-type="action"></i>
|
|
||||||
</div>
|
|
||||||
<div class="gm-resource">
|
|
||||||
<i class="fa-solid fa-hand-sparkles"></i>
|
|
||||||
<span>{{combat.system.actions}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="gm-resource-tools">
|
|
||||||
<i class="fa-solid fa-arrow-right-long trade-actions {{#if (lt combat.system.actions 2)}}disabled{{/if}}"></i>
|
|
||||||
<i class="fa-solid fa-arrow-left-long trade-fear {{#if (lt fear 1)}}disabled{{/if}}"></i>
|
|
||||||
</div>
|
|
||||||
<div class="gm-resource">
|
|
||||||
<i class="fa-solid fa-skull"></i>
|
|
||||||
<span>{{fear}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="gm-resource-controls">
|
|
||||||
<i class="fa-solid fa-caret-up icon-button up {{#if (gte fear 6)}}disabled{{/if}}" data-type="fear"></i>
|
|
||||||
<i class="fa-solid fa-caret-down icon-button down {{#if (lte fear 0)}}disabled{{/if}}" data-type="fear"></i>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ol id="combat-tracker" class="directory-list">
|
|
||||||
{{#each turns}}
|
|
||||||
<li class="combatant actor directory-item flexrow {{this.css}}" data-combatant-id="{{this.id}}">
|
|
||||||
<img class="token-image" data-src="{{this.img}}" alt="{{this.name}}"/>
|
|
||||||
<div class="token-name flexcol">
|
|
||||||
<h4>{{this.name}}</h4>
|
|
||||||
<div class="combatant-controls flexrow">
|
|
||||||
{{#if ../user.isGM}}
|
|
||||||
<a class="combatant-control {{#if this.hidden}}active{{/if}}" aria-label="{{localize 'COMBAT.ToggleVis'}}" role="button" data-tooltip="COMBAT.ToggleVis" data-control="toggleHidden">
|
|
||||||
<i class="fas fa-eye-slash"></i>
|
|
||||||
</a>
|
|
||||||
<a class="combatant-control {{#if this.defeated}}active{{/if}}" aria-label="{{localize 'COMBAT.ToggleDead'}}" role="button" data-tooltip="COMBAT.ToggleDead" data-control="toggleDefeated">
|
|
||||||
<i class="fas fa-skull"></i>
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
{{#if this.canPing}}
|
|
||||||
<a class="combatant-control" aria-label="{{localize 'COMBAT.PingCombatant'}}" role="button" data-tooltip="COMBAT.PingCombatant" data-control="pingCombatant">
|
|
||||||
<i class="fa-solid fa-bullseye-arrow"></i>
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
{{#unless ../user.isGM}}
|
|
||||||
<a class="combatant-control" aria-label="{{localize 'COMBAT.PanToCombatant'}}" role="button" data-tooltip="COMBAT.PanToCombatant" data-control="panToCombatant">
|
|
||||||
<i class="fa-solid fa-arrows-to-eye"></i>
|
|
||||||
</a>
|
|
||||||
{{/unless}}
|
|
||||||
<div class="token-effects">
|
|
||||||
{{#each this.effects}}
|
|
||||||
<img class="token-effect" src="{{this}}"/>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.hasResource}}
|
|
||||||
<div class="token-resource">
|
|
||||||
<span class="resource">{{this.resource}}</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="token-action-tokens">
|
|
||||||
{{#if this.playerCharacter}}
|
|
||||||
<i class="fa-solid fa-hand use-action-token" data-combatant="{{this.id}}"></i>
|
|
||||||
{{else if (and (not this.ownedByPlayer) ../user.isGM)}}
|
|
||||||
<i class="fa-solid fa-hand use-action-token {{#if (lt ../combat.system.actions 1)}}disabled{{/if}}" data-combatant="{{this.id}}"></i>
|
|
||||||
{{/if}}
|
|
||||||
{{!-- {{#if this.hasRolled}}
|
|
||||||
<span class="initiative">{{this.initiative}}</span>
|
|
||||||
{{else if this.owner}}
|
|
||||||
<a class="combatant-control roll" aria-label="{{localize 'COMBAT.InitiativeRoll'}}" role="button" data-tooltip="COMBAT.InitiativeRoll" data-control="rollInitiative"></a>
|
|
||||||
{{/if}} --}}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<nav id="combat-controls" class="directory-footer flexrow" data-tooltip-direction="UP">
|
|
||||||
{{#if hasCombat}}
|
|
||||||
{{#if user.isGM}}
|
|
||||||
{{#if round}}
|
|
||||||
<a class="combat-control" aria-label="{{localize 'COMBAT.RoundPrev'}}" role="button" data-tooltip="COMBAT.RoundPrev" data-control="previousRound"><i class="fas fa-step-backward"></i></a>
|
|
||||||
<a class="combat-control" aria-label="{{localize 'COMBAT.TurnPrev'}}" role="button" data-tooltip="COMBAT.TurnPrev" data-control="previousTurn"><i class="fas fa-arrow-left"></i></a>
|
|
||||||
<a class="combat-control center" aria-label="{{localize 'COMBAT.End'}}" role="button" data-control="endCombat">{{localize 'COMBAT.End'}}</a>
|
|
||||||
<a class="combat-control" aria-label="{{localize 'COMBAT.TurnNext'}}" role="button" data-tooltip="COMBAT.TurnNext" data-control="nextTurn"><i class="fas fa-arrow-right"></i></a>
|
|
||||||
<a class="combat-control" aria-label="{{localize 'COMBAT.RoundNext'}}" role="button" data-tooltip="COMBAT.RoundNext" data-control="nextRound"><i class="fas fa-step-forward"></i></a>
|
|
||||||
{{else}}
|
|
||||||
<a class="combat-control center" aria-label="{{localize 'COMBAT.Begin'}}" role="button" data-control="startCombat">{{localize 'COMBAT.Begin'}}</a>
|
|
||||||
{{/if}}
|
|
||||||
{{else if control}}
|
|
||||||
<a class="combat-control" aria-label="{{localize 'COMBAT.TurnPrev'}}" role="button" data-tooltip="COMBAT.TurnPrev" data-control="previousTurn"><i class="fas fa-arrow-left"></i></a>
|
|
||||||
<a class="combat-control center" aria-label="{{localize 'COMBAT.TurnEnd'}}" role="button" data-control="nextTurn">{{localize 'COMBAT.TurnEnd'}}</a>
|
|
||||||
<a class="combat-control" aria-label="{{localize 'COMBAT.TurnNext'}}" role="button" data-tooltip="COMBAT.TurnNext" data-control="nextTurn"><i class="fas fa-arrow-right"></i></a>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</nav>
|
|
||||||
</section>
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
<aside id="players" class="app daggerheart {{#if hide}}hidden{{/if}}">
|
|
||||||
<h3 aria-label="{{localize 'PLAYERS.Title'}}" role="button">
|
|
||||||
<div class="flex-centered">
|
|
||||||
<i class="fas fa-users"></i>
|
|
||||||
{{ localize "PLAYERS.Title" }}
|
|
||||||
{{#if showOffline}}
|
|
||||||
<i class="players-mode fas fa-angle-down"></i>
|
|
||||||
{{else}}
|
|
||||||
<i class="players-mode fas fa-angle-up"></i>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="players-container">
|
|
||||||
<i class="fa-solid fa-skull"></i>
|
|
||||||
<div>{{this.fear}}</div>
|
|
||||||
{{#if user.isGM}}
|
|
||||||
<div class="flexcol">
|
|
||||||
<i class="fa-solid fa-chevron-up fear-control up {{#if (gte this.fear 6)}}disabled{{/if}}"></i>
|
|
||||||
<i class="fa-solid fa-chevron-down fear-control down {{#if (lte this.fear 0)}}disabled{{/if}}"></i>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<ol aria-label="{{localize 'PLAYERS.List'}}" tabIndex="0" id="player-list">
|
|
||||||
{{#each users as |user|}}
|
|
||||||
<li class="player {{#if user.isGM}}gm{{/if}} flexrow" data-user-id="{{user._id}}">
|
|
||||||
<span class="player-active {{#if user.active}}active{{else}}inactive{{/if}}"
|
|
||||||
style="background: {{user.color}}; border: 1px solid {{user.border.css}}"></span>
|
|
||||||
<span class="player-name {{#if user.isSelf}}self{{/if}}" data-tooltip="{{user.displayName}}">
|
|
||||||
{{user.displayName}}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ol>
|
|
||||||
</aside>
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<div class="flex-col">
|
|
||||||
<div class="levelup-title-container">Level {{activeLevel}}</div>
|
|
||||||
<div class="levelup-section">
|
|
||||||
{{#each data}}
|
|
||||||
{{> "systems/daggerheart/templates/views/parts/level.hbs" data=this }}
|
|
||||||
{{/each}}
|
|
||||||
{{!-- {{#each levelupConfig as |configData key|}}
|
|
||||||
{{> "systems/daggerheart/templates/views/parts/level.hbs" configData=configData levelData=(lookup ../levelData key) completedSelection=../completedSelection activeTier=../activeTier activeLevel=../activeLevel category=key }}
|
|
||||||
{{/each}} --}}
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
|
|
||||||
{{#if (eq activeLevel changedLevel )}}
|
|
||||||
<button data-action="finishLevelup" {{#if (not completedSelection)}}disabled{{/if}}>{{localize "DAGGERHEART.Application.LevelUp.TakeLevelUp"}}</button>
|
|
||||||
{{else}}
|
|
||||||
<button data-action="advanceLevel" {{#if (not completedSelection)}}disabled{{/if}}>{{localize "DAGGERHEART.Application.LevelUp.AdvanceLevel" level=(add activeLevel 1)}}</button>
|
|
||||||
{{/if}}
|
|
||||||
<button data-action="close">{{localize "DAGGERHEART.Application.Cancel"}}</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
11
templates/views/levelup/parts/selectable-card-preview.hbs
Normal file
11
templates/views/levelup/parts/selectable-card-preview.hbs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<div class="component dh-style card-preview-container selectable {{#if this.disabled}}disabled{{/if}}" data-action="selectPreview" data-path="{{this.path}}" data-uuid="{{this.uuid}}" {{#if selected}}data-selected="true"{{/if}}>
|
||||||
|
<div class="preview-image-outer-container">
|
||||||
|
<img class="preview-image-container" src="{{this.img}}" />
|
||||||
|
{{#if this.selected}}
|
||||||
|
<div class="preview-selected-icon-container">
|
||||||
|
<i class="fa-solid fa-check"></i>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<div class="preview-text-container">{{this.name}}</div>
|
||||||
|
</div>
|
||||||
40
templates/views/levelup/tabs/advancements.hbs
Normal file
40
templates/views/levelup/tabs/advancements.hbs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<section
|
||||||
|
class='tab {{tabs.advancements.cssClass}} {{tabs.advancements.id}}'
|
||||||
|
data-tab='{{tabs.advancements.id}}'
|
||||||
|
data-group='{{tabs.advancements.group}}'
|
||||||
|
>
|
||||||
|
<div class="section-container">
|
||||||
|
<div class="tiers-container">
|
||||||
|
{{#each this.levelup.tiersForRendering as |tier key|}}
|
||||||
|
<fieldset class="tier-container {{#if (not tier.active)}}inactive{{/if}}">
|
||||||
|
<legend>{{tier.name}}</legend>
|
||||||
|
|
||||||
|
{{#each tier.groups}}
|
||||||
|
<div class="checkbox-group-container">
|
||||||
|
<div class="checkboxes-container">
|
||||||
|
{{#each this.checkboxGroups}}
|
||||||
|
<div class="checkbox-grouping-coontainer {{#if this.multi}}multi{{/if}}">
|
||||||
|
{{#each this.checkboxes}}
|
||||||
|
<input
|
||||||
|
type="checkbox" class="selection-checkbox{{#if (gt this.cost 1)}} multi{{/if}}" {{checked this.selected}} {{#if this.disabled}}disabled{{/if}}
|
||||||
|
data-tier="{{this.tier}}"
|
||||||
|
data-level="{{this.level}}"
|
||||||
|
data-option="{{this.type}}"
|
||||||
|
data-checkbox-nr="{{this.checkboxNr}}"
|
||||||
|
data-cost="{{this.minCost}}"
|
||||||
|
data-amount="{{this.amount}}"
|
||||||
|
data-value="{{this.value}}"
|
||||||
|
data-type="{{this.type}}"
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<div class="checkbox-group-label">{{this.label}}</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</fieldset>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
95
templates/views/levelup/tabs/selections.hbs
Normal file
95
templates/views/levelup/tabs/selections.hbs
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
<section
|
||||||
|
class='tab {{tabs.selections.cssClass}} {{tabs.selections.id}}'
|
||||||
|
data-tab='{{tabs.selections.id}}'
|
||||||
|
data-group='{{tabs.selections.group}}'
|
||||||
|
>
|
||||||
|
<div class="section-container levelup-selections-container">
|
||||||
|
{{#if (gt this.newExperiences.length 0)}}
|
||||||
|
<div>
|
||||||
|
<h3>{{localize "DAGGERHEART.Application.LevelUp.summary.newExperiences"}}</h3>
|
||||||
|
<div class="achievement-experience-cards">
|
||||||
|
{{#each this.newExperiences}}
|
||||||
|
<div class="achievement-experience-card">
|
||||||
|
<div class="flexrow">
|
||||||
|
<input type="text" name="{{concat "levelup.levels." this.level ".achievements.experiences." this.key ".name"}}" value="{{this.name}}" placeholder="{{localize "DAGGERHEART.Application.LevelUp.summary.experiencePlaceholder"}}" />
|
||||||
|
<div class="flex0">{{signedNumber this.modifier}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="achievement-experience-marker">
|
||||||
|
{{#if this.name}}<i class="fa-solid fa-check"></i>{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.traits.active}}
|
||||||
|
<div>
|
||||||
|
<h3 class="levelup-selections-title">
|
||||||
|
<div>{{localize "DAGGERHEART.Application.LevelUp.summary.traits"}}</div>
|
||||||
|
<div>{{this.traits.progress.selected}}/{{this.traits.progress.max}}</div>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<input type="text" class="levelup-trait-increases" value="{{this.traits.values}}" />
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.experienceIncreases.active}}
|
||||||
|
<div>
|
||||||
|
<h3 class="levelup-selections-title">
|
||||||
|
<div>{{localize "DAGGERHEART.Application.LevelUp.summary.experienceIncreases"}}</div>
|
||||||
|
<div>{{this.experienceIncreases.progress.selected}}/{{this.experienceIncreases.progress.max}}</div>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<input type="text" class="levelup-experience-increases" value="{{this.experienceIncreases.values}}" />
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (gt this.domainCards.length 0)}}
|
||||||
|
<div>
|
||||||
|
<h3>{{localize "DAGGERHEART.Application.LevelUp.summary.domainCards"}}</h3>
|
||||||
|
|
||||||
|
<div class="levelup-card-selection domain-cards">
|
||||||
|
{{#each this.domainCards}}
|
||||||
|
{{> "systems/daggerheart/templates/components/card-preview.hbs" this }}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (gt this.subclassCards.length 0)}}
|
||||||
|
<div>
|
||||||
|
<h3>{{localize "DAGGERHEART.Application.LevelUp.summary.subclass"}}</h3>
|
||||||
|
|
||||||
|
<div class="levelup-card-selection subclass-cards">
|
||||||
|
{{#each this.subclassCards}}
|
||||||
|
{{> "systems/daggerheart/templates/views/levelup/parts/selectable-card-preview.hbs" img=this.img name=this.name path=this.path selected=this.selected uuid=this.uuid disabled=this.disabled }}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.multiclass}}
|
||||||
|
<div>
|
||||||
|
<h3>{{localize "DAGGERHEART.Application.LevelUp.summary.multiclass"}}</h3>
|
||||||
|
|
||||||
|
<div class="levelup-card-selection multiclass-cards" data-path="{{this.multiclass.path}}" data-tier="{{this.multiclass.tier}}" data-min-cost="{{this.multiclass.minCost}}" data-amount="{{this.multiclass.amount}}" data-value="{{this.multiclass.value}}" data-type="{{this.multiclass.type}}">
|
||||||
|
{{> "systems/daggerheart/templates/components/card-preview.hbs" this.multiclass }}
|
||||||
|
<div class="levelup-domains-selection-container">
|
||||||
|
{{#each this.multiclass.domains}}
|
||||||
|
<div class="levelup-domain-selection-container {{#if this.disabled}}disabled{{/if}}" {{#if (not this.disabled)}}data-action="selectDomain" data-uuid="{{../multiclass.uuid}}" data-domain="{{this.id}}" data-path="{{../multiclass.path}}" {{/if}}>
|
||||||
|
<div class="levelup-domain-label">{{localize this.label}}</div>
|
||||||
|
<img src="{{this.src}}" />
|
||||||
|
{{#if this.selected}}
|
||||||
|
<div class="levelup-domain-selected">
|
||||||
|
<i class="fa-solid fa-check"></i>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
134
templates/views/levelup/tabs/summary.hbs
Normal file
134
templates/views/levelup/tabs/summary.hbs
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
<section
|
||||||
|
class='tab {{tabs.summary.cssClass}} {{tabs.summary.id}}'
|
||||||
|
data-tab='{{tabs.summary.id}}'
|
||||||
|
data-group='{{tabs.summary.group}}'
|
||||||
|
>
|
||||||
|
<div class="section-container levelup-summary-container">
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "DAGGERHEART.Application.LevelUp.summary.levelAchievements"}}</legend>
|
||||||
|
|
||||||
|
<div class="level-achievements-container">
|
||||||
|
{{#if this.achievements.proficiency.shown}}
|
||||||
|
<div>
|
||||||
|
<div class="increase-container">
|
||||||
|
{{localize "DAGGERHEART.Application.LevelUp.summary.proficiencyIncrease" proficiency=this.achievements.proficiency.old }}
|
||||||
|
<i class="fa-solid fa-arrow-right-long"></i>
|
||||||
|
{{this.achievements.proficiency.new}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<div>
|
||||||
|
<h5 class="summary-section">{{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholds"}}{{#if this.levelAchievements.damageThresholds.unarmored}}({{localize "DAGGERHEART.General.unarmored"}}){{/if}}</h5>
|
||||||
|
<div class="increase-container">
|
||||||
|
{{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholdMajorIncrease" threshold=this.achievements.damageThresholds.major.old }}
|
||||||
|
<i class="fa-solid fa-arrow-right-long"></i>
|
||||||
|
{{this.achievements.damageThresholds.major.new}}
|
||||||
|
</div>
|
||||||
|
<div class="increase-container">
|
||||||
|
{{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholdSevereIncrease" threshold=this.achievements.damageThresholds.severe.old }}
|
||||||
|
<i class="fa-solid fa-arrow-right-long"></i>
|
||||||
|
{{this.achievements.damageThresholds.severe.new}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#if this.achievements.domainCards.shown}}
|
||||||
|
<div>
|
||||||
|
<h5>{{localize "DAGGERHEART.Application.LevelUp.summary.domainCards"}}</h5>
|
||||||
|
<div class="summary-selection-container">
|
||||||
|
{{#each this.achievements.domainCards.values}}
|
||||||
|
<div class="summary-selection">{{this.name}}</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if this.achievements.experiences.shown}}
|
||||||
|
<div>
|
||||||
|
<h5>{{localize "DAGGERHEART.Application.LevelUp.summary.newExperiences"}}</h5>
|
||||||
|
<div class="summary-selection-container">
|
||||||
|
{{#each this.achievements.experiences.values}}
|
||||||
|
<div class="summary-selection">{{this.name}} {{signedNumber this.modifier}}</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "DAGGERHEART.Application.LevelUp.summary.levelAdvancements"}}</legend>
|
||||||
|
|
||||||
|
<div class="level-advancements-container">
|
||||||
|
{{#if this.advancements.statistics.shown}}
|
||||||
|
<div>
|
||||||
|
{{#if this.advancements.statistics.proficiency.shown}}
|
||||||
|
<div class="increase-container">
|
||||||
|
{{localize "DAGGERHEART.Application.LevelUp.summary.proficiencyIncrease" proficiency=this.advancements.statistics.proficiency.old }}
|
||||||
|
<i class="fa-solid fa-arrow-right-long"></i>
|
||||||
|
{{this.advancements.statistics.proficiency.new}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.advancements.statistics.hitPoints.shown}}
|
||||||
|
<div class="increase-container">
|
||||||
|
{{localize "DAGGERHEART.Application.LevelUp.summary.hpIncrease" hitPoints=this.advancements.statistics.hitPoints.old }}
|
||||||
|
<i class="fa-solid fa-arrow-right-long"></i>
|
||||||
|
{{this.advancements.statistics.hitPoints.new}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.advancements.statistics.stress.shown}}
|
||||||
|
<div class="increase-container">
|
||||||
|
{{localize "DAGGERHEART.Application.LevelUp.summary.stressIncrease" stress=this.advancements.statistics.stress.old }}
|
||||||
|
<i class="fa-solid fa-arrow-right-long"></i>
|
||||||
|
{{this.advancements.statistics.stress.new}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if this.advancements.statistics.evasion.shown}}
|
||||||
|
<div class="increase-container">
|
||||||
|
{{localize "DAGGERHEART.Application.LevelUp.summary.evasionIncrease" evasion=this.advancements.statistics.evasion.old }}
|
||||||
|
<i class="fa-solid fa-arrow-right-long"></i>
|
||||||
|
{{this.advancements.statistics.evasion.new}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.advancements.traits}}
|
||||||
|
<div>
|
||||||
|
<h5>{{localize "DAGGERHEART.Application.LevelUp.summary.traits"}}</h5>
|
||||||
|
<div class="summary-selection-container">
|
||||||
|
{{#each this.advancements.traits}}
|
||||||
|
<div class="summary-selection">{{this}}</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.advancements.domainCards}}
|
||||||
|
<div>
|
||||||
|
<h5>{{localize "DAGGERHEART.Application.LevelUp.summary.domainCards"}}</h5>
|
||||||
|
<div class="summary-selection-container">
|
||||||
|
{{#each this.advancements.domainCards}}
|
||||||
|
<div class="summary-selection">{{this.name}}</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.advancements.experiences}}
|
||||||
|
<div>
|
||||||
|
<h5>{{localize "DAGGERHEART.Application.LevelUp.summary.experienceIncreases"}}</h5>
|
||||||
|
<div class="summary-selection-container">
|
||||||
|
{{#each this.advancements.experiences}}
|
||||||
|
<div class="summary-selection">{{this.name}} {{signedNumber this.modifier}}</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<footer class="levelup-footer">
|
||||||
|
<button data-action="save" {{#if (not this.levelup.allLevelsFinished)}}disabled{{/if}}>{{localize "Finish Levelup"}}</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
40
templates/views/levelup/tabs/tab-navigation.hbs
Normal file
40
templates/views/levelup/tabs/tab-navigation.hbs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<section class='tab-navigation'>
|
||||||
|
<line-div></line-div>
|
||||||
|
<div class="levelup-navigation-container">
|
||||||
|
{{#if this.showTabs}}
|
||||||
|
<nav class='feature-tab sheet-tabs tabs' data-group='primary'>
|
||||||
|
{{#each tabs as |tab|}}
|
||||||
|
{{#if (not (eq tab.id 'summary'))}}
|
||||||
|
<div class="levelup-tab-container">
|
||||||
|
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
|
||||||
|
{{localize tab.label}}
|
||||||
|
</a>
|
||||||
|
{{#if tab.progress}}
|
||||||
|
<div>{{tab.progress.selected}}/{{tab.progress.max}}</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</nav>
|
||||||
|
{{/if}}
|
||||||
|
<div class="levelup-navigation-actions {{#if (not this.showTabs)}}test{{/if}}">
|
||||||
|
{{#if this.navigate.previous.fromSummary}}
|
||||||
|
<button data-action="activatePart" data-part="advancements">{{localize "DAGGERHEART.Application.LevelUp.navigateToLevelup"}}</button>
|
||||||
|
{{else}}
|
||||||
|
{{#if (not this.navigate.previous.disabled)}}
|
||||||
|
<button data-action="updateCurrentLevel" >{{this.navigate.previous.label}}</button>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if this.navigate.next.show}}
|
||||||
|
{{#if this.navigate.next.toSummary}}
|
||||||
|
<button data-action="activatePart" data-part="summary" {{#if this.navigate.next.disabled}}disabled{{/if}}>{{localize "DAGGERHEART.Application.LevelUp.navigateToSummary"}}</button>
|
||||||
|
{{else}}
|
||||||
|
<button data-action="updateCurrentLevel" data-forward="true" {{#if this.navigate.next.disabled}}disabled{{/if}}>{{this.navigate.next.label}}</button>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
<div></div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<line-div></line-div>
|
||||||
|
</section>
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
<div class="levelup-container {{#if (not data.active)}}disabled{{/if}}">
|
|
||||||
<fieldset class="levelup-inner-container">
|
|
||||||
<legend class="levelup-legend">
|
|
||||||
{{data.label}}
|
|
||||||
</legend>
|
|
||||||
|
|
||||||
<div class="levelup-info">{{data.info}}</div>
|
|
||||||
<div class="levelup-pretext">{{data.pretext}}</div>
|
|
||||||
<div class="levelup-body">
|
|
||||||
{{#each data.choices as |choice choiceKey|}}
|
|
||||||
<div class="levelup-choice-row">
|
|
||||||
<div class="levelup-choice-row-inner">
|
|
||||||
{{#each choice.values as |value valueKey|}}
|
|
||||||
{{#times choice.cost}}
|
|
||||||
<div class="levelup-choice-input-container">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
data-action="toggleBox"
|
|
||||||
data-path="{{../path}}"
|
|
||||||
data-level-attribute="{{@../key}}"
|
|
||||||
{{checked ../selected}}
|
|
||||||
{{#if ../disabled}}disabled{{/if}}
|
|
||||||
/>
|
|
||||||
{{#if (lt (add this 1) ../../cost)}}
|
|
||||||
<i class="fa-solid fa-link"></i>
|
|
||||||
{{/if}}
|
|
||||||
{{#if ../locked}}
|
|
||||||
<i class="fa-solid fa-lock"></i>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/times}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
<div>{{localize choice.description}}</div>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
<div class="levelup-posttext">{{data.posttext}}</div>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,52 +1,47 @@
|
||||||
import fs from "fs";
|
import fs from 'fs';
|
||||||
import path from "path";
|
import path from 'path';
|
||||||
import readline from "readline";
|
import readline from 'readline';
|
||||||
|
|
||||||
console.log("Reforging Symlinks");
|
console.log('Reforging Symlinks');
|
||||||
|
|
||||||
const askQuestion = (question) => {
|
const askQuestion = question => {
|
||||||
const rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
output: process.stdout
|
output: process.stdout
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise((resolve) =>
|
return new Promise(resolve =>
|
||||||
rl.question(question, (answer) => {
|
rl.question(question, answer => {
|
||||||
rl.close();
|
rl.close();
|
||||||
resolve(answer);
|
resolve(answer);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const installPath = await askQuestion("Enter your Foundry install path: ");
|
const installPath = await askQuestion('Enter your Foundry install path: ');
|
||||||
|
|
||||||
// Determine if it's an Electron install (nested structure)
|
// Determine if it's an Electron install (nested structure)
|
||||||
const nested = fs.existsSync(path.join(installPath, "resources", "app"));
|
const nested = fs.existsSync(path.join(installPath, 'resources', 'app'));
|
||||||
const fileRoot = nested
|
const fileRoot = nested ? path.join(installPath, 'resources', 'app') : installPath;
|
||||||
? path.join(installPath, "resources", "app")
|
|
||||||
: installPath;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.promises.mkdir("foundry");
|
await fs.promises.mkdir('foundry');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code !== "EEXIST") throw e;
|
if (e.code !== 'EEXIST') throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// JavaScript files
|
// JavaScript files
|
||||||
for (const p of ["client", "common", "tsconfig.json"]) {
|
for (const p of ['client', 'common', 'tsconfig.json']) {
|
||||||
try {
|
try {
|
||||||
await fs.promises.symlink(path.join(fileRoot, p), path.join("foundry", p));
|
await fs.promises.symlink(path.join(fileRoot, p), path.join('foundry', p));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code !== "EEXIST") throw e;
|
if (e.code !== 'EEXIST') throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Language files
|
// Language files
|
||||||
try {
|
try {
|
||||||
await fs.promises.symlink(
|
await fs.promises.symlink(path.join(fileRoot, 'public', 'lang'), path.join('foundry', 'lang'));
|
||||||
path.join(fileRoot, "public", "lang"),
|
|
||||||
path.join("foundry", "lang")
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code !== "EEXIST") throw e;
|
if (e.code !== 'EEXIST') throw e;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue