mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-03-11 03:27:08 +01:00
Merge branch 'main' into feature/-179-apply-items-filter-in-actors-sheet
This commit is contained in:
commit
09eaa0d771
143 changed files with 5790 additions and 2780 deletions
|
|
@ -1,19 +1,24 @@
|
||||||
# Daggerheart
|
# Daggerheart
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Overview](#overview)
|
- [Overview](#overview)
|
||||||
- [User Install Guide](#user-install)
|
- [User Install Guide](#user-install)
|
||||||
- [Developer Setup](#developer-setup)
|
- [Developer Setup](#developer-setup)
|
||||||
- [Contribution Info](#contributing)
|
- [Contribution Info](#contributing)
|
||||||
|
|
||||||
## Overivew
|
## Overview
|
||||||
|
|
||||||
This is a community repo for a Foundry VTT implementation of Daggerheart. It is not associated with Critical Role or Darrington Press.
|
This is a community repo for a Foundry VTT implementation of Daggerheart. It is not associated with Critical Role or Darrington Press.
|
||||||
|
|
||||||
## User Install
|
## User Install
|
||||||
|
|
||||||
1. **(Not Yet Supported - No Releases Yet)** Pasting `https://raw.githubusercontent.com/Foundryborne/daggerheart/refs/heads/main/system.json` into the Install System dialog on the Setup menu of the application.
|
1. **(Not Yet Supported - No Releases Yet)** Pasting `https://raw.githubusercontent.com/Foundryborne/daggerheart/refs/heads/main/system.json` into the Install System dialog on the Setup menu of the application.
|
||||||
2. **(Not Yet Supported - No Releases Yet)** Browsing the repository's Releases page, where you can copy any system.json link for use in the Install System dialog.
|
2. **(Not Yet Supported - No Releases Yet)** Browsing the repository's Releases page, where you can copy any system.json link for use in the Install System dialog.
|
||||||
3. **(Not Yet Supported - No Releases Yet)** Downloading one of the .zip archives from the Releases page and extracting it into your foundry Data folder, under Data/systems/daggerheart.
|
3. **(Not Yet Supported - No Releases Yet)** Downloading one of the .zip archives from the Releases page and extracting it into your foundry Data folder, under Data/systems/daggerheart.
|
||||||
|
|
||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
- Open a terminal in the directory with the repo `cd <path>/<to>/<repo>`
|
- Open a terminal in the directory with the repo `cd <path>/<to>/<repo>`
|
||||||
- NOTE: The repo should be placed in the system files are or somewhere else and a link (if on linux) is placed in the system directory
|
- NOTE: The repo should be placed in the system files are or somewhere else and a link (if on linux) is placed in the system directory
|
||||||
- NOTE: Linux link can be made using `ln -snf <path to development folder> daggerheart` inside the system folder
|
- NOTE: Linux link can be made using `ln -snf <path to development folder> daggerheart` inside the system folder
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ Hooks.once('init', () => {
|
||||||
|
|
||||||
Actors.unregisterSheet('core', foundry.applications.sheets.ActorSheetV2);
|
Actors.unregisterSheet('core', foundry.applications.sheets.ActorSheetV2);
|
||||||
Actors.registerSheet(SYSTEM.id, applications.DhCharacterSheet, { types: ['character'], makeDefault: true });
|
Actors.registerSheet(SYSTEM.id, applications.DhCharacterSheet, { types: ['character'], makeDefault: true });
|
||||||
|
Actors.registerSheet(SYSTEM.id, applications.DhCompanionSheet, { types: ['companion'], makeDefault: true });
|
||||||
Actors.registerSheet(SYSTEM.id, applications.DhpAdversarySheet, { types: ['adversary'], makeDefault: true });
|
Actors.registerSheet(SYSTEM.id, applications.DhpAdversarySheet, { types: ['adversary'], makeDefault: true });
|
||||||
Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true });
|
Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true });
|
||||||
|
|
||||||
|
|
@ -123,6 +124,7 @@ Hooks.once('init', () => {
|
||||||
|
|
||||||
CONFIG.ui.resources = Resources;
|
CONFIG.ui.resources = Resources;
|
||||||
CONFIG.ux.ContextMenu = applications.DhContextMenu;
|
CONFIG.ux.ContextMenu = applications.DhContextMenu;
|
||||||
|
CONFIG.ux.TooltipManager = applications.DhTooltipManager;
|
||||||
|
|
||||||
game.socket.on(`system.${SYSTEM.id}`, handleSocketEvent);
|
game.socket.on(`system.${SYSTEM.id}`, handleSocketEvent);
|
||||||
|
|
||||||
|
|
@ -211,46 +213,27 @@ Hooks.on('chatMessage', (_, message) => {
|
||||||
})
|
})
|
||||||
: game.i18n.localize('DAGGERHEART.General.Duality');
|
: game.i18n.localize('DAGGERHEART.General.Duality');
|
||||||
|
|
||||||
const hopeAndFearRoll = `1${rollCommand.hope ?? 'd12'}+1${rollCommand.fear ?? 'd12'}`;
|
const config = {
|
||||||
const advantageRoll = `${advantageState === true ? '+d6' : advantageState === false ? '-d6' : ''}`;
|
|
||||||
const attributeRoll = `${trait?.value ? `${trait.value > 0 ? `+${trait.value}` : `${trait.value}`}` : ''}`;
|
|
||||||
const roll = await Roll.create(`${hopeAndFearRoll}${advantageRoll}${attributeRoll}`).evaluate();
|
|
||||||
|
|
||||||
setDiceSoNiceForDualityRoll(roll, advantageState);
|
|
||||||
|
|
||||||
resolve({
|
|
||||||
roll,
|
|
||||||
trait: trait
|
|
||||||
? {
|
|
||||||
value: trait.value,
|
|
||||||
label: `${game.i18n.localize(abilities[traitValue].label)} ${trait.value >= 0 ? `+` : ``}${trait.value}`
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
title
|
|
||||||
});
|
|
||||||
}).then(async ({ roll, trait, title }) => {
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
|
||||||
const systemData = new DHDualityRoll({
|
|
||||||
title: title,
|
title: title,
|
||||||
origin: target?.id,
|
roll: {
|
||||||
roll: roll,
|
trait: traitValue
|
||||||
modifiers: trait ? [trait] : [],
|
},
|
||||||
hope: { dice: rollCommand.hope ?? 'd12', value: roll.dice[0].total },
|
data: {
|
||||||
fear: { dice: rollCommand.fear ?? 'd12', value: roll.dice[1].total },
|
traits: {
|
||||||
advantage: advantageState !== null ? { dice: 'd6', value: roll.dice[2].total } : undefined,
|
[traitValue]: trait
|
||||||
advantageState
|
}
|
||||||
});
|
},
|
||||||
|
source: target,
|
||||||
const msgData = {
|
hasSave: false,
|
||||||
type: 'dualityRoll',
|
dialog: { configure: false },
|
||||||
sound: CONFIG.sounds.dice,
|
evaluate: true,
|
||||||
system: systemData,
|
advantage: rollCommand.advantage == true,
|
||||||
user: game.user.id,
|
disadvantage: rollCommand.disadvantage == true
|
||||||
content: 'systems/daggerheart/templates/chat/duality-roll.hbs',
|
|
||||||
rolls: [roll]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
cls.create(msgData);
|
await CONFIG.Dice.daggerheart['DualityRoll'].build(config);
|
||||||
|
|
||||||
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,9 +268,9 @@ const preloadHandlebarsTemplates = async function () {
|
||||||
return foundry.applications.handlebars.loadTemplates([
|
return foundry.applications.handlebars.loadTemplates([
|
||||||
'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs',
|
'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs',
|
||||||
|
'systems/daggerheart/templates/sheets/global/partials/action-item.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs',
|
||||||
|
|
||||||
'systems/daggerheart/templates/sheets/parts/attributes.hbs',
|
'systems/daggerheart/templates/sheets/parts/attributes.hbs',
|
||||||
'systems/daggerheart/templates/sheets/parts/defense.hbs',
|
'systems/daggerheart/templates/sheets/parts/defense.hbs',
|
||||||
'systems/daggerheart/templates/sheets/parts/armor.hbs',
|
'systems/daggerheart/templates/sheets/parts/armor.hbs',
|
||||||
|
|
@ -301,10 +284,6 @@ const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/sheets/parts/heritage.hbs',
|
'systems/daggerheart/templates/sheets/parts/heritage.hbs',
|
||||||
'systems/daggerheart/templates/sheets/parts/subclassFeature.hbs',
|
'systems/daggerheart/templates/sheets/parts/subclassFeature.hbs',
|
||||||
'systems/daggerheart/templates/sheets/parts/effects.hbs',
|
'systems/daggerheart/templates/sheets/parts/effects.hbs',
|
||||||
'systems/daggerheart/templates/sheets/character/sections/inventory.hbs',
|
|
||||||
'systems/daggerheart/templates/sheets/character/sections/loadout.hbs',
|
|
||||||
'systems/daggerheart/templates/sheets/character/parts/heritageCard.hbs',
|
|
||||||
'systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs',
|
|
||||||
'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-features.hbs',
|
'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-features.hbs',
|
||||||
'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-feature.hbs',
|
'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-feature.hbs',
|
||||||
'systems/daggerheart/templates/components/card-preview.hbs',
|
'systems/daggerheart/templates/components/card-preview.hbs',
|
||||||
|
|
@ -322,7 +301,7 @@ const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/views/actionTypes/range-target.hbs',
|
'systems/daggerheart/templates/views/actionTypes/range-target.hbs',
|
||||||
'systems/daggerheart/templates/views/actionTypes/effect.hbs',
|
'systems/daggerheart/templates/views/actionTypes/effect.hbs',
|
||||||
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
||||||
|
'systems/daggerheart/templates/chat/parts/damage-chat.hbs',
|
||||||
'systems/daggerheart/templates/chat/parts/target-chat.hbs'
|
'systems/daggerheart/templates/chat/parts/target-chat.hbs'
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
187
lang/en.json
187
lang/en.json
|
|
@ -14,11 +14,17 @@
|
||||||
},
|
},
|
||||||
"Actor": {
|
"Actor": {
|
||||||
"character": "Character",
|
"character": "Character",
|
||||||
|
"companion": "Companion",
|
||||||
"adversary": "Adversary",
|
"adversary": "Adversary",
|
||||||
"environment": "Environment"
|
"environment": "Environment"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DAGGERHEART": {
|
"DAGGERHEART": {
|
||||||
|
"UI": {
|
||||||
|
"notifications": {
|
||||||
|
"adversaryMissing": "The linked adversary doesn't exist in the world."
|
||||||
|
}
|
||||||
|
},
|
||||||
"Settings": {
|
"Settings": {
|
||||||
"Menu": {
|
"Menu": {
|
||||||
"Automation": {
|
"Automation": {
|
||||||
|
|
@ -191,6 +197,10 @@
|
||||||
"Full": "Disadvantage",
|
"Full": "Disadvantage",
|
||||||
"Short": "Dis"
|
"Short": "Dis"
|
||||||
},
|
},
|
||||||
|
"Neutral": {
|
||||||
|
"Full": "None",
|
||||||
|
"Short": "no"
|
||||||
|
},
|
||||||
"OK": "OK",
|
"OK": "OK",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"Or": "Or",
|
"Or": "Or",
|
||||||
|
|
@ -222,12 +232,22 @@
|
||||||
"Major": "Major",
|
"Major": "Major",
|
||||||
"Minor": "Minor",
|
"Minor": "Minor",
|
||||||
"None": "None"
|
"None": "None"
|
||||||
}
|
},
|
||||||
|
"tabs": {
|
||||||
|
"details": "Details",
|
||||||
|
"attack": "Attack",
|
||||||
|
"experiences": "Experiences",
|
||||||
|
"features": "Features",
|
||||||
|
"actions": "Actions",
|
||||||
|
"potentialAdversaries": "Potential Adversaries",
|
||||||
|
"adversaries": "Adversaries"
|
||||||
|
},
|
||||||
|
"basics": "Basics"
|
||||||
},
|
},
|
||||||
"ActionType": {
|
"ActionType": {
|
||||||
"Passive": "Passive",
|
"passive": "Passive",
|
||||||
"Action": "Action",
|
"action": "Action",
|
||||||
"Reaction": "Reaction"
|
"reaction": "Reaction"
|
||||||
},
|
},
|
||||||
"Abilities": {
|
"Abilities": {
|
||||||
"agility": {
|
"agility": {
|
||||||
|
|
@ -311,45 +331,45 @@
|
||||||
},
|
},
|
||||||
"Adversary": {
|
"Adversary": {
|
||||||
"Type": {
|
"Type": {
|
||||||
"Bruiser": {
|
"bruiser": {
|
||||||
"label": "Bruiser",
|
"label": "Bruiser",
|
||||||
"Description": "Tough adversaries with powerful attacks."
|
"description": "Tough adversaries with powerful attacks."
|
||||||
},
|
},
|
||||||
"Horde": {
|
"horde": {
|
||||||
"label": "Horde",
|
"label": "Horde",
|
||||||
"Description": "A Horde represents a number of foes working in a group."
|
"description": "A Horde represents a number of foes working in a group."
|
||||||
},
|
},
|
||||||
"Leader": {
|
"leader": {
|
||||||
"label": "Leader",
|
"label": "Leader",
|
||||||
"Description": "Adversaries that command and summon other adversaries."
|
"description": "Adversaries that command and summon other adversaries."
|
||||||
},
|
},
|
||||||
"Minion": {
|
"minion": {
|
||||||
"label": "Minion",
|
"label": "Minion",
|
||||||
"Description": "Basic enemies that are easily dispatched but dangerous in numbers."
|
"description": "Basic enemies that are easily dispatched but dangerous in numbers."
|
||||||
},
|
},
|
||||||
"Ranged": {
|
"ranged": {
|
||||||
"label": "Ranged",
|
"label": "Ranged",
|
||||||
"Description": "Adversaries that attack from a distance."
|
"description": "Adversaries that attack from a distance."
|
||||||
},
|
},
|
||||||
"Skulk": {
|
"skulk": {
|
||||||
"label": "Skulk",
|
"label": "Skulk",
|
||||||
"Description": "Adversaries that maneuver and exploit opportunities to ambush their opponents."
|
"description": "Adversaries that maneuver and exploit opportunities to ambush their opponents."
|
||||||
},
|
},
|
||||||
"Social": {
|
"social": {
|
||||||
"label": "Social",
|
"label": "Social",
|
||||||
"Description": "Adversaries that are primarily interpersonal threats or challenges."
|
"description": "Adversaries that are primarily interpersonal threats or challenges."
|
||||||
},
|
},
|
||||||
"Solo": {
|
"solo": {
|
||||||
"label": "Solo",
|
"label": "Solo",
|
||||||
"Description": "Designed to present a challenge to a whole party."
|
"description": "Designed to present a challenge to a whole party."
|
||||||
},
|
},
|
||||||
"Standard": {
|
"standard": {
|
||||||
"label": "Standard",
|
"label": "Standard",
|
||||||
"Description": "Rank and File adversaries."
|
"description": "Rank and File adversaries."
|
||||||
},
|
},
|
||||||
"Support": {
|
"support": {
|
||||||
"label": "Support",
|
"label": "Support",
|
||||||
"Description": "Enemies that enhance their allies and/or disrupt their opponents."
|
"description": "Enemies that enhance their allies and/or disrupt their opponents."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Trait": {
|
"Trait": {
|
||||||
|
|
@ -372,19 +392,19 @@
|
||||||
},
|
},
|
||||||
"Environment": {
|
"Environment": {
|
||||||
"Type": {
|
"Type": {
|
||||||
"Exploration": {
|
"exploration": {
|
||||||
"label": "Exploration",
|
"label": "Exploration",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"Social": {
|
"social": {
|
||||||
"label": "Social",
|
"label": "Social",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"Traversal": {
|
"traversal": {
|
||||||
"label": "Traversal",
|
"label": "Traversal",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"Event": {
|
"event": {
|
||||||
"label": "Event",
|
"label": "Event",
|
||||||
"description": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
|
|
@ -490,7 +510,29 @@
|
||||||
"evasion": "Permanently gain a +1 bonus to your Evasion.",
|
"evasion": "Permanently gain a +1 bonus to your Evasion.",
|
||||||
"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.",
|
||||||
"proficiency": "Increase your Proficiency by +1.",
|
"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."
|
"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.",
|
||||||
|
"intelligent": "Your companion gains a permanent +1 bonus to a Companion Experience of your choice.",
|
||||||
|
"lightInTheDark": "Gain an additional Hope slot for your character.",
|
||||||
|
"creatureComfort": "Once per rest, when you take time during a quiet moment to give your companion love and attention, you can gain a Hope or you can both clear a Stress.",
|
||||||
|
"armored": "When your companion takes damage, you can mark one of your Armor Slots instead of marking one of their Stress.",
|
||||||
|
"vicious": "Increase your companion's damage dice or range by one step (d6 to d8, Close to Far, etc.)",
|
||||||
|
"resilient": "Your companion gains an additional Stress slot.",
|
||||||
|
"bonded": "When you mark your last Hit Point, your companion rushes to your side to comfort you. Roll a number of d6s equal to the unmarked Stress slots they have and mark them. If any roll a 6, your companion helps you up. Clear your last Hit Point and return to the scene.",
|
||||||
|
"aware": "Your companion gains a permanent +2 bonus to their Evasion."
|
||||||
|
},
|
||||||
|
"Actions": {
|
||||||
|
"CreatureComfort": {
|
||||||
|
"Name": "Creature Comfort",
|
||||||
|
"Description": "Once per rest, when you take time during a quiet moment to give your companion love and attention, you can gain a Hope or you can both clear a Stress."
|
||||||
|
},
|
||||||
|
"Armored": {
|
||||||
|
"Name": "Armored",
|
||||||
|
"Description": "When your companion takes damage, you can mark one of your Armor Slots instead of marking one of their Stress."
|
||||||
|
},
|
||||||
|
"Bonded": {
|
||||||
|
"Name": "Bonded",
|
||||||
|
"Description": "When you mark your last Hit Point, your companion rushes to your side to comfort you. Roll a number of d6s equal to the unmarked Stress slots they have and mark them. If any roll a 6, your companion helps you up. Clear your last Hit Point and return to the scene."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Tier2": {
|
"Tier2": {
|
||||||
"Label": "Levels 2-4",
|
"Label": "Levels 2-4",
|
||||||
|
|
@ -961,7 +1003,9 @@
|
||||||
"content": "Returning to the previous level selection will remove all selections made for this level. Do you want to proceed?"
|
"content": "Returning to the previous level selection will remove all selections made for this level. Do you want to proceed?"
|
||||||
},
|
},
|
||||||
"Selections": {
|
"Selections": {
|
||||||
"emptyDomainCardHint": "{domain} level {level} or below"
|
"emptyDomainCardHint": "{domain} level {level} or below",
|
||||||
|
"viciousDamage": "Damage",
|
||||||
|
"viciousRange": "Range"
|
||||||
},
|
},
|
||||||
"summary": {
|
"summary": {
|
||||||
"levelAchievements": "Level Achievements",
|
"levelAchievements": "Level Achievements",
|
||||||
|
|
@ -979,7 +1023,11 @@
|
||||||
"multiclass": "Multiclass",
|
"multiclass": "Multiclass",
|
||||||
"traits": "Increased Traits",
|
"traits": "Increased Traits",
|
||||||
"experienceIncreases": "Experience Increases",
|
"experienceIncreases": "Experience Increases",
|
||||||
"damageThresholds": "Damage Thresholds"
|
"damageThresholds": "Damage Thresholds",
|
||||||
|
"vicious": "Vicious",
|
||||||
|
"damageIncreased": "Damage Increased: {damage}",
|
||||||
|
"rangeIncreased": "Range Increased: {range}",
|
||||||
|
"simpleFeature": "Feature: {feature}"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"info": {
|
"info": {
|
||||||
|
|
@ -1034,7 +1082,10 @@
|
||||||
"DamageRoll": {
|
"DamageRoll": {
|
||||||
"Title": "Damage - {damage}",
|
"Title": "Damage - {damage}",
|
||||||
"DealDamageToTargets": "Damage Hit Targets",
|
"DealDamageToTargets": "Damage Hit Targets",
|
||||||
"DealDamage": "Deal Damage"
|
"DealDamage": "Deal Damage",
|
||||||
|
"RollDamage": "Roll Damage",
|
||||||
|
"HitTarget": "Hit Targets",
|
||||||
|
"SelectedTarget": "Selected"
|
||||||
},
|
},
|
||||||
"ApplyEffect": {
|
"ApplyEffect": {
|
||||||
"Title": "Apply Effects - {name}"
|
"Title": "Apply Effects - {name}"
|
||||||
|
|
@ -1087,6 +1138,7 @@
|
||||||
"RemoveCountdownText": "Are you sure you want to remove the countdown: {name}?",
|
"RemoveCountdownText": "Are you sure you want to remove the countdown: {name}?",
|
||||||
"OpenOwnership": "Edit Player Ownership",
|
"OpenOwnership": "Edit Player Ownership",
|
||||||
"Title": "{type} Countdowns",
|
"Title": "{type} Countdowns",
|
||||||
|
"ToggleSimple": "Toggle Simple View",
|
||||||
"Types": {
|
"Types": {
|
||||||
"narrative": "Narrative",
|
"narrative": "Narrative",
|
||||||
"encounter": "Encounter"
|
"encounter": "Encounter"
|
||||||
|
|
@ -1132,6 +1184,8 @@
|
||||||
"CharacterSetup": "Character setup isn't done yet",
|
"CharacterSetup": "Character setup isn't done yet",
|
||||||
"Level": "Level",
|
"Level": "Level",
|
||||||
"LevelUp": "You can level up",
|
"LevelUp": "You can level up",
|
||||||
|
"Features": "Features",
|
||||||
|
"CompanionFeatures": "Companion Features",
|
||||||
"Tabs": {
|
"Tabs": {
|
||||||
"Features": "Features",
|
"Features": "Features",
|
||||||
"Inventory": "Inventory",
|
"Inventory": "Inventory",
|
||||||
|
|
@ -1176,9 +1230,6 @@
|
||||||
"Experience": {
|
"Experience": {
|
||||||
"Title": "Experience"
|
"Title": "Experience"
|
||||||
},
|
},
|
||||||
"Features": {
|
|
||||||
"Title": "Class Features"
|
|
||||||
},
|
|
||||||
"Gold": {
|
"Gold": {
|
||||||
"Title": "Gold",
|
"Title": "Gold",
|
||||||
"Coins": "Coins",
|
"Coins": "Coins",
|
||||||
|
|
@ -1245,6 +1296,24 @@
|
||||||
"tooLowLevel": "You cannot lower the character level below starting level"
|
"tooLowLevel": "You cannot lower the character level below starting level"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Companion": {
|
||||||
|
"FIELDS": {
|
||||||
|
"partner": { "label": "Partner" },
|
||||||
|
"evasion": {
|
||||||
|
"value": { "label": "Evasion" }
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"stress": {
|
||||||
|
"value": { "label": "Stress" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attack": {
|
||||||
|
"name": { "label": "Attack Name" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Experiences": "Experiences",
|
||||||
|
"Level": "Level"
|
||||||
|
},
|
||||||
"Adversary": {
|
"Adversary": {
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
"tier": { "label": "Tier" },
|
"tier": { "label": "Tier" },
|
||||||
|
|
@ -1284,14 +1353,18 @@
|
||||||
},
|
},
|
||||||
"Tabs": {
|
"Tabs": {
|
||||||
"Main": "Data",
|
"Main": "Data",
|
||||||
"Information": "Information"
|
"Information": "Information",
|
||||||
|
"features": "Features",
|
||||||
|
"notes": "Notes",
|
||||||
|
"effects": "Effects"
|
||||||
},
|
},
|
||||||
"General": "General",
|
"General": "General",
|
||||||
"DamageThresholds": "Damage Thresholds",
|
"DamageThresholds": "Damage Thresholds",
|
||||||
"HitPoints": "Hit Points",
|
"HitPoints": "Hit Points",
|
||||||
"Stress": "Stress",
|
"Stress": "Stress",
|
||||||
"Experiences": "Experiences",
|
"Experiences": "Experiences",
|
||||||
"Attack": "Attack"
|
"Attack": "Attack",
|
||||||
|
"horderHp": "Horde/HP"
|
||||||
},
|
},
|
||||||
"Environment": {
|
"Environment": {
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
|
|
@ -1303,6 +1376,12 @@
|
||||||
},
|
},
|
||||||
"difficulty": {
|
"difficulty": {
|
||||||
"label": "Difficulty"
|
"label": "Difficulty"
|
||||||
|
},
|
||||||
|
"impulses": {
|
||||||
|
"label": "Impulses"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"label": "Description"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Tabs": {
|
"Tabs": {
|
||||||
|
|
@ -1516,9 +1595,11 @@
|
||||||
},
|
},
|
||||||
"Tooltip": {
|
"Tooltip": {
|
||||||
"openItemWorld": "Open Item World",
|
"openItemWorld": "Open Item World",
|
||||||
|
"openActorWorld": "Open Actor World",
|
||||||
"sendToChat": "Send to Chat",
|
"sendToChat": "Send to Chat",
|
||||||
"moreOptions": "More Options",
|
"moreOptions": "More Options",
|
||||||
"equip": "Equip",
|
"equip": "Equip",
|
||||||
|
"edit": "Edit",
|
||||||
"unequip": "Unequip",
|
"unequip": "Unequip",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"sendToVault": "Send to Vault",
|
"sendToVault": "Send to Vault",
|
||||||
|
|
@ -1526,29 +1607,29 @@
|
||||||
},
|
},
|
||||||
"Actions": {
|
"Actions": {
|
||||||
"Types": {
|
"Types": {
|
||||||
"Attack": {
|
"attack": {
|
||||||
"Name": "Attack"
|
"name": "Attack"
|
||||||
},
|
},
|
||||||
"Spellcast": {
|
"spellcast": {
|
||||||
"Name": "Spellcast"
|
"name": "Spellcast"
|
||||||
},
|
},
|
||||||
"Resource": {
|
"resource": {
|
||||||
"Name": "Resource"
|
"name": "Resource"
|
||||||
},
|
},
|
||||||
"Damage": {
|
"damage": {
|
||||||
"Name": "Damage"
|
"name": "Damage"
|
||||||
},
|
},
|
||||||
"Healing": {
|
"healing": {
|
||||||
"Name": "Healing"
|
"name": "Healing"
|
||||||
},
|
},
|
||||||
"Summon": {
|
"summon": {
|
||||||
"Name": "Summon"
|
"name": "Summon"
|
||||||
},
|
},
|
||||||
"Effect": {
|
"effect": {
|
||||||
"Name": "Effect"
|
"name": "Effect"
|
||||||
},
|
},
|
||||||
"Macro": {
|
"macro": {
|
||||||
"Name": "Macro"
|
"name": "Macro"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Settings": {
|
"Settings": {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
export { default as DhCharacterSheet } from './sheets/character.mjs';
|
export { default as DhCharacterSheet } from './sheets/actors/character.mjs';
|
||||||
export { default as DhpAdversarySheet } from './sheets/adversary.mjs';
|
export { default as DhpAdversarySheet } from './sheets/actors/adversary.mjs';
|
||||||
|
export { default as DhCompanionSheet } from './sheets/companion.mjs';
|
||||||
export { default as DhpClassSheet } from './sheets/items/class.mjs';
|
export { default as DhpClassSheet } from './sheets/items/class.mjs';
|
||||||
export { default as DhpSubclass } from './sheets/items/subclass.mjs';
|
export { default as DhpSubclass } from './sheets/items/subclass.mjs';
|
||||||
export { default as DhpFeatureSheet } from './sheets/items/feature.mjs';
|
export { default as DhpFeatureSheet } from './sheets/items/feature.mjs';
|
||||||
|
|
@ -11,9 +12,10 @@ export { default as DhpConsumable } from './sheets/items/consumable.mjs';
|
||||||
export { default as DhpWeapon } from './sheets/items/weapon.mjs';
|
export { default as DhpWeapon } from './sheets/items/weapon.mjs';
|
||||||
export { default as DhpArmor } from './sheets/items/armor.mjs';
|
export { default as DhpArmor } from './sheets/items/armor.mjs';
|
||||||
export { default as DhpChatMessage } from './chatMessage.mjs';
|
export { default as DhpChatMessage } from './chatMessage.mjs';
|
||||||
export { default as DhpEnvironment } from './sheets/environment.mjs';
|
export { default as DhpEnvironment } from './sheets/actors/environment.mjs';
|
||||||
export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs';
|
export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs';
|
||||||
export { default as DhContextMenu } from './contextMenu.mjs';
|
export { default as DhContextMenu } from './contextMenu.mjs';
|
||||||
|
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
||||||
|
|
||||||
export * as api from './sheets/api/_modules.mjs';
|
export * as api from './sheets/api/_modules.mjs';
|
||||||
export * as ux from "./ux/_module.mjs";
|
export * as ux from "./ux/_module.mjs";
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ export default class AncestrySelectionDialog extends HandlebarsApplicationMixin(
|
||||||
}
|
}
|
||||||
|
|
||||||
static _onEditImage() {
|
static _onEditImage() {
|
||||||
const fp = new FilePicker({
|
const fp = new foundry.applications.apps.FilePicker.implementation({
|
||||||
current: this.data.ancestryInfo.img,
|
current: this.data.ancestryInfo.img,
|
||||||
type: 'image',
|
type: 'image',
|
||||||
redirectToRoot: ['icons/svg/mystery-man.svg'],
|
redirectToRoot: ['icons/svg/mystery-man.svg'],
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
async renderHTML() {
|
async renderHTML() {
|
||||||
if(this.system.messageTemplate) this.content = await foundry.applications.handlebars.renderTemplate(this.system.messageTemplate, this.system);
|
if (this.system.messageTemplate)
|
||||||
|
this.content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
this.system.messageTemplate,
|
||||||
|
this.system
|
||||||
|
);
|
||||||
|
|
||||||
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
||||||
const html = await super.renderHTML();
|
const html = await super.renderHTML();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export default class DhContextMenu extends ContextMenu {
|
export default class DhContextMenu extends foundry.applications.ux.ContextMenu.implementation {
|
||||||
constructor(container, selector, menuItems, options) {
|
constructor(container, selector, menuItems, options) {
|
||||||
super(container, selector, menuItems, options);
|
super(container, selector, menuItems, options);
|
||||||
|
|
||||||
|
|
@ -26,10 +26,16 @@ export default class DhContextMenu extends ContextMenu {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const { clientX, clientY } = event;
|
const { clientX, clientY } = event;
|
||||||
const selector = "[data-item-id]";
|
const selector = '[data-item-id]';
|
||||||
const target = event.target.closest(selector) ?? event.currentTarget.closest(selector);
|
const target = event.target.closest(selector) ?? event.currentTarget.closest(selector);
|
||||||
target?.dispatchEvent(new PointerEvent("contextmenu", {
|
target?.dispatchEvent(
|
||||||
view: window, bubbles: true, cancelable: true, clientX, clientY
|
new PointerEvent('contextmenu', {
|
||||||
}));
|
view: window,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
clientX,
|
||||||
|
clientY
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { countdownTypes } from '../config/generalConfig.mjs';
|
import { countdownTypes } from '../config/generalConfig.mjs';
|
||||||
import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs';
|
import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs';
|
||||||
|
import constructHTMLButton from '../helpers/utils.mjs';
|
||||||
import OwnershipSelection from './ownershipSelection.mjs';
|
import OwnershipSelection from './ownershipSelection.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
@ -25,14 +26,15 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
frame: true,
|
frame: true,
|
||||||
title: 'Countdowns',
|
title: 'Countdowns',
|
||||||
resizable: true,
|
resizable: true,
|
||||||
minimizable: true
|
minimizable: false
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
addCountdown: this.addCountdown,
|
addCountdown: this.addCountdown,
|
||||||
removeCountdown: this.removeCountdown,
|
removeCountdown: this.removeCountdown,
|
||||||
editImage: this.onEditImage,
|
editImage: this.onEditImage,
|
||||||
openOwnership: this.openOwnership,
|
openOwnership: this.openOwnership,
|
||||||
openCountdownOwnership: this.openCountdownOwnership
|
openCountdownOwnership: this.openCountdownOwnership,
|
||||||
|
toggleSimpleView: this.toggleSimpleView
|
||||||
},
|
},
|
||||||
form: { handler: this.updateData, submitOnChange: true }
|
form: { handler: this.updateData, submitOnChange: true }
|
||||||
};
|
};
|
||||||
|
|
@ -53,11 +55,47 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onFirstRender(context, options) {
|
async _preFirstRender(context, options) {
|
||||||
super._onFirstRender(context, options);
|
options.position =
|
||||||
|
game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].position) ??
|
||||||
|
Countdowns.DEFAULT_OPTIONS.position;
|
||||||
|
|
||||||
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
const viewSetting =
|
||||||
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].simple) ?? !game.user.isGM;
|
||||||
|
this.simpleView =
|
||||||
|
game.user.isGM || !this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER) ? viewSetting : true;
|
||||||
|
context.simple = this.simpleView;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onPosition(position) {
|
||||||
|
game.user.setFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].position, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _renderFrame(options) {
|
||||||
|
const frame = await super._renderFrame(options);
|
||||||
|
|
||||||
|
if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER)) {
|
||||||
|
const button = constructHTMLButton({
|
||||||
|
label: '',
|
||||||
|
classes: ['header-control', 'icon', 'fa-solid', 'fa-wrench'],
|
||||||
|
dataset: { action: 'toggleSimpleView', tooltip: 'DAGGERHEART.Countdown.ToggleSimple' }
|
||||||
|
});
|
||||||
|
this.window.controls.after(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
testUserPermission(level, exact, altSettings) {
|
||||||
|
if (game.user.isGM) return true;
|
||||||
|
|
||||||
|
const settings =
|
||||||
|
altSettings ?? game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||||
|
const defaultAllowed = exact ? settings.ownership.default === level : settings.ownership.default >= level;
|
||||||
|
const userAllowed = exact
|
||||||
|
? settings.playerOwnership[game.user.id]?.value === level
|
||||||
|
: settings.playerOwnership[game.user.id]?.value >= level;
|
||||||
|
return defaultAllowed || userAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
|
|
@ -67,15 +105,17 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
context.isGM = game.user.isGM;
|
context.isGM = game.user.isGM;
|
||||||
context.base = this.basePath;
|
context.base = this.basePath;
|
||||||
|
|
||||||
context.canCreate = countdownData.playerOwnership[game.user.id].value === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
context.canCreate = this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true);
|
||||||
context.source = {
|
context.source = {
|
||||||
...countdownData,
|
...countdownData,
|
||||||
countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => {
|
countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => {
|
||||||
const countdown = countdownData.countdowns[key];
|
const countdown = countdownData.countdowns[key];
|
||||||
|
|
||||||
const ownershipValue = countdown.playerOwnership[game.user.id].value;
|
if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED, false, countdown)) {
|
||||||
if (ownershipValue > CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
acc[key] = {
|
||||||
acc[key] = { ...countdown, canEdit: ownershipValue === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER };
|
...countdown,
|
||||||
|
canEdit: this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true, countdown)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
|
|
@ -83,7 +123,7 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
};
|
};
|
||||||
context.systemFields = countdownData.schema.fields;
|
context.systemFields = countdownData.schema.fields;
|
||||||
context.countdownFields = context.systemFields.countdowns.element.fields;
|
context.countdownFields = context.systemFields.countdowns.element.fields;
|
||||||
context.minimized = this.minimized || _options.isFirstRender;
|
context.simple = this.simpleView;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -110,28 +150,6 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async minimize() {
|
|
||||||
await super.minimize();
|
|
||||||
|
|
||||||
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
|
||||||
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
async maximize() {
|
|
||||||
if (this.minimized) {
|
|
||||||
const settings = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
|
||||||
if (settings.playerOwnership[game.user.id].value <= CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED) {
|
|
||||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.Countdown.Notifications.LimitedOwnership'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
|
||||||
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
await super.maximize();
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateSetting(update) {
|
async updateSetting(update) {
|
||||||
if (game.user.isGM) {
|
if (game.user.isGM) {
|
||||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update);
|
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update);
|
||||||
|
|
@ -160,7 +178,7 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
static onEditImage(_, target) {
|
static onEditImage(_, target) {
|
||||||
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||||
const current = setting.countdowns[target.dataset.countdown].img;
|
const current = setting.countdowns[target.dataset.countdown].img;
|
||||||
const fp = new FilePicker({
|
const fp = new foundry.applications.apps.FilePicker.implementation({
|
||||||
current,
|
current,
|
||||||
type: 'image',
|
type: 'image',
|
||||||
callback: async path => this.updateImage.bind(this)(path, target.dataset.countdown),
|
callback: async path => this.updateImage.bind(this)(path, target.dataset.countdown),
|
||||||
|
|
@ -213,11 +231,17 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async toggleSimpleView() {
|
||||||
|
this.simpleView = !this.simpleView;
|
||||||
|
await game.user.setFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].simple, this.simpleView);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
async updateCountdownValue(event, increase) {
|
async updateCountdownValue(event, increase) {
|
||||||
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||||
const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown];
|
const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown];
|
||||||
|
|
||||||
if (countdown.playerOwnership[game.user.id] < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
|
if (!this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
383
module/applications/levelup/characterLevelup.mjs
Normal file
383
module/applications/levelup/characterLevelup.mjs
Normal file
|
|
@ -0,0 +1,383 @@
|
||||||
|
import LevelUpBase from './levelup.mjs';
|
||||||
|
import { DhLevelup } from '../../data/levelup.mjs';
|
||||||
|
import { domains } from '../../config/domainConfig.mjs';
|
||||||
|
import { abilities } from '../../config/actorConfig.mjs';
|
||||||
|
|
||||||
|
export default class DhCharacterLevelUp extends LevelUpBase {
|
||||||
|
constructor(actor) {
|
||||||
|
super(actor);
|
||||||
|
|
||||||
|
this.levelTiers = this.addBonusChoices(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers));
|
||||||
|
const playerLevelupData = actor.system.levelData;
|
||||||
|
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData));
|
||||||
|
}
|
||||||
|
|
||||||
|
async _preparePartContext(partId, context) {
|
||||||
|
await super._preparePartContext(partId, context);
|
||||||
|
|
||||||
|
const currentLevel = this.levelup.levels[this.levelup.currentLevel];
|
||||||
|
switch (partId) {
|
||||||
|
case 'selections':
|
||||||
|
const advancementChoices = Object.keys(currentLevel.choices).reduce((acc, choiceKey) => {
|
||||||
|
Object.keys(currentLevel.choices[choiceKey]).forEach(checkboxNr => {
|
||||||
|
const checkbox = currentLevel.choices[choiceKey][checkboxNr];
|
||||||
|
const data = {
|
||||||
|
...checkbox,
|
||||||
|
path: `levels.${this.levelup.currentLevel}.choices.${choiceKey}.${checkboxNr}`,
|
||||||
|
level: this.levelup.currentLevel
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!acc[choiceKey]) acc[choiceKey] = [];
|
||||||
|
acc[choiceKey].push(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const traits = Object.values(advancementChoices.trait ?? {});
|
||||||
|
const traitValues = traits.filter(trait => trait.data.length > 0).flatMap(trait => trait.data);
|
||||||
|
context.traits = {
|
||||||
|
values: traitValues,
|
||||||
|
active: traits.length > 0,
|
||||||
|
progress: {
|
||||||
|
selected: traitValues.length,
|
||||||
|
max: traits.reduce((acc, exp) => acc + exp.amount, 0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const experienceIncreases = Object.values(advancementChoices.experience ?? {});
|
||||||
|
const experienceIncreaseValues = experienceIncreases
|
||||||
|
.filter(exp => exp.data.length > 0)
|
||||||
|
.flatMap(exp =>
|
||||||
|
exp.data.map(data => {
|
||||||
|
const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
|
||||||
|
return this.actor.system.experiences[experience].name;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
context.experienceIncreases = {
|
||||||
|
values: experienceIncreaseValues,
|
||||||
|
active: experienceIncreases.length > 0,
|
||||||
|
progress: {
|
||||||
|
selected: experienceIncreaseValues.length,
|
||||||
|
max: experienceIncreases.reduce((acc, exp) => acc + exp.amount, 0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
context.newExperiences = Object.keys(currentLevel.achievements.experiences).map(key => {
|
||||||
|
const experience = currentLevel.achievements.experiences[key];
|
||||||
|
return {
|
||||||
|
...experience,
|
||||||
|
level: this.levelup.currentLevel,
|
||||||
|
key: key
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const allDomainCards = {
|
||||||
|
...advancementChoices.domainCard,
|
||||||
|
...currentLevel.achievements.domainCards
|
||||||
|
};
|
||||||
|
const allDomainCardKeys = Object.keys(allDomainCards);
|
||||||
|
|
||||||
|
const classDomainsData = this.actor.system.class.value.system.domains.map(domain => ({
|
||||||
|
domain,
|
||||||
|
multiclass: false
|
||||||
|
}));
|
||||||
|
const multiclassDomainsData = (this.actor.system.multiclass?.value?.system?.domains ?? []).map(
|
||||||
|
domain => ({ domain, multiclass: true })
|
||||||
|
);
|
||||||
|
const domainsData = [...classDomainsData, ...multiclassDomainsData];
|
||||||
|
const multiclassDomain = this.levelup.classUpgradeChoices?.multiclass?.domain;
|
||||||
|
if (multiclassDomain) {
|
||||||
|
if (!domainsData.some(x => x.domain === multiclassDomain))
|
||||||
|
domainsData.push({ domain: multiclassDomain, multiclass: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
context.domainCards = [];
|
||||||
|
for (var key of allDomainCardKeys) {
|
||||||
|
const domainCard = allDomainCards[key];
|
||||||
|
if (domainCard.level > this.levelup.endLevel) continue;
|
||||||
|
|
||||||
|
const uuid = domainCard.data?.length > 0 ? domainCard.data[0] : domainCard.uuid;
|
||||||
|
const card = uuid ? await foundry.utils.fromUuid(uuid) : {};
|
||||||
|
|
||||||
|
context.domainCards.push({
|
||||||
|
...(card.toObject?.() ?? card),
|
||||||
|
emptySubtexts: domainsData.map(domain => {
|
||||||
|
const levelBase = domain.multiclass
|
||||||
|
? Math.ceil(this.levelup.currentLevel / 2)
|
||||||
|
: this.levelup.currentLevel;
|
||||||
|
const levelMax = domainCard.secondaryData?.limit
|
||||||
|
? Math.min(domainCard.secondaryData.limit, levelBase)
|
||||||
|
: levelBase;
|
||||||
|
|
||||||
|
return game.i18n.format('DAGGERHEART.Application.LevelUp.Selections.emptyDomainCardHint', {
|
||||||
|
domain: game.i18n.localize(domains[domain.domain].label),
|
||||||
|
level: levelMax
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
path: domainCard.data
|
||||||
|
? `${domainCard.path}.data`
|
||||||
|
: `levels.${domainCard.level}.achievements.domainCards.${key}.uuid`,
|
||||||
|
limit: domainCard.secondaryData?.limit ?? null,
|
||||||
|
compendium: 'domains'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const subclassSelections = advancementChoices.subclass?.flatMap(x => x.data) ?? [];
|
||||||
|
const possibleSubclasses = [this.actor.system.class.subclass];
|
||||||
|
if (this.actor.system.multiclass?.subclass) {
|
||||||
|
possibleSubclasses.push(this.actor.system.multiclass.subclass);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.subclassCards = [];
|
||||||
|
if (advancementChoices.subclass?.length > 0) {
|
||||||
|
const featureStateIncrease = Object.values(this.levelup.levels).reduce((acc, level) => {
|
||||||
|
acc += Object.values(level.choices).filter(choice => {
|
||||||
|
return Object.values(choice).every(checkbox => checkbox.type === 'subclass');
|
||||||
|
}).length;
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
for (var subclass of possibleSubclasses) {
|
||||||
|
const choice =
|
||||||
|
advancementChoices.subclass.find(x => x.data[0] === subclass.uuid) ??
|
||||||
|
advancementChoices.subclass.find(x => x.data.length === 0);
|
||||||
|
const featureState = subclass.system.featureState + featureStateIncrease;
|
||||||
|
const data = await foundry.utils.fromUuid(subclass.uuid);
|
||||||
|
context.subclassCards.push({
|
||||||
|
...data.toObject(),
|
||||||
|
path: choice?.path,
|
||||||
|
uuid: data.uuid,
|
||||||
|
selected: subclassSelections.includes(subclass.uuid),
|
||||||
|
featureState: featureState,
|
||||||
|
featureLabel: game.i18n.localize(subclassFeatureLabels[featureState]),
|
||||||
|
isMulticlass: subclass.system.isMulticlass ? 'true' : 'false'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const multiclasses = Object.values(advancementChoices.multiclass ?? {});
|
||||||
|
if (multiclasses?.[0]) {
|
||||||
|
const data = multiclasses[0];
|
||||||
|
const multiclass = data.data.length > 0 ? await foundry.utils.fromUuid(data.data[0]) : {};
|
||||||
|
|
||||||
|
context.multiclass = {
|
||||||
|
...data,
|
||||||
|
...(multiclass.toObject?.() ?? multiclass),
|
||||||
|
uuid: multiclass.uuid,
|
||||||
|
domains:
|
||||||
|
multiclass?.system?.domains.map(key => {
|
||||||
|
const domain = domains[key];
|
||||||
|
const alreadySelected = this.actor.system.class.value.system.domains.includes(key);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...domain,
|
||||||
|
selected: key === data.secondaryData.domain,
|
||||||
|
disabled:
|
||||||
|
(data.secondaryData.domain && key !== data.secondaryData.domain) ||
|
||||||
|
alreadySelected
|
||||||
|
};
|
||||||
|
}) ?? [],
|
||||||
|
subclasses:
|
||||||
|
multiclass?.system?.subclasses.map(subclass => ({
|
||||||
|
...subclass,
|
||||||
|
uuid: subclass.uuid,
|
||||||
|
selected: data.secondaryData.subclass === subclass.uuid,
|
||||||
|
disabled: data.secondaryData.subclass && data.secondaryData.subclass !== subclass.uuid
|
||||||
|
})) ?? [],
|
||||||
|
compendium: 'classes',
|
||||||
|
limit: 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'summary':
|
||||||
|
const { current: currentActorLevel, changed: changedActorLevel } = this.actor.system.levelData.level;
|
||||||
|
const actorArmor = this.actor.system.armor;
|
||||||
|
const levelKeys = Object.keys(this.levelup.levels);
|
||||||
|
let achivementProficiency = 0;
|
||||||
|
const achievementCards = [];
|
||||||
|
let achievementExperiences = [];
|
||||||
|
for (var levelKey of levelKeys) {
|
||||||
|
const level = this.levelup.levels[levelKey];
|
||||||
|
if (Number(levelKey) < this.levelup.startLevel) continue;
|
||||||
|
|
||||||
|
achivementProficiency += level.achievements.proficiency ?? 0;
|
||||||
|
const cards = level.achievements.domainCards ? Object.values(level.achievements.domainCards) : null;
|
||||||
|
if (cards) {
|
||||||
|
for (var card of cards) {
|
||||||
|
const itemCard = await foundry.utils.fromUuid(card.uuid);
|
||||||
|
achievementCards.push(itemCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
achievementExperiences = level.achievements.experiences
|
||||||
|
? Object.values(level.achievements.experiences).reduce((acc, experience) => {
|
||||||
|
if (experience.name) acc.push(experience);
|
||||||
|
return acc;
|
||||||
|
}, [])
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
context.achievements = {
|
||||||
|
proficiency: {
|
||||||
|
old: this.actor.system.proficiency.total,
|
||||||
|
new: this.actor.system.proficiency.total + achivementProficiency,
|
||||||
|
shown: achivementProficiency > 0
|
||||||
|
},
|
||||||
|
damageThresholds: {
|
||||||
|
major: {
|
||||||
|
old: this.actor.system.damageThresholds.major,
|
||||||
|
new: this.actor.system.damageThresholds.major + changedActorLevel - currentActorLevel
|
||||||
|
},
|
||||||
|
severe: {
|
||||||
|
old: this.actor.system.damageThresholds.severe,
|
||||||
|
new:
|
||||||
|
this.actor.system.damageThresholds.severe +
|
||||||
|
(actorArmor
|
||||||
|
? changedActorLevel - currentActorLevel
|
||||||
|
: (changedActorLevel - currentActorLevel) * 2)
|
||||||
|
},
|
||||||
|
unarmored: !actorArmor
|
||||||
|
},
|
||||||
|
domainCards: {
|
||||||
|
values: achievementCards,
|
||||||
|
shown: achievementCards.length > 0
|
||||||
|
},
|
||||||
|
experiences: {
|
||||||
|
values: achievementExperiences,
|
||||||
|
shown: achievementExperiences.length > 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const advancement = {};
|
||||||
|
for (var levelKey of levelKeys) {
|
||||||
|
const level = this.levelup.levels[levelKey];
|
||||||
|
if (Number(levelKey) < this.levelup.startLevel) continue;
|
||||||
|
|
||||||
|
for (var choiceKey of Object.keys(level.choices)) {
|
||||||
|
const choice = level.choices[choiceKey];
|
||||||
|
for (var checkbox of Object.values(choice)) {
|
||||||
|
switch (choiceKey) {
|
||||||
|
case 'proficiency':
|
||||||
|
case 'hitPoint':
|
||||||
|
case 'stress':
|
||||||
|
case 'evasion':
|
||||||
|
advancement[choiceKey] = advancement[choiceKey]
|
||||||
|
? advancement[choiceKey] + Number(checkbox.value)
|
||||||
|
: Number(checkbox.value);
|
||||||
|
break;
|
||||||
|
case 'trait':
|
||||||
|
if (!advancement[choiceKey]) advancement[choiceKey] = {};
|
||||||
|
for (var traitKey of checkbox.data) {
|
||||||
|
if (!advancement[choiceKey][traitKey]) advancement[choiceKey][traitKey] = 0;
|
||||||
|
advancement[choiceKey][traitKey] += 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'domainCard':
|
||||||
|
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||||
|
if (checkbox.data.length === 1) {
|
||||||
|
const choiceItem = await foundry.utils.fromUuid(checkbox.data[0]);
|
||||||
|
advancement[choiceKey].push(choiceItem.toObject());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'experience':
|
||||||
|
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||||
|
const data = checkbox.data.map(data => {
|
||||||
|
const experience = Object.keys(this.actor.system.experiences).find(
|
||||||
|
x => x === data
|
||||||
|
);
|
||||||
|
return this.actor.system.experiences[experience]?.description ?? '';
|
||||||
|
});
|
||||||
|
advancement[choiceKey].push({ data: data, value: checkbox.value });
|
||||||
|
break;
|
||||||
|
case 'subclass':
|
||||||
|
if (checkbox.data[0]) {
|
||||||
|
const subclassItem = await foundry.utils.fromUuid(checkbox.data[0]);
|
||||||
|
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||||
|
advancement[choiceKey].push({
|
||||||
|
...subclassItem.toObject(),
|
||||||
|
featureLabel: game.i18n.localize(
|
||||||
|
subclassFeatureLabels[Number(checkbox.secondaryData.featureState)]
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'multiclass':
|
||||||
|
const multiclassItem = await foundry.utils.fromUuid(checkbox.data[0]);
|
||||||
|
const subclass = multiclassItem
|
||||||
|
? await foundry.utils.fromUuid(checkbox.secondaryData.subclass)
|
||||||
|
: null;
|
||||||
|
advancement[choiceKey] = multiclassItem
|
||||||
|
? {
|
||||||
|
...multiclassItem.toObject(),
|
||||||
|
domain: checkbox.secondaryData.domain
|
||||||
|
? game.i18n.localize(domains[checkbox.secondaryData.domain].label)
|
||||||
|
: null,
|
||||||
|
subclass: subclass ? subclass.name : null
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.advancements = {
|
||||||
|
statistics: {
|
||||||
|
proficiency: {
|
||||||
|
old: context.achievements.proficiency.new,
|
||||||
|
new: context.achievements.proficiency.new + (advancement.proficiency ?? 0)
|
||||||
|
},
|
||||||
|
hitPoints: {
|
||||||
|
old: this.actor.system.resources.hitPoints.maxTotal,
|
||||||
|
new: this.actor.system.resources.hitPoints.maxTotal + (advancement.hitPoint ?? 0)
|
||||||
|
},
|
||||||
|
stress: {
|
||||||
|
old: this.actor.system.resources.stress.maxTotal,
|
||||||
|
new: this.actor.system.resources.stress.maxTotal + (advancement.stress ?? 0)
|
||||||
|
},
|
||||||
|
evasion: {
|
||||||
|
old: this.actor.system.evasion.total,
|
||||||
|
new: this.actor.system.evasion.total + (advancement.evasion ?? 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
traits: Object.keys(this.actor.system.traits).reduce((acc, traitKey) => {
|
||||||
|
if (advancement.trait?.[traitKey]) {
|
||||||
|
if (!acc) acc = {};
|
||||||
|
acc[traitKey] = {
|
||||||
|
label: game.i18n.localize(abilities[traitKey].label),
|
||||||
|
old: this.actor.system.traits[traitKey].total,
|
||||||
|
new: this.actor.system.traits[traitKey].total + advancement.trait[traitKey]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, null),
|
||||||
|
domainCards: advancement.domainCard ?? [],
|
||||||
|
experiences:
|
||||||
|
advancement.experience?.flatMap(x => x.data.map(data => ({ name: data, modifier: x.value }))) ??
|
||||||
|
[],
|
||||||
|
multiclass: advancement.multiclass,
|
||||||
|
subclass: advancement.subclass
|
||||||
|
};
|
||||||
|
|
||||||
|
context.advancements.statistics.proficiency.shown =
|
||||||
|
context.advancements.statistics.proficiency.new > context.advancements.statistics.proficiency.old;
|
||||||
|
context.advancements.statistics.hitPoints.shown =
|
||||||
|
context.advancements.statistics.hitPoints.new > context.advancements.statistics.hitPoints.old;
|
||||||
|
context.advancements.statistics.stress.shown =
|
||||||
|
context.advancements.statistics.stress.new > context.advancements.statistics.stress.old;
|
||||||
|
context.advancements.statistics.evasion.shown =
|
||||||
|
context.advancements.statistics.evasion.new > context.advancements.statistics.evasion.old;
|
||||||
|
context.advancements.statistics.shown =
|
||||||
|
context.advancements.statistics.proficiency.shown ||
|
||||||
|
context.advancements.statistics.hitPoints.shown ||
|
||||||
|
context.advancements.statistics.stress.shown ||
|
||||||
|
context.advancements.statistics.evasion.shown;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
163
module/applications/levelup/companionLevelup.mjs
Normal file
163
module/applications/levelup/companionLevelup.mjs
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
import BaseLevelUp from './levelup.mjs';
|
||||||
|
import { defaultCompanionTier, LevelOptionType } from '../../data/levelTier.mjs';
|
||||||
|
import { DhLevelup } from '../../data/levelup.mjs';
|
||||||
|
import { diceTypes, range } from '../../config/generalConfig.mjs';
|
||||||
|
|
||||||
|
export default class DhCompanionLevelUp extends BaseLevelUp {
|
||||||
|
constructor(actor) {
|
||||||
|
super(actor);
|
||||||
|
|
||||||
|
this.levelTiers = this.addBonusChoices(defaultCompanionTier);
|
||||||
|
const playerLevelupData = actor.system.levelData;
|
||||||
|
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData));
|
||||||
|
}
|
||||||
|
|
||||||
|
async _preparePartContext(partId, context) {
|
||||||
|
await super._preparePartContext(partId, context);
|
||||||
|
|
||||||
|
const currentLevel = this.levelup.levels[this.levelup.currentLevel];
|
||||||
|
switch (partId) {
|
||||||
|
case 'selections':
|
||||||
|
const advancementChoices = Object.keys(currentLevel.choices).reduce((acc, choiceKey) => {
|
||||||
|
Object.keys(currentLevel.choices[choiceKey]).forEach(checkboxNr => {
|
||||||
|
const checkbox = currentLevel.choices[choiceKey][checkboxNr];
|
||||||
|
const data = {
|
||||||
|
...checkbox,
|
||||||
|
path: `levels.${this.levelup.currentLevel}.choices.${choiceKey}.${checkboxNr}`,
|
||||||
|
level: this.levelup.currentLevel
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!acc[choiceKey]) acc[choiceKey] = [];
|
||||||
|
acc[choiceKey].push(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const experienceIncreases = Object.values(advancementChoices.experience ?? {});
|
||||||
|
const experienceIncreaseValues = experienceIncreases
|
||||||
|
.filter(exp => exp.data.length > 0)
|
||||||
|
.flatMap(exp =>
|
||||||
|
exp.data.map(data => {
|
||||||
|
const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
|
||||||
|
return this.actor.system.experiences[experience].name;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
context.experienceIncreases = {
|
||||||
|
values: experienceIncreaseValues,
|
||||||
|
active: experienceIncreases.length > 0,
|
||||||
|
progress: {
|
||||||
|
selected: experienceIncreaseValues.length,
|
||||||
|
max: experienceIncreases.reduce((acc, exp) => acc + exp.amount, 0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
context.newExperiences = Object.keys(currentLevel.achievements.experiences).map(key => {
|
||||||
|
const experience = currentLevel.achievements.experiences[key];
|
||||||
|
return {
|
||||||
|
...experience,
|
||||||
|
level: this.levelup.currentLevel,
|
||||||
|
key: key
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
context.vicious = advancementChoices.vicious ? Object.values(advancementChoices.vicious) : null;
|
||||||
|
context.viciousChoices = {
|
||||||
|
damage: game.i18n.localize('DAGGERHEART.Application.LevelUp.Selections.viciousDamage'),
|
||||||
|
range: game.i18n.localize('DAGGERHEART.Application.LevelUp.Selections.viciousRange')
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'summary':
|
||||||
|
const levelKeys = Object.keys(this.levelup.levels);
|
||||||
|
const actorDamageDice = this.actor.system.attack.damage.parts[0].value.dice;
|
||||||
|
const actorRange = this.actor.system.attack.range;
|
||||||
|
const advancement = {};
|
||||||
|
for (var levelKey of levelKeys) {
|
||||||
|
const level = this.levelup.levels[levelKey];
|
||||||
|
if (Number(levelKey) < this.levelup.startLevel) continue;
|
||||||
|
|
||||||
|
for (var choiceKey of Object.keys(level.choices)) {
|
||||||
|
const choice = level.choices[choiceKey];
|
||||||
|
for (var checkbox of Object.values(choice)) {
|
||||||
|
switch (choiceKey) {
|
||||||
|
case 'stress':
|
||||||
|
case 'evasion':
|
||||||
|
advancement[choiceKey] = advancement[choiceKey]
|
||||||
|
? advancement[choiceKey] + Number(checkbox.value)
|
||||||
|
: Number(checkbox.value);
|
||||||
|
break;
|
||||||
|
case 'experience':
|
||||||
|
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||||
|
const data = checkbox.data.map(data => {
|
||||||
|
const experience = Object.keys(this.actor.system.experiences).find(
|
||||||
|
x => x === data
|
||||||
|
);
|
||||||
|
return this.actor.system.experiences[experience]?.name ?? '';
|
||||||
|
});
|
||||||
|
advancement[choiceKey].push({ data: data, value: checkbox.value });
|
||||||
|
break;
|
||||||
|
case 'vicious':
|
||||||
|
if (!advancement[choiceKey]) advancement[choiceKey] = { damage: null, range: null };
|
||||||
|
const isDamage = checkbox.data[0] === 'damage';
|
||||||
|
const options = isDamage ? diceTypes : range;
|
||||||
|
const keys = Object.keys(options);
|
||||||
|
const actorKey = keys.indexOf(isDamage ? actorDamageDice : actorRange);
|
||||||
|
const currentIndex = advancement[choiceKey][checkbox.data[0]]
|
||||||
|
? keys.indexOf(advancement[choiceKey][checkbox.data[0]])
|
||||||
|
: actorKey;
|
||||||
|
advancement[choiceKey][checkbox.data[0]] =
|
||||||
|
options[keys[Math.min(currentIndex + 1, keys.length - 1)]];
|
||||||
|
default:
|
||||||
|
if (!advancement.simple) advancement.simple = {};
|
||||||
|
advancement.simple[choiceKey] = game.i18n.localize(
|
||||||
|
LevelOptionType[checkbox.type].label
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.advancements = {
|
||||||
|
statistics: {
|
||||||
|
stress: {
|
||||||
|
old: this.actor.system.resources.stress.maxTotal,
|
||||||
|
new: this.actor.system.resources.stress.maxTotal + (advancement.stress ?? 0)
|
||||||
|
},
|
||||||
|
evasion: {
|
||||||
|
old: this.actor.system.evasion.total,
|
||||||
|
new: this.actor.system.evasion.total + (advancement.evasion ?? 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
experiences:
|
||||||
|
advancement.experience?.flatMap(x => x.data.map(data => ({ name: data, modifier: x.value }))) ??
|
||||||
|
[],
|
||||||
|
vicious: {
|
||||||
|
damage: advancement.vicious?.damage
|
||||||
|
? {
|
||||||
|
old: actorDamageDice,
|
||||||
|
new: advancement.vicious.damage
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
range: advancement.vicious?.range
|
||||||
|
? {
|
||||||
|
old: game.i18n.localize(`DAGGERHEART.Range.${actorRange}.name`),
|
||||||
|
new: game.i18n.localize(advancement.vicious.range.label)
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
},
|
||||||
|
simple: advancement.simple ?? {}
|
||||||
|
};
|
||||||
|
|
||||||
|
context.advancements.statistics.stress.shown =
|
||||||
|
context.advancements.statistics.stress.new > context.advancements.statistics.stress.old;
|
||||||
|
context.advancements.statistics.evasion.shown =
|
||||||
|
context.advancements.statistics.evasion.new > context.advancements.statistics.evasion.old;
|
||||||
|
context.advancements.statistics.shown =
|
||||||
|
context.advancements.statistics.stress.shown || context.advancements.statistics.evasion.shown;
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { abilities, subclassFeatureLabels } from '../config/actorConfig.mjs';
|
import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs';
|
||||||
import { domains } from '../config/domainConfig.mjs';
|
import { domains } from '../../config/domainConfig.mjs';
|
||||||
import { DhLevelup } from '../data/levelup.mjs';
|
import { getDeleteKeys, tagifyElement } from '../../helpers/utils.mjs';
|
||||||
import { getDeleteKeys, tagifyElement } from '../helpers/utils.mjs';
|
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
|
@ -10,10 +9,6 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
super({});
|
super({});
|
||||||
|
|
||||||
this.actor = actor;
|
this.actor = actor;
|
||||||
this.levelTiers = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers);
|
|
||||||
|
|
||||||
const playerLevelupData = actor.system.levelData;
|
|
||||||
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData, actor.system.level));
|
|
||||||
|
|
||||||
this._dragDrop = this._createDragDropHandlers();
|
this._dragDrop = this._createDragDropHandlers();
|
||||||
this.tabGroups.primary = 'advancements';
|
this.tabGroups.primary = 'advancements';
|
||||||
|
|
@ -81,6 +76,21 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
addBonusChoices(levelTiers) {
|
||||||
|
for (var tierKey in levelTiers.tiers) {
|
||||||
|
const tier = levelTiers.tiers[tierKey];
|
||||||
|
tier.maxSelections = [...Array(tier.levels.end - tier.levels.start + 1).keys()].reduce((acc, index) => {
|
||||||
|
const level = tier.levels.start + index;
|
||||||
|
const bonus = this.actor.system.levelData.level.bonuses[level];
|
||||||
|
acc[level] = tier.availableOptions + (bonus ?? 0);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return levelTiers;
|
||||||
|
}
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.levelup = this.levelup;
|
context.levelup = this.levelup;
|
||||||
|
|
@ -118,181 +128,6 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
context.tabs.advancements.progress = { selected: selections, max: currentLevel.maxSelections };
|
context.tabs.advancements.progress = { selected: selections, max: currentLevel.maxSelections };
|
||||||
context.showTabs = this.tabGroups.primary !== 'summary';
|
context.showTabs = this.tabGroups.primary !== 'summary';
|
||||||
break;
|
break;
|
||||||
case 'selections':
|
|
||||||
const advancementChoices = Object.keys(currentLevel.choices).reduce((acc, choiceKey) => {
|
|
||||||
Object.keys(currentLevel.choices[choiceKey]).forEach(checkboxNr => {
|
|
||||||
const checkbox = currentLevel.choices[choiceKey][checkboxNr];
|
|
||||||
const data = {
|
|
||||||
...checkbox,
|
|
||||||
path: `levels.${this.levelup.currentLevel}.choices.${choiceKey}.${checkboxNr}`,
|
|
||||||
level: this.levelup.currentLevel
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!acc[choiceKey]) acc[choiceKey] = [];
|
|
||||||
acc[choiceKey].push(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const traits = Object.values(advancementChoices.trait ?? {});
|
|
||||||
const traitValues = traits.filter(trait => trait.data.length > 0).flatMap(trait => trait.data);
|
|
||||||
context.traits = {
|
|
||||||
values: traitValues,
|
|
||||||
active: traits.length > 0,
|
|
||||||
progress: {
|
|
||||||
selected: traitValues.length,
|
|
||||||
max: traits.reduce((acc, exp) => acc + exp.amount, 0)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const experienceIncreases = Object.values(advancementChoices.experience ?? {});
|
|
||||||
const experienceIncreaseValues = experienceIncreases
|
|
||||||
.filter(exp => exp.data.length > 0)
|
|
||||||
.flatMap(exp =>
|
|
||||||
exp.data.map(data => {
|
|
||||||
const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
|
|
||||||
return this.actor.system.experiences[experience].description;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
context.experienceIncreases = {
|
|
||||||
values: experienceIncreaseValues,
|
|
||||||
active: experienceIncreases.length > 0,
|
|
||||||
progress: {
|
|
||||||
selected: experienceIncreaseValues.length,
|
|
||||||
max: experienceIncreases.reduce((acc, exp) => acc + exp.amount, 0)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
context.newExperiences = Object.keys(currentLevel.achievements.experiences).map(key => {
|
|
||||||
const experience = currentLevel.achievements.experiences[key];
|
|
||||||
return {
|
|
||||||
...experience,
|
|
||||||
level: this.levelup.currentLevel,
|
|
||||||
key: key
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const allDomainCards = {
|
|
||||||
...advancementChoices.domainCard,
|
|
||||||
...currentLevel.achievements.domainCards
|
|
||||||
};
|
|
||||||
const allDomainCardKeys = Object.keys(allDomainCards);
|
|
||||||
|
|
||||||
const classDomainsData = this.actor.system.class.value.system.domains.map(domain => ({
|
|
||||||
domain,
|
|
||||||
multiclass: false
|
|
||||||
}));
|
|
||||||
const multiclassDomainsData = (this.actor.system.multiclass?.value?.system?.domains ?? []).map(
|
|
||||||
domain => ({ domain, multiclass: true })
|
|
||||||
);
|
|
||||||
const domainsData = [...classDomainsData, ...multiclassDomainsData];
|
|
||||||
const multiclassDomain = this.levelup.classUpgradeChoices?.multiclass?.domain;
|
|
||||||
if (multiclassDomain) {
|
|
||||||
if (!domainsData.some(x => x.domain === multiclassDomain))
|
|
||||||
domainsData.push({ domain: multiclassDomain, multiclass: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
context.domainCards = [];
|
|
||||||
for (var key of allDomainCardKeys) {
|
|
||||||
const domainCard = allDomainCards[key];
|
|
||||||
if (domainCard.level > this.levelup.endLevel) continue;
|
|
||||||
|
|
||||||
const uuid = domainCard.data?.length > 0 ? domainCard.data[0] : domainCard.uuid;
|
|
||||||
const card = uuid ? await foundry.utils.fromUuid(uuid) : {};
|
|
||||||
|
|
||||||
context.domainCards.push({
|
|
||||||
...(card.toObject?.() ?? card),
|
|
||||||
emptySubtexts: domainsData.map(domain => {
|
|
||||||
const levelBase = domain.multiclass
|
|
||||||
? Math.ceil(this.levelup.currentLevel / 2)
|
|
||||||
: this.levelup.currentLevel;
|
|
||||||
const levelMax = domainCard.secondaryData?.limit
|
|
||||||
? Math.min(domainCard.secondaryData.limit, levelBase)
|
|
||||||
: levelBase;
|
|
||||||
|
|
||||||
return game.i18n.format('DAGGERHEART.Application.LevelUp.Selections.emptyDomainCardHint', {
|
|
||||||
domain: game.i18n.localize(domains[domain.domain].label),
|
|
||||||
level: levelMax
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
path: domainCard.data
|
|
||||||
? `${domainCard.path}.data`
|
|
||||||
: `levels.${domainCard.level}.achievements.domainCards.${key}.uuid`,
|
|
||||||
limit: domainCard.secondaryData?.limit ?? null,
|
|
||||||
compendium: 'domains'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const subclassSelections = advancementChoices.subclass?.flatMap(x => x.data) ?? [];
|
|
||||||
const possibleSubclasses = [this.actor.system.class.subclass];
|
|
||||||
if (this.actor.system.multiclass?.subclass) {
|
|
||||||
possibleSubclasses.push(this.actor.system.multiclass.subclass);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.subclassCards = [];
|
|
||||||
if (advancementChoices.subclass?.length > 0) {
|
|
||||||
const featureStateIncrease = Object.values(this.levelup.levels).reduce((acc, level) => {
|
|
||||||
acc += Object.values(level.choices).filter(choice => {
|
|
||||||
return Object.values(choice).every(checkbox => checkbox.type === 'subclass');
|
|
||||||
}).length;
|
|
||||||
return acc;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
for (var subclass of possibleSubclasses) {
|
|
||||||
const choice =
|
|
||||||
advancementChoices.subclass.find(x => x.data[0] === subclass.uuid) ??
|
|
||||||
advancementChoices.subclass.find(x => x.data.length === 0);
|
|
||||||
const featureState = subclass.system.featureState + featureStateIncrease;
|
|
||||||
const data = await foundry.utils.fromUuid(subclass.uuid);
|
|
||||||
context.subclassCards.push({
|
|
||||||
...data.toObject(),
|
|
||||||
path: choice?.path,
|
|
||||||
uuid: data.uuid,
|
|
||||||
selected: subclassSelections.includes(subclass.uuid),
|
|
||||||
featureState: featureState,
|
|
||||||
featureLabel: game.i18n.localize(subclassFeatureLabels[featureState]),
|
|
||||||
isMulticlass: subclass.system.isMulticlass ? 'true' : 'false'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const multiclasses = Object.values(advancementChoices.multiclass ?? {});
|
|
||||||
if (multiclasses?.[0]) {
|
|
||||||
const data = multiclasses[0];
|
|
||||||
const multiclass = data.data.length > 0 ? await foundry.utils.fromUuid(data.data[0]) : {};
|
|
||||||
|
|
||||||
context.multiclass = {
|
|
||||||
...data,
|
|
||||||
...(multiclass.toObject?.() ?? multiclass),
|
|
||||||
uuid: multiclass.uuid,
|
|
||||||
domains:
|
|
||||||
multiclass?.system?.domains.map(key => {
|
|
||||||
const domain = domains[key];
|
|
||||||
const alreadySelected = this.actor.system.class.value.system.domains.includes(key);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...domain,
|
|
||||||
selected: key === data.secondaryData.domain,
|
|
||||||
disabled:
|
|
||||||
(data.secondaryData.domain && key !== data.secondaryData.domain) ||
|
|
||||||
alreadySelected
|
|
||||||
};
|
|
||||||
}) ?? [],
|
|
||||||
subclasses:
|
|
||||||
multiclass?.system?.subclasses.map(subclass => ({
|
|
||||||
...subclass,
|
|
||||||
uuid: subclass.uuid,
|
|
||||||
selected: data.secondaryData.subclass === subclass.uuid,
|
|
||||||
disabled: data.secondaryData.subclass && data.secondaryData.subclass !== subclass.uuid
|
|
||||||
})) ?? [],
|
|
||||||
compendium: 'classes',
|
|
||||||
limit: 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'summary':
|
|
||||||
const { current: currentActorLevel, changed: changedActorLevel } = this.actor.system.levelData.level;
|
const { current: currentActorLevel, changed: changedActorLevel } = this.actor.system.levelData.level;
|
||||||
const actorArmor = this.actor.system.armor;
|
const actorArmor = this.actor.system.armor;
|
||||||
const levelKeys = Object.keys(this.levelup.levels);
|
const levelKeys = Object.keys(this.levelup.levels);
|
||||||
|
|
@ -516,7 +351,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
experienceIncreaseTagify,
|
experienceIncreaseTagify,
|
||||||
Object.keys(this.actor.system.experiences).reduce((acc, id) => {
|
Object.keys(this.actor.system.experiences).reduce((acc, id) => {
|
||||||
const experience = this.actor.system.experiences[id];
|
const experience = this.actor.system.experiences[id];
|
||||||
acc[id] = { label: experience.description };
|
acc[id] = { label: experience.name };
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {}),
|
}, {}),
|
||||||
|
|
@ -594,8 +429,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const cardExistsInCharacter = this.actor.items.find(x => x.name === item.name); // Any other way to check? The item is a copy so different ids
|
||||||
Object.values(this.levelup.levels).some(level => {
|
const cardExistsInLevelup = Object.values(this.levelup.levels).some(level => {
|
||||||
const achievementExists = Object.values(level.achievements.domainCards).some(
|
const achievementExists = Object.values(level.achievements.domainCards).some(
|
||||||
card => card.uuid === item.uuid
|
card => card.uuid === item.uuid
|
||||||
);
|
);
|
||||||
|
|
@ -606,8 +441,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
});
|
});
|
||||||
|
|
||||||
return achievementExists || advancementExists;
|
return achievementExists || advancementExists;
|
||||||
})
|
});
|
||||||
) {
|
if (cardExistsInCharacter || cardExistsInLevelup) {
|
||||||
ui.notifications.error(
|
ui.notifications.error(
|
||||||
game.i18n.localize('DAGGERHEART.Application.LevelUp.notifications.error.domainCardDuplicate')
|
game.i18n.localize('DAGGERHEART.Application.LevelUp.notifications.error.domainCardDuplicate')
|
||||||
);
|
);
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
/** NOT USED ANYMORE - TO BE DELETED **/
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
export default class NpcRollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class NpcRollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
import DHDamageRoll from '../data/chat-message/damageRoll.mjs';
|
||||||
import D20RollDialog from '../dialogs/d20RollDialog.mjs';
|
import D20RollDialog from '../dialogs/d20RollDialog.mjs';
|
||||||
import DamageDialog from '../dialogs/damageDialog.mjs';
|
import DamageDialog from '../dialogs/damageDialog.mjs';
|
||||||
|
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
- Damage & other resources roll
|
- Damage & other resources roll
|
||||||
|
|
@ -7,6 +9,7 @@ import DamageDialog from '../dialogs/damageDialog.mjs';
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class DHRoll extends Roll {
|
export class DHRoll extends Roll {
|
||||||
|
baseTerms = [];
|
||||||
constructor(formula, data, options) {
|
constructor(formula, data, options) {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
}
|
}
|
||||||
|
|
@ -36,6 +39,7 @@ export class DHRoll extends Roll {
|
||||||
if (config.dialog.configure !== false) {
|
if (config.dialog.configure !== false) {
|
||||||
// Open Roll Dialog
|
// Open Roll Dialog
|
||||||
const DialogClass = config.dialog?.class ?? this.DefaultDialog;
|
const DialogClass = config.dialog?.class ?? this.DefaultDialog;
|
||||||
|
console.log(roll, config);
|
||||||
const configDialog = await DialogClass.configure(roll, config, message);
|
const configDialog = await DialogClass.configure(roll, config, message);
|
||||||
if (!configDialog) return;
|
if (!configDialog) return;
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +62,7 @@ export class DHRoll extends Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Chat Message
|
// Create Chat Message
|
||||||
if (message.data) {
|
if (config.source?.message) {
|
||||||
} else {
|
} else {
|
||||||
const messageData = {};
|
const messageData = {};
|
||||||
config.message = await this.toMessage(roll, config);
|
config.message = await this.toMessage(roll, config);
|
||||||
|
|
@ -93,12 +97,31 @@ export class DHRoll extends Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
static applyKeybindings(config) {
|
static applyKeybindings(config) {
|
||||||
|
if (config.event)
|
||||||
config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey);
|
config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formatModifier(modifier) {
|
||||||
|
const numTerm = modifier < 0 ? '-' : '+';
|
||||||
|
return [
|
||||||
|
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
||||||
|
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFaces(faces) {
|
||||||
|
return Number(faces.startsWith('d') ? faces.replace('d', '') : faces);
|
||||||
|
}
|
||||||
|
|
||||||
constructFormula(config) {
|
constructFormula(config) {
|
||||||
// const formula = Roll.replaceFormulaData(this.options.roll.formula, config.data);
|
this.terms = Roll.parse(this.options.roll.formula, config.data);
|
||||||
this.terms = Roll.parse(this.options.roll.formula, config.data)
|
|
||||||
|
if (this.options.extraFormula) {
|
||||||
|
this.terms.push(
|
||||||
|
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||||
|
...this.constructor.parse(this.options.extraFormula, this.options.data)
|
||||||
|
);
|
||||||
|
}
|
||||||
return (this._formula = this.constructor.getFormula(this.terms));
|
return (this._formula = this.constructor.getFormula(this.terms));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -112,10 +135,6 @@ export class DualityDie extends foundry.dice.terms.Die {
|
||||||
export class D20Roll extends DHRoll {
|
export class D20Roll extends DHRoll {
|
||||||
constructor(formula, data = {}, options = {}) {
|
constructor(formula, data = {}, options = {}) {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
// this.createBaseDice();
|
|
||||||
// this.configureModifiers();
|
|
||||||
|
|
||||||
// this._formula = this.resetFormula();
|
|
||||||
this.constructFormula();
|
this.constructFormula();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,7 +157,7 @@ export class D20Roll extends DHRoll {
|
||||||
|
|
||||||
set d20(faces) {
|
set d20(faces) {
|
||||||
if (!(this.terms[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
if (!(this.terms[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
this.terms[0].faces = faces;
|
this.terms[0].faces = this.getFaces(faces);
|
||||||
}
|
}
|
||||||
|
|
||||||
get dAdvantage() {
|
get dAdvantage() {
|
||||||
|
|
@ -151,36 +170,82 @@ export class D20Roll extends DHRoll {
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasAdvantage() {
|
get hasAdvantage() {
|
||||||
return this.options.advantage === this.constructor.ADV_MODE.ADVANTAGE;
|
return this.options.roll.advantage === this.constructor.ADV_MODE.ADVANTAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasDisadvantage() {
|
get hasDisadvantage() {
|
||||||
return this.options.advantage === this.constructor.ADV_MODE.DISADVANTAGE;
|
return this.options.roll.advantage === this.constructor.ADV_MODE.DISADVANTAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static applyKeybindings(config) {
|
static applyKeybindings(config) {
|
||||||
const keys = {
|
let keys = {
|
||||||
|
normal: true,
|
||||||
|
advantage: false,
|
||||||
|
disadvantage: false
|
||||||
|
};
|
||||||
|
|
||||||
|
if (config.event) {
|
||||||
|
keys = {
|
||||||
normal: config.event.shiftKey || config.event.altKey || config.event.ctrlKey,
|
normal: config.event.shiftKey || config.event.altKey || config.event.ctrlKey,
|
||||||
advantage: config.event.altKey,
|
advantage: config.event.altKey,
|
||||||
disadvantage: config.event.ctrlKey
|
disadvantage: config.event.ctrlKey
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Should the roll configuration dialog be displayed?
|
// Should the roll configuration dialog be displayed?
|
||||||
config.dialog.configure ??= !Object.values(keys).some(k => k);
|
config.dialog.configure ??= !Object.values(keys).some(k => k);
|
||||||
|
|
||||||
// Determine advantage mode
|
// Determine advantage mode
|
||||||
const advantage = config.advantage || keys.advantage;
|
const advantage = config.roll.advantage === this.ADV_MODE.ADVANTAGE || keys.advantage || config.advantage;
|
||||||
const disadvantage = config.disadvantage || keys.disadvantage;
|
const disadvantage =
|
||||||
if (advantage && !disadvantage) config.advantage = this.ADV_MODE.ADVANTAGE;
|
config.roll.advantage === this.ADV_MODE.DISADVANTAGE || keys.disadvantage || config.disadvantage;
|
||||||
else if (!advantage && disadvantage) config.advantage = this.ADV_MODE.DISADVANTAGE;
|
if (advantage && !disadvantage) config.roll.advantage = this.ADV_MODE.ADVANTAGE;
|
||||||
else config.advantage = this.ADV_MODE.NORMAL;
|
else if (!advantage && disadvantage) config.roll.advantage = this.ADV_MODE.DISADVANTAGE;
|
||||||
|
else config.roll.advantage = this.ADV_MODE.NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructFormula(config) {
|
||||||
|
// this.terms = [];
|
||||||
|
this.createBaseDice();
|
||||||
|
this.configureModifiers();
|
||||||
|
this.resetFormula();
|
||||||
|
return this._formula;
|
||||||
}
|
}
|
||||||
|
|
||||||
createBaseDice() {
|
createBaseDice() {
|
||||||
if (this.terms[0] instanceof foundry.dice.terms.Die) return;
|
if (this.terms[0] instanceof foundry.dice.terms.Die) {
|
||||||
|
this.terms = [this.terms[0]];
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.terms[0] = new foundry.dice.terms.Die({ faces: 20 });
|
this.terms[0] = new foundry.dice.terms.Die({ faces: 20 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configureModifiers() {
|
||||||
|
this.applyAdvantage();
|
||||||
|
this.applyBaseBonus();
|
||||||
|
|
||||||
|
this.options.experiences?.forEach(m => {
|
||||||
|
if (this.options.data.experiences?.[m])
|
||||||
|
this.options.roll.modifiers.push({
|
||||||
|
label: this.options.data.experiences[m].name,
|
||||||
|
value: this.options.data.experiences[m].total ?? this.options.data.experiences[m].value
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.options.roll.modifiers?.forEach(m => {
|
||||||
|
this.terms.push(...this.formatModifier(m.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.baseTerms = foundry.utils.deepClone(this.terms);
|
||||||
|
|
||||||
|
if (this.options.extraFormula) {
|
||||||
|
this.terms.push(
|
||||||
|
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||||
|
...this.constructor.parse(this.options.extraFormula, this.options.data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
applyAdvantage() {
|
applyAdvantage() {
|
||||||
this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m));
|
this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m));
|
||||||
if (!this.hasAdvantage && !this.hasDisadvantage) this.number = 1;
|
if (!this.hasAdvantage && !this.hasDisadvantage) this.number = 1;
|
||||||
|
|
@ -190,45 +255,26 @@ export class D20Roll extends DHRoll {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trait bonus != Adversary
|
|
||||||
configureModifiers() {
|
|
||||||
this.applyAdvantage();
|
|
||||||
// this.options.roll.modifiers = [];
|
|
||||||
this.applyBaseBonus();
|
|
||||||
|
|
||||||
this.options.experiences?.forEach(m => {
|
|
||||||
if (this.options.data.experiences?.[m])
|
|
||||||
this.options.roll.modifiers.push({
|
|
||||||
label: this.options.data.experiences[m].name,
|
|
||||||
value: this.options.data.experiences[m].total ?? this.options.data.experiences[m].value
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.options.roll.modifiers?.forEach(m => {
|
|
||||||
this.terms.push(...this.formatModifier(m.value));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.options.extraFormula) {
|
|
||||||
this.terms.push(
|
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
|
||||||
...this.constructor.parse(this.options.extraFormula, this.getRollData())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// this.resetFormula();
|
|
||||||
}
|
|
||||||
|
|
||||||
constructFormula(config) {
|
|
||||||
this.terms = [];
|
|
||||||
this.createBaseDice();
|
|
||||||
this.configureModifiers();
|
|
||||||
this.resetFormula();
|
|
||||||
return this._formula;
|
|
||||||
}
|
|
||||||
|
|
||||||
applyBaseBonus() {
|
applyBaseBonus() {
|
||||||
this.options.roll.modifiers = [{
|
this.options.roll.modifiers = [];
|
||||||
|
if (!this.options.roll.bonus) return;
|
||||||
|
this.options.roll.modifiers.push({
|
||||||
label: 'Bonus to Hit',
|
label: 'Bonus to Hit',
|
||||||
value: Roll.replaceFormulaData('@attackBonus', this.data)
|
value: this.options.roll.bonus
|
||||||
}];
|
// value: Roll.replaceFormulaData('@attackBonus', this.data)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||||
|
if (config.evaluate !== false) await roll.evaluate();
|
||||||
|
const advantageState =
|
||||||
|
config.roll.advantage == this.ADV_MODE.ADVANTAGE
|
||||||
|
? true
|
||||||
|
: config.roll.advantage == this.ADV_MODE.DISADVANTAGE
|
||||||
|
? false
|
||||||
|
: null;
|
||||||
|
setDiceSoNiceForDualityRoll(roll, advantageState);
|
||||||
|
this.postEvaluate(roll, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
static postEvaluate(roll, config = {}) {
|
static postEvaluate(roll, config = {}) {
|
||||||
|
|
@ -238,25 +284,30 @@ export class D20Roll extends DHRoll {
|
||||||
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
|
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
|
||||||
target.hit = this.isCritical || roll.total >= difficulty;
|
target.hit = this.isCritical || roll.total >= difficulty;
|
||||||
});
|
});
|
||||||
} else if (config.roll.difficulty) config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
} else if (config.roll.difficulty)
|
||||||
|
config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
||||||
config.roll.advantage = {
|
config.roll.advantage = {
|
||||||
type: config.advantage,
|
type: config.roll.advantage,
|
||||||
dice: roll.dAdvantage?.denomination,
|
dice: roll.dAdvantage?.denomination,
|
||||||
value: roll.dAdvantage?.total
|
value: roll.dAdvantage?.total
|
||||||
};
|
};
|
||||||
config.roll.modifierTotal = config.roll.modifiers.reduce((a, c) => a + Number(c.value), 0);
|
config.roll.extra = roll.dice
|
||||||
|
.filter(d => !roll.baseTerms.includes(d))
|
||||||
|
.map(d => {
|
||||||
|
return {
|
||||||
|
dice: d.denomination,
|
||||||
|
value: d.total
|
||||||
|
};
|
||||||
|
});
|
||||||
|
config.roll.modifierTotal = 0;
|
||||||
|
for (let i = 0; i < roll.terms.length; i++) {
|
||||||
|
if (
|
||||||
|
roll.terms[i] instanceof foundry.dice.terms.NumericTerm &&
|
||||||
|
!!roll.terms[i - 1] &&
|
||||||
|
roll.terms[i - 1] instanceof foundry.dice.terms.OperatorTerm
|
||||||
|
)
|
||||||
|
config.roll.modifierTotal += Number(`${roll.terms[i - 1].operator}${roll.terms[i].total}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRollData() {
|
|
||||||
return this.options.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
formatModifier(modifier) {
|
|
||||||
const numTerm = modifier < 0 ? '-' : '+';
|
|
||||||
return [
|
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
|
||||||
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resetFormula() {
|
resetFormula() {
|
||||||
|
|
@ -265,6 +316,8 @@ export class D20Roll extends DHRoll {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DualityRoll extends D20Roll {
|
export class DualityRoll extends D20Roll {
|
||||||
|
_advantageFaces = 6;
|
||||||
|
|
||||||
constructor(formula, data = {}, options = {}) {
|
constructor(formula, data = {}, options = {}) {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
}
|
}
|
||||||
|
|
@ -282,7 +335,7 @@ export class DualityRoll extends D20Roll {
|
||||||
|
|
||||||
set dHope(faces) {
|
set dHope(faces) {
|
||||||
if (!(this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
|
if (!(this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
|
||||||
this.terms[0].faces = faces;
|
this.terms[0].faces = this.getFaces(faces);
|
||||||
// this.#hopeDice = `d${face}`;
|
// this.#hopeDice = `d${face}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,7 +348,7 @@ export class DualityRoll extends D20Roll {
|
||||||
|
|
||||||
set dFear(faces) {
|
set dFear(faces) {
|
||||||
if (!(this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
|
if (!(this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
|
||||||
this.dice[1].faces = faces;
|
this.dice[1].faces = this.getFaces(faces);
|
||||||
// this.#fearDice = `d${face}`;
|
// this.#fearDice = `d${face}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,6 +356,14 @@ export class DualityRoll extends D20Roll {
|
||||||
return this.dice[2];
|
return this.dice[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get advantageFaces() {
|
||||||
|
return this._advantageFaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
set advantageFaces(faces) {
|
||||||
|
this._advantageFaces = this.getFaces(faces);
|
||||||
|
}
|
||||||
|
|
||||||
get isCritical() {
|
get isCritical() {
|
||||||
if (!this.dHope._evaluated || !this.dFear._evaluated) return;
|
if (!this.dHope._evaluated || !this.dFear._evaluated) return;
|
||||||
return this.dHope.total === this.dFear.total;
|
return this.dHope.total === this.dFear.total;
|
||||||
|
|
@ -332,25 +393,27 @@ export class DualityRoll extends D20Roll {
|
||||||
return game.i18n.localize(label);
|
return game.i18n.localize(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFormula() {}
|
||||||
|
|
||||||
createBaseDice() {
|
createBaseDice() {
|
||||||
if (
|
if (
|
||||||
this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie &&
|
this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie &&
|
||||||
this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie
|
this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie
|
||||||
)
|
) {
|
||||||
|
this.terms = [this.terms[0], this.terms[1], this.terms[2]];
|
||||||
return;
|
return;
|
||||||
if (!(this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie))
|
}
|
||||||
this.terms[0] = new CONFIG.Dice.daggerheart.DualityDie();
|
this.terms[0] = new CONFIG.Dice.daggerheart.DualityDie();
|
||||||
this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' });
|
this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' });
|
||||||
if (!(this.dice[2] instanceof CONFIG.Dice.daggerheart.DualityDie))
|
|
||||||
this.terms[2] = new CONFIG.Dice.daggerheart.DualityDie();
|
this.terms[2] = new CONFIG.Dice.daggerheart.DualityDie();
|
||||||
}
|
}
|
||||||
|
|
||||||
applyAdvantage() {
|
applyAdvantage() {
|
||||||
const dieFaces = 6,
|
const dieFaces = this.advantageFaces,
|
||||||
bardRallyFaces = this.hasBarRally,
|
bardRallyFaces = this.hasBarRally,
|
||||||
advDie = new foundry.dice.terms.Die({ faces: dieFaces });
|
advDie = new foundry.dice.terms.Die({ faces: dieFaces });
|
||||||
if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces)
|
if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces)
|
||||||
this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: (this.hasDisadvantage ? '-' : '+') }));
|
this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }));
|
||||||
if (bardRallyFaces) {
|
if (bardRallyFaces) {
|
||||||
const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces });
|
const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces });
|
||||||
if (this.hasAdvantage) {
|
if (this.hasAdvantage) {
|
||||||
|
|
@ -367,10 +430,12 @@ export class DualityRoll extends D20Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
applyBaseBonus() {
|
applyBaseBonus() {
|
||||||
this.options.roll.modifiers = [{
|
this.options.roll.modifiers = [];
|
||||||
|
if (!this.options.roll.trait) return;
|
||||||
|
this.options.roll.modifiers.push({
|
||||||
label: `DAGGERHEART.Abilities.${this.options.roll.trait}.name`,
|
label: `DAGGERHEART.Abilities.${this.options.roll.trait}.name`,
|
||||||
value: Roll.replaceFormulaData(`@traits.${this.options.roll.trait}.total`, this.data)
|
value: Roll.replaceFormulaData(`@traits.${this.options.roll.trait}.total`, this.data)
|
||||||
}];
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static postEvaluate(roll, config = {}) {
|
static postEvaluate(roll, config = {}) {
|
||||||
|
|
@ -388,7 +453,6 @@ export class DualityRoll extends D20Roll {
|
||||||
total: roll.dHope.total + roll.dFear.total,
|
total: roll.dHope.total + roll.dFear.total,
|
||||||
label: roll.totalLabel
|
label: roll.totalLabel
|
||||||
};
|
};
|
||||||
console.log(roll, config)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -404,5 +468,9 @@ export class DamageRoll extends DHRoll {
|
||||||
static async postEvaluate(roll, config = {}) {
|
static async postEvaluate(roll, config = {}) {
|
||||||
super.postEvaluate(roll, config);
|
super.postEvaluate(roll, config);
|
||||||
config.roll.type = config.type;
|
config.roll.type = config.type;
|
||||||
|
if (config.source?.message) {
|
||||||
|
const chatMessage = ui.chat.collection.get(config.source.message);
|
||||||
|
chatMessage.update({ 'system.damage': config });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
/** NOT USED ANYMORE - TO BE DELETED **/
|
||||||
|
|
||||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ export default class DhSettingsActionView extends HandlebarsApplicationMixin(App
|
||||||
}
|
}
|
||||||
|
|
||||||
static onEditImage() {
|
static onEditImage() {
|
||||||
const fp = new FilePicker({
|
const fp = new foundry.applications.apps.FilePicker.implementation({
|
||||||
current: this.img,
|
current: this.img,
|
||||||
type: 'image',
|
type: 'image',
|
||||||
callback: async path => {
|
callback: async path => {
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
import DHActionConfig from '../config/Action.mjs';
|
import DHActionConfig from '../../config/Action.mjs';
|
||||||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
import DaggerheartSheet from '../daggerheart-sheet.mjs';
|
||||||
|
import DHAdversarySettings from '../applications/adversary-settings.mjs';
|
||||||
|
|
||||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||||
export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
tag: 'form',
|
||||||
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'adversary'],
|
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'adversary'],
|
||||||
position: { width: 450, height: 1000 },
|
position: { width: 660, height: 766 },
|
||||||
actions: {
|
actions: {
|
||||||
reactionRoll: this.reactionRoll,
|
reactionRoll: this.reactionRoll,
|
||||||
attackRoll: this.attackRoll,
|
useItem: this.useItem,
|
||||||
|
toChat: this.toChat,
|
||||||
attackConfigure: this.attackConfigure,
|
attackConfigure: this.attackConfigure,
|
||||||
addExperience: this.addExperience,
|
addExperience: this.addExperience,
|
||||||
removeExperience: this.removeExperience,
|
removeExperience: this.removeExperience,
|
||||||
toggleHP: this.toggleHP,
|
toggleHP: this.toggleHP,
|
||||||
toggleStress: this.toggleStress
|
toggleStress: this.toggleStress,
|
||||||
|
openSettings: this.openSettings
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
handler: this.updateForm,
|
handler: this.updateForm,
|
||||||
|
|
@ -24,28 +27,37 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
|
sidebar: { template: 'systems/daggerheart/templates/sheets/actors/adversary/sidebar.hbs' },
|
||||||
header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' },
|
header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' },
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
actions: { template: 'systems/daggerheart/templates/sheets/actors/adversary/actions.hbs' },
|
||||||
main: { template: 'systems/daggerheart/templates/sheets/actors/adversary/main.hbs' },
|
notes: { template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs' },
|
||||||
information: { template: 'systems/daggerheart/templates/sheets/actors/adversary/information.hbs' }
|
effects: { template: 'systems/daggerheart/templates/sheets/actors/adversary/effects.hbs' }
|
||||||
};
|
};
|
||||||
|
|
||||||
static TABS = {
|
static TABS = {
|
||||||
main: {
|
actions: {
|
||||||
active: true,
|
active: true,
|
||||||
cssClass: '',
|
cssClass: '',
|
||||||
group: 'primary',
|
group: 'primary',
|
||||||
id: 'main',
|
id: 'actions',
|
||||||
icon: null,
|
icon: null,
|
||||||
label: 'DAGGERHEART.Sheets.Adversary.Tabs.Main'
|
label: 'DAGGERHEART.General.tabs.actions'
|
||||||
},
|
},
|
||||||
information: {
|
notes: {
|
||||||
active: false,
|
active: false,
|
||||||
cssClass: '',
|
cssClass: '',
|
||||||
group: 'primary',
|
group: 'primary',
|
||||||
id: 'information',
|
id: 'notes',
|
||||||
icon: null,
|
icon: null,
|
||||||
label: 'DAGGERHEART.Sheets.Adversary.Tabs.Information'
|
label: 'DAGGERHEART.Sheets.Adversary.Tabs.notes'
|
||||||
|
},
|
||||||
|
effects: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'effects',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.Sheets.Adversary.Tabs.effects'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -56,10 +68,15 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
context.systemFields.attack.fields = this.document.system.attack.schema.fields;
|
context.systemFields.attack.fields = this.document.system.attack.schema.fields;
|
||||||
context.getEffectDetails = this.getEffectDetails.bind(this);
|
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||||
context.isNPC = true;
|
context.isNPC = true;
|
||||||
console.log(context)
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAction(element) {
|
||||||
|
const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId,
|
||||||
|
item = this.document.system.actions.find(x => x.id === itemId);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
static async updateForm(event, _, formData) {
|
||||||
await this.document.update(formData.object);
|
await this.document.update(formData.object);
|
||||||
this.render();
|
this.render();
|
||||||
|
|
@ -70,7 +87,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
event: event,
|
event: event,
|
||||||
title: `${this.actor.name} - Reaction Roll`,
|
title: `${this.actor.name} - Reaction Roll`,
|
||||||
roll: {
|
roll: {
|
||||||
modifier: null,
|
// modifier: null,
|
||||||
type: 'reaction'
|
type: 'reaction'
|
||||||
},
|
},
|
||||||
chatMessage: {
|
chatMessage: {
|
||||||
|
|
@ -86,8 +103,40 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static async attackRoll(event) {
|
static async openSettings() {
|
||||||
this.actor.system.attack.use(event);
|
await new DHAdversarySettings(this.document).render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async useItem(event) {
|
||||||
|
const action = this.getAction(event) ?? this.actor.system.attack;
|
||||||
|
action.use(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async toChat(event, button) {
|
||||||
|
if (button?.dataset?.type === 'experience') {
|
||||||
|
const experience = this.document.system.experiences[button.dataset.uuid];
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const systemData = {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.General.Experience.Single'),
|
||||||
|
description: `${experience.name} ${
|
||||||
|
experience.modifier < 0 ? experience.modifier : `+${experience.modifier}`
|
||||||
|
}`
|
||||||
|
};
|
||||||
|
const msg = new cls({
|
||||||
|
type: 'abilityUse',
|
||||||
|
user: game.user.id,
|
||||||
|
system: systemData,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||||
|
systemData
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
} else {
|
||||||
|
const item = this.getAction(event) ?? this.document.system.attack;
|
||||||
|
item.toChat(this.document.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async attackConfigure(event) {
|
static async attackConfigure(event) {
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
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 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';
|
import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
|
||||||
import DhCharacterCreation from '../characterCreation.mjs';
|
import DhCharacterCreation from '../../characterCreation.mjs';
|
||||||
import FilterMenu from '../ux/filter-menu.mjs';
|
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||||
|
|
||||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||||
const { TextEditor } = foundry.applications.ux;
|
const { TextEditor } = foundry.applications.ux;
|
||||||
|
|
@ -305,8 +305,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
getItem(element) {
|
getItem(element) {
|
||||||
const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId,
|
const listElement = (element.target ?? element).closest('[data-item-id]');
|
||||||
item = this.document.items.get(itemId);
|
const document = listElement.dataset.companion ? this.document.system.companion : this.document;
|
||||||
|
|
||||||
|
const itemId = listElement.dataset.itemId,
|
||||||
|
item = document.items.get(itemId);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,7 +318,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static _onEditImage() {
|
static _onEditImage() {
|
||||||
const fp = new FilePicker({
|
const fp = new foundry.applications.apps.FilePicker.implementation({
|
||||||
current: this.document.img,
|
current: this.document.img,
|
||||||
type: 'image',
|
type: 'image',
|
||||||
redirectToRoot: ['icons/svg/mystery-man.svg'],
|
redirectToRoot: ['icons/svg/mystery-man.svg'],
|
||||||
|
|
@ -330,25 +333,8 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.document = this.document;
|
context.document = this.document;
|
||||||
context.tabs = super._getTabs(this.constructor.TABS);
|
context.tabs = super._getTabs(this.constructor.TABS);
|
||||||
|
|
||||||
context.config = SYSTEM;
|
context.config = SYSTEM;
|
||||||
|
|
||||||
const selectedAttributes = Object.values(this.document.system.traits).map(x => x.base);
|
|
||||||
context.abilityScoreArray = await game.settings
|
|
||||||
.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew)
|
|
||||||
.traitArray.reduce((acc, x) => {
|
|
||||||
const selectedIndex = selectedAttributes.indexOf(x);
|
|
||||||
if (selectedIndex !== -1) {
|
|
||||||
selectedAttributes.splice(selectedIndex, 1);
|
|
||||||
} else {
|
|
||||||
acc.push({ name: x, value: x });
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
if (!context.abilityScoreArray.includes(0)) context.abilityScoreArray.push({ name: 0, value: 0 });
|
|
||||||
context.abilityScoresFinished = context.abilityScoreArray.every(x => x.value === 0);
|
|
||||||
|
|
||||||
context.attributes = Object.keys(this.document.system.traits).reduce((acc, key) => {
|
context.attributes = Object.keys(this.document.system.traits).reduce((acc, key) => {
|
||||||
acc[key] = {
|
acc[key] = {
|
||||||
...this.document.system.traits[key],
|
...this.document.system.traits[key],
|
||||||
|
|
@ -359,67 +345,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const ancestry = await this.mapFeatureType(
|
|
||||||
this.document.system.ancestry ? [this.document.system.ancestry] : [],
|
|
||||||
SYSTEM.GENERAL.objectTypes
|
|
||||||
);
|
|
||||||
const community = await this.mapFeatureType(
|
|
||||||
this.document.system.community ? [this.document.system.community] : [],
|
|
||||||
SYSTEM.GENERAL.objectTypes
|
|
||||||
);
|
|
||||||
const foundation = {
|
|
||||||
ancestry: ancestry[0],
|
|
||||||
community: community[0],
|
|
||||||
advancement: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const nrLoadoutCards = this.document.system.domainCards.loadout.length;
|
|
||||||
const loadout = await this.mapFeatureType(this.document.system.domainCards.loadout, SYSTEM.DOMAIN.cardTypes);
|
|
||||||
const vault = await this.mapFeatureType(this.document.system.domainCards.vault, SYSTEM.DOMAIN.cardTypes);
|
|
||||||
context.abilities = {
|
|
||||||
foundation: foundation,
|
|
||||||
loadout: {
|
|
||||||
top: loadout.slice(0, Math.min(2, nrLoadoutCards)),
|
|
||||||
bottom: nrLoadoutCards > 2 ? loadout.slice(2, Math.min(5, nrLoadoutCards)) : [],
|
|
||||||
nrTotal: nrLoadoutCards,
|
|
||||||
listView: game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS.displayDomainCardsAsList)
|
|
||||||
},
|
|
||||||
vault: vault.map(x => ({
|
|
||||||
...x,
|
|
||||||
uuid: x.uuid,
|
|
||||||
sendToLoadoutDisabled: this.document.system.domainCards.loadout.length >= 5
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
context.inventory = {
|
context.inventory = {
|
||||||
consumable: {
|
|
||||||
titles: {
|
|
||||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.ConsumableTitle'),
|
|
||||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
|
||||||
},
|
|
||||||
items: this.document.items.filter(x => x.type === 'consumable')
|
|
||||||
},
|
|
||||||
miscellaneous: {
|
|
||||||
titles: {
|
|
||||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.MiscellaneousTitle'),
|
|
||||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
|
||||||
},
|
|
||||||
items: this.document.items.filter(x => x.type === 'miscellaneous')
|
|
||||||
},
|
|
||||||
weapons: {
|
|
||||||
titles: {
|
|
||||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.WeaponsTitle'),
|
|
||||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
|
||||||
},
|
|
||||||
items: this.document.items.filter(x => x.type === 'weapon')
|
|
||||||
},
|
|
||||||
armor: {
|
|
||||||
titles: {
|
|
||||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.ArmorsTitle'),
|
|
||||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
|
||||||
},
|
|
||||||
items: this.document.items.filter(x => x.type === 'armor')
|
|
||||||
},
|
|
||||||
currency: {
|
currency: {
|
||||||
title: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Title'),
|
title: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Title'),
|
||||||
coins: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Coins'),
|
coins: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Coins'),
|
||||||
|
|
@ -557,102 +483,6 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/* Filter Menus */
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
_createFilterMenus() {
|
|
||||||
//Menus could be a application option if needed
|
|
||||||
const menus = [
|
|
||||||
{
|
|
||||||
key: 'inventory',
|
|
||||||
container: '[data-application-part="inventory"]',
|
|
||||||
content: '.items-section',
|
|
||||||
callback: this._onMenuFilterInventory.bind(this),
|
|
||||||
target: '.filter-button',
|
|
||||||
filters: FilterMenu.invetoryFilters
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'loadout',
|
|
||||||
container: '[data-application-part="loadout"]',
|
|
||||||
content: '.items-section',
|
|
||||||
callback: this._onMenuFilterLoadout.bind(this),
|
|
||||||
target: '.filter-button',
|
|
||||||
filters: FilterMenu.cardsFilters
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
menus.forEach(m => {
|
|
||||||
const container = this.element.querySelector(m.container);
|
|
||||||
this.#menu[m.key] = new FilterMenu(container, m.target, m.filters, m.callback, {
|
|
||||||
contentSelector: m.content
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback when filters change
|
|
||||||
* @param {PointerEvent} event
|
|
||||||
* @param {HTMLElement} html
|
|
||||||
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
|
|
||||||
*/
|
|
||||||
_onMenuFilterInventory(event, html, filters) {
|
|
||||||
this.#filteredItems.inventory.menu.clear();
|
|
||||||
|
|
||||||
for (const li of html.querySelectorAll('.inventory-item')) {
|
|
||||||
const item = this.document.items.get(li.dataset.itemId);
|
|
||||||
|
|
||||||
const matchesMenu =
|
|
||||||
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
|
|
||||||
if (matchesMenu) this.#filteredItems.inventory.menu.add(item.id);
|
|
||||||
|
|
||||||
const { search } = this.#filteredItems.inventory;
|
|
||||||
li.hidden = !(search.has(item.id) && matchesMenu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback when filters change
|
|
||||||
* @param {PointerEvent} event
|
|
||||||
* @param {HTMLElement} html
|
|
||||||
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
|
|
||||||
*/
|
|
||||||
_onMenuFilterLoadout(event, html, filters) {
|
|
||||||
this.#filteredItems.loadout.menu.clear();
|
|
||||||
|
|
||||||
for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) {
|
|
||||||
const item = this.document.items.get(li.dataset.itemId);
|
|
||||||
|
|
||||||
const matchesMenu =
|
|
||||||
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
|
|
||||||
if (matchesMenu) this.#filteredItems.loadout.menu.add(item.id);
|
|
||||||
|
|
||||||
const { search } = this.#filteredItems.loadout;
|
|
||||||
li.hidden = !(search.has(item.id) && matchesMenu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
async mapFeatureType(data, configType) {
|
|
||||||
return await Promise.all(
|
|
||||||
data.map(async x => {
|
|
||||||
const abilities = x.system.abilities
|
|
||||||
? await Promise.all(x.system.abilities.map(async x => await fromUuid(x.uuid)))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
...x,
|
|
||||||
uuid: x.uuid,
|
|
||||||
system: {
|
|
||||||
...x.system,
|
|
||||||
abilities: abilities,
|
|
||||||
type: game.i18n.localize(configType[x.system.type ?? x.type].label)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async rollAttribute(event, button) {
|
static async rollAttribute(event, button) {
|
||||||
const abilityLabel = game.i18n.localize(abilities[button.dataset.attribute].label);
|
const abilityLabel = game.i18n.localize(abilities[button.dataset.attribute].label);
|
||||||
const config = {
|
const config = {
|
||||||
|
|
@ -738,7 +568,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
new DhlevelUp(this.document).render(true);
|
new DhCharacterlevelUp(this.document).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async useDomainCard(event, button) {
|
static async useDomainCard(event, button) {
|
||||||
|
|
@ -804,15 +634,22 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
static async useItem(event, button) {
|
static async useItem(event, button) {
|
||||||
const item = this.getItem(button);
|
const item = this.getItem(button);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
|
// Should dandle its actions. Or maybe they'll be separate buttons as per an Issue on the board
|
||||||
|
if (item.type === 'feature') {
|
||||||
|
item.toChat();
|
||||||
|
} else {
|
||||||
const wasUsed = await item.use(event);
|
const wasUsed = await item.use(event);
|
||||||
if (wasUsed && item.type === 'weapon') {
|
if (wasUsed && item.type === 'weapon') {
|
||||||
Hooks.callAll(SYSTEM.HOOKS.characterAttack, {});
|
Hooks.callAll(SYSTEM.HOOKS.characterAttack, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static async viewObject(event, button) {
|
static async viewObject(event) {
|
||||||
const item = this.getItem(event);
|
const item = this.getItem(event);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
item.sheet.render(true);
|
item.sheet.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -866,9 +703,10 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteItem(event, button) {
|
static async deleteItem(event) {
|
||||||
const item = this.getItem(event);
|
const item = this.getItem(event);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
await item.delete();
|
await item.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -910,9 +748,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
const systemData = {
|
const systemData = {
|
||||||
name: game.i18n.localize('DAGGERHEART.General.Experience.Single'),
|
name: game.i18n.localize('DAGGERHEART.General.Experience.Single'),
|
||||||
description: `${experience.description} ${
|
description: `${experience.name} ${experience.total < 0 ? experience.total : `+${experience.total}`}`
|
||||||
experience.total < 0 ? experience.total : `+${experience.total}`
|
|
||||||
}`
|
|
||||||
};
|
};
|
||||||
const msg = new cls({
|
const msg = new cls({
|
||||||
type: 'abilityUse',
|
type: 'abilityUse',
|
||||||
140
module/applications/sheets/actors/environment.mjs
Normal file
140
module/applications/sheets/actors/environment.mjs
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
import DaggerheartSheet from '../daggerheart-sheet.mjs';
|
||||||
|
import DHEnvironmentSettings from '../applications/environment-settings.mjs';
|
||||||
|
|
||||||
|
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||||
|
export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'environment'],
|
||||||
|
position: {
|
||||||
|
width: 500
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
addAdversary: this.addAdversary,
|
||||||
|
deleteProperty: this.deleteProperty,
|
||||||
|
openSettings: this.openSettings,
|
||||||
|
useItem: this.useItem,
|
||||||
|
toChat: this.toChat
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this._updateForm,
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
|
},
|
||||||
|
dragDrop: [{ dragSelector: '.action-section .inventory-item', dropSelector: null }]
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' },
|
||||||
|
actions: { template: 'systems/daggerheart/templates/sheets/actors/environment/actions.hbs' },
|
||||||
|
potentialAdversaries: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets/actors/environment/potentialAdversaries.hbs'
|
||||||
|
},
|
||||||
|
notes: { template: 'systems/daggerheart/templates/sheets/actors/environment/notes.hbs' }
|
||||||
|
};
|
||||||
|
|
||||||
|
static TABS = {
|
||||||
|
actions: {
|
||||||
|
active: true,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'actions',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.General.tabs.actions'
|
||||||
|
},
|
||||||
|
potentialAdversaries: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'potentialAdversaries',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.General.tabs.potentialAdversaries'
|
||||||
|
},
|
||||||
|
notes: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'notes',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.Sheets.Adversary.Tabs.notes'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.document = this.document;
|
||||||
|
context.tabs = super._getTabs(this.constructor.TABS);
|
||||||
|
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAction(element) {
|
||||||
|
const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId,
|
||||||
|
item = this.document.system.actions.find(x => x.id === itemId);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async openSettings() {
|
||||||
|
await new DHEnvironmentSettings(this.document).render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async _updateForm(event, _, formData) {
|
||||||
|
await this.document.update(formData.object);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
getEffectDetails(id) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static async addAdversary() {
|
||||||
|
await this.document.update({
|
||||||
|
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
||||||
|
'DAGGERHEART.Sheets.Environment.newAdversary'
|
||||||
|
)
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteProperty(_, target) {
|
||||||
|
await this.document.update({ [`${target.dataset.path}.-=${target.id}`]: null });
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
async viewAdversary(_, button) {
|
||||||
|
const target = button.closest('[data-item-uuid]');
|
||||||
|
const adversary = await foundry.utils.fromUuid(target.dataset.itemUuid);
|
||||||
|
if (!adversary) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.adversaryMissing'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
adversary.sheet.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async useItem(event, button) {
|
||||||
|
const action = this.getAction(event);
|
||||||
|
if (!action) {
|
||||||
|
await this.viewAdversary(event, button);
|
||||||
|
} else {
|
||||||
|
action.use(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async toChat(event) {
|
||||||
|
const item = this.getAction(event);
|
||||||
|
item.toChat(this.document.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onDragStart(event) {
|
||||||
|
const item = event.currentTarget.closest('.inventory-item');
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
const adversary = game.actors.find(x => x.type === 'adversary' && x.id === item.dataset.itemId);
|
||||||
|
const adversaryData = { type: 'Actor', uuid: adversary.uuid };
|
||||||
|
event.dataTransfer.setData('text/plain', JSON.stringify(adversaryData));
|
||||||
|
event.dataTransfer.setDragImage(item, 60, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -46,19 +46,18 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
|
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
case 'description':
|
case 'description':
|
||||||
const value = foundry.utils.getProperty(this.document, "system.description") ?? "";
|
const value = foundry.utils.getProperty(this.document, 'system.description') ?? '';
|
||||||
context.enrichedDescription = await TextEditor.enrichHTML(value, {
|
context.enrichedDescription = await TextEditor.enrichHTML(value, {
|
||||||
relativeTo: this.item,
|
relativeTo: this.item,
|
||||||
rollData: this.item.getRollData(),
|
rollData: this.item.getRollData(),
|
||||||
secrets: this.item.isOwner
|
secrets: this.item.isOwner
|
||||||
})
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Application Clicks Actions */
|
/* Application Clicks Actions */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -73,7 +72,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
'systems/daggerheart/templates/views/actionType.hbs',
|
'systems/daggerheart/templates/views/actionType.hbs',
|
||||||
{ types: SYSTEM.ACTIONS.actionTypes }
|
{ types: SYSTEM.ACTIONS.actionTypes }
|
||||||
),
|
),
|
||||||
title = 'Select Action Type'
|
title = 'Select Action Type';
|
||||||
|
|
||||||
return foundry.applications.api.DialogV2.prompt({
|
return foundry.applications.api.DialogV2.prompt({
|
||||||
window: { title },
|
window: { title },
|
||||||
|
|
@ -134,9 +133,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const actionIndex = button.closest('[data-index]').dataset.index;
|
const actionIndex = button.closest('[data-index]').dataset.index;
|
||||||
await this.document.update({
|
await this.document.update({
|
||||||
'system.actions': this.document.system.actions.filter(
|
'system.actions': this.document.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex))
|
||||||
(_, index) => index !== Number.parseInt(actionIndex)
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
181
module/applications/sheets/applications/adversary-settings.mjs
Normal file
181
module/applications/sheets/applications/adversary-settings.mjs
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
import DHActionConfig from '../../config/Action.mjs';
|
||||||
|
import DHBaseItemSheet from '../api/base-item.mjs';
|
||||||
|
import { actionsTypes } from '../../../data/_module.mjs';
|
||||||
|
|
||||||
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class DHAdversarySettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(actor) {
|
||||||
|
super({});
|
||||||
|
|
||||||
|
this.actor = actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return `${game.i18n.localize('DAGGERHEART.Sheets.TABS.settings')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'dh-style', 'dialog', 'adversary-settings'],
|
||||||
|
window: {
|
||||||
|
icon: 'fa-solid fa-wrench',
|
||||||
|
resizable: false
|
||||||
|
},
|
||||||
|
position: { width: 455, height: 'auto' },
|
||||||
|
actions: {
|
||||||
|
addExperience: this.#addExperience,
|
||||||
|
removeExperience: this.#removeExperience,
|
||||||
|
addAction: this.#addAction,
|
||||||
|
editAction: this.#editAction,
|
||||||
|
removeAction: this.#removeAction
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this.updateForm,
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
header: {
|
||||||
|
id: 'header',
|
||||||
|
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/header.hbs'
|
||||||
|
},
|
||||||
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
|
details: {
|
||||||
|
id: 'details',
|
||||||
|
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/details.hbs'
|
||||||
|
},
|
||||||
|
attack: {
|
||||||
|
id: 'attack',
|
||||||
|
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/attack.hbs'
|
||||||
|
},
|
||||||
|
experiences: {
|
||||||
|
id: 'experiences',
|
||||||
|
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/experiences.hbs'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
id: 'actions',
|
||||||
|
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/actions.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static TABS = {
|
||||||
|
details: {
|
||||||
|
active: true,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'details',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.General.tabs.details'
|
||||||
|
},
|
||||||
|
attack: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'attack',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.General.tabs.attack'
|
||||||
|
},
|
||||||
|
experiences: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'experiences',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.General.tabs.experiences'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'actions',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.General.tabs.actions'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.document = this.actor;
|
||||||
|
context.tabs = this._getTabs(this.constructor.TABS);
|
||||||
|
context.systemFields = this.actor.system.schema.fields;
|
||||||
|
context.systemFields.attack.fields = this.actor.system.attack.schema.fields;
|
||||||
|
context.isNPC = true;
|
||||||
|
console.log(context)
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getTabs(tabs) {
|
||||||
|
for (const v of Object.values(tabs)) {
|
||||||
|
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
|
||||||
|
v.cssClass = v.active ? 'active' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #addExperience() {
|
||||||
|
const newExperience = {
|
||||||
|
name: 'Experience',
|
||||||
|
modifier: 0
|
||||||
|
};
|
||||||
|
await this.actor.update({ [`system.experiences.${foundry.utils.randomID()}`]: newExperience });
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #removeExperience(_, target) {
|
||||||
|
await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null });
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #addAction(_event, _button) {
|
||||||
|
const actionType = await DHBaseItemSheet.selectActionType();
|
||||||
|
if (!actionType) return;
|
||||||
|
try {
|
||||||
|
const cls = actionsTypes[actionType] ?? actionsTypes.attack,
|
||||||
|
action = new cls(
|
||||||
|
{
|
||||||
|
_id: foundry.utils.randomID(),
|
||||||
|
type: actionType,
|
||||||
|
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType].name),
|
||||||
|
...cls.getSourceConfig(this.actor)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parent: this.actor
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await this.actor.update({ 'system.actions': [...this.actor.system.actions, action] });
|
||||||
|
await new DHActionConfig(this.actor.system.actions[this.actor.system.actions.length - 1]).render({
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #editAction(event, target) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const actionIndex = target.dataset.index;
|
||||||
|
await new DHActionConfig(this.actor.system.actions[actionIndex]).render({
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #removeAction(event, target) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const actionIndex = target.dataset.index;
|
||||||
|
await this.actor.update({
|
||||||
|
'system.actions': this.actor.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex))
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateForm(event, _, formData) {
|
||||||
|
await this.actor.update(formData.object);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
220
module/applications/sheets/applications/environment-settings.mjs
Normal file
220
module/applications/sheets/applications/environment-settings.mjs
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
import DHActionConfig from '../../config/Action.mjs';
|
||||||
|
import DHBaseItemSheet from '../api/base-item.mjs';
|
||||||
|
import { actionsTypes } from '../../../data/_module.mjs';
|
||||||
|
|
||||||
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class DHEnvironmentSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(actor) {
|
||||||
|
super({});
|
||||||
|
|
||||||
|
this.actor = actor;
|
||||||
|
this._dragDrop = this._createDragDropHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return `${game.i18n.localize('DAGGERHEART.Sheets.TABS.settings')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'dh-style', 'dialog', 'environment-settings'],
|
||||||
|
window: {
|
||||||
|
icon: 'fa-solid fa-wrench',
|
||||||
|
resizable: false
|
||||||
|
},
|
||||||
|
position: { width: 455, height: 'auto' },
|
||||||
|
actions: {
|
||||||
|
addAction: this.#addAction,
|
||||||
|
editAction: this.#editAction,
|
||||||
|
removeAction: this.#removeAction,
|
||||||
|
addCategory: this.#addCategory,
|
||||||
|
deleteProperty: this.#deleteProperty,
|
||||||
|
viewAdversary: this.#viewAdversary,
|
||||||
|
deleteAdversary: this.#deleteAdversary
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this.updateForm,
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
|
},
|
||||||
|
dragDrop: [{ dragSelector: null, dropSelector: '.category-container' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
header: {
|
||||||
|
id: 'header',
|
||||||
|
template: 'systems/daggerheart/templates/sheets/applications/environment-settings/header.hbs'
|
||||||
|
},
|
||||||
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
|
details: {
|
||||||
|
id: 'details',
|
||||||
|
template: 'systems/daggerheart/templates/sheets/applications/environment-settings/details.hbs'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
id: 'actions',
|
||||||
|
template: 'systems/daggerheart/templates/sheets/applications/environment-settings/actions.hbs'
|
||||||
|
},
|
||||||
|
adversaries: {
|
||||||
|
id: 'adversaries',
|
||||||
|
template: 'systems/daggerheart/templates/sheets/applications/environment-settings/adversaries.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static TABS = {
|
||||||
|
details: {
|
||||||
|
active: true,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'details',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.General.tabs.details'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'actions',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.General.tabs.actions'
|
||||||
|
},
|
||||||
|
adversaries: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'adversaries',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.General.tabs.adversaries'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.document = this.actor;
|
||||||
|
context.tabs = this._getTabs(this.constructor.TABS);
|
||||||
|
context.systemFields = this.actor.system.schema.fields;
|
||||||
|
context.isNPC = true;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
|
this._dragDrop.forEach(d => d.bind(htmlElement));
|
||||||
|
}
|
||||||
|
|
||||||
|
_createDragDropHandlers() {
|
||||||
|
return this.options.dragDrop.map(d => {
|
||||||
|
d.callbacks = {
|
||||||
|
drop: this._onDrop.bind(this)
|
||||||
|
};
|
||||||
|
return new foundry.applications.ux.DragDrop.implementation(d);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_getTabs(tabs) {
|
||||||
|
for (const v of Object.values(tabs)) {
|
||||||
|
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
|
||||||
|
v.cssClass = v.active ? 'active' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #addAction(_event, _button) {
|
||||||
|
const actionType = await DHBaseItemSheet.selectActionType();
|
||||||
|
if (!actionType) return;
|
||||||
|
try {
|
||||||
|
const cls = actionsTypes[actionType] ?? actionsTypes.attack,
|
||||||
|
action = new cls(
|
||||||
|
{
|
||||||
|
_id: foundry.utils.randomID(),
|
||||||
|
type: actionType,
|
||||||
|
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType].name),
|
||||||
|
...cls.getSourceConfig(this.actor)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parent: this.actor
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await this.actor.update({ 'system.actions': [...this.actor.system.actions, action] });
|
||||||
|
await new DHActionConfig(this.actor.system.actions[this.actor.system.actions.length - 1]).render({
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #editAction(event, target) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const actionIndex = target.dataset.index;
|
||||||
|
await new DHActionConfig(this.actor.system.actions[actionIndex]).render({
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #removeAction(event, target) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const actionIndex = target.dataset.index;
|
||||||
|
await this.actor.update({
|
||||||
|
'system.actions': this.actor.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex))
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #addCategory() {
|
||||||
|
await this.actor.update({
|
||||||
|
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
||||||
|
'DAGGERHEART.Sheets.Environment.newAdversary'
|
||||||
|
)
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #deleteProperty(_, target) {
|
||||||
|
await this.actor.update({ [`${target.dataset.path}.-=${target.id}`]: null });
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #viewAdversary(_, button) {
|
||||||
|
const adversary = await foundry.utils.fromUuid(button.dataset.adversary);
|
||||||
|
if (!adversary) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.adversaryMissing'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
adversary.sheet.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #deleteAdversary(event, target) {
|
||||||
|
const adversaryKey = target.dataset.adversary;
|
||||||
|
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`;
|
||||||
|
const newAdversaries = foundry.utils
|
||||||
|
.getProperty(this.actor, path)
|
||||||
|
.filter(x => x && (x?.uuid ?? x) !== adversaryKey);
|
||||||
|
await this.actor.update({ [path]: newAdversaries });
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onDrop(event) {
|
||||||
|
const data = TextEditor.getDragEventData(event);
|
||||||
|
const item = await fromUuid(data.uuid);
|
||||||
|
if (item.type === 'adversary') {
|
||||||
|
const target = event.target.closest('.category-container');
|
||||||
|
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`;
|
||||||
|
const current = foundry.utils.getProperty(this.actor, path).map(x => x.uuid);
|
||||||
|
await this.actor.update({
|
||||||
|
[path]: [...current, item.uuid]
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateForm(event, _, formData) {
|
||||||
|
await this.actor.update(formData.object);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
86
module/applications/sheets/companion.mjs
Normal file
86
module/applications/sheets/companion.mjs
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
import { GMUpdateEvent, socketEvent } from '../../helpers/socket.mjs';
|
||||||
|
import DhCompanionlevelUp from '../levelup/companionLevelup.mjs';
|
||||||
|
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||||
|
|
||||||
|
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||||
|
export default class DhCompanionSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'companion'],
|
||||||
|
position: { width: 700, height: 1000 },
|
||||||
|
actions: {
|
||||||
|
attackRoll: this.attackRoll,
|
||||||
|
levelUp: this.levelUp
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this.updateForm,
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
sidebar: { template: 'systems/daggerheart/templates/sheets/actors/companion/tempMain.hbs' }
|
||||||
|
};
|
||||||
|
|
||||||
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
|
htmlElement.querySelector('.partner-value')?.addEventListener('change', this.onPartnerChange.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.document = this.document;
|
||||||
|
context.playerCharacters = game.actors
|
||||||
|
.filter(
|
||||||
|
x =>
|
||||||
|
x.type === 'character' &&
|
||||||
|
(x.ownership.default === 3 ||
|
||||||
|
x.ownership[game.user.id] === 3 ||
|
||||||
|
this.document.system.partner?.uuid === x.uuid)
|
||||||
|
)
|
||||||
|
.map(x => ({ key: x.uuid, name: x.name }));
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateForm(event, _, formData) {
|
||||||
|
await this.document.update(formData.object);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onPartnerChange(event) {
|
||||||
|
const partnerDocument = event.target.value
|
||||||
|
? await foundry.utils.fromUuid(event.target.value)
|
||||||
|
: this.document.system.partner;
|
||||||
|
const partnerUpdate = { 'system.companion': event.target.value ? this.document.uuid : null };
|
||||||
|
|
||||||
|
if (!partnerDocument.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)) {
|
||||||
|
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||||
|
action: socketEvent.GMUpdate,
|
||||||
|
data: {
|
||||||
|
action: GMUpdateEvent.UpdateDocument,
|
||||||
|
uuid: partnerDocument.uuid,
|
||||||
|
update: update
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await partnerDocument.update(partnerUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.document.update({ 'system.partner': event.target.value });
|
||||||
|
|
||||||
|
if (!event.target.value) {
|
||||||
|
await this.document.updateLevel(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async attackRoll(event) {
|
||||||
|
this.actor.system.attack.use(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async levelUp() {
|
||||||
|
new DhCompanionlevelUp(this.document).render(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ export default function DhpApplicationMixin(Base) {
|
||||||
const attr = target.dataset.edit;
|
const attr = target.dataset.edit;
|
||||||
const current = foundry.utils.getProperty(this.document, attr);
|
const current = foundry.utils.getProperty(this.document, attr);
|
||||||
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {};
|
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {};
|
||||||
const fp = new FilePicker({
|
const fp = new foundry.applications.apps.FilePicker.implementation({
|
||||||
current,
|
current,
|
||||||
type: 'image',
|
type: 'image',
|
||||||
redirectToRoot: img ? [img] : [],
|
redirectToRoot: img ? [img] : [],
|
||||||
|
|
@ -60,7 +60,7 @@ export default function DhpApplicationMixin(Base) {
|
||||||
// drop: this._canDragDrop.bind(this)
|
// drop: this._canDragDrop.bind(this)
|
||||||
// };
|
// };
|
||||||
d.callbacks = {
|
d.callbacks = {
|
||||||
// dragstart: this._onDragStart.bind(this),
|
dragstart: this._onDragStart.bind(this),
|
||||||
// dragover: this._onDragOver.bind(this),
|
// dragover: this._onDragOver.bind(this),
|
||||||
drop: this._onDrop.bind(this)
|
drop: this._onDrop.bind(this)
|
||||||
};
|
};
|
||||||
|
|
@ -68,6 +68,7 @@ export default function DhpApplicationMixin(Base) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _onDragStart(event) {}
|
||||||
_onDrop(event) {}
|
_onDrop(event) {}
|
||||||
|
|
||||||
_getTabs(tabs) {
|
_getTabs(tabs) {
|
||||||
|
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
|
||||||
|
|
||||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
|
||||||
export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
|
|
||||||
static DEFAULT_OPTIONS = {
|
|
||||||
tag: 'form',
|
|
||||||
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'environment'],
|
|
||||||
position: {
|
|
||||||
width: 450,
|
|
||||||
height: 1000
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
addAdversary: this.addAdversary,
|
|
||||||
addFeature: this.addFeature,
|
|
||||||
deleteProperty: this.deleteProperty,
|
|
||||||
viewAdversary: this.viewAdversary
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
handler: this._updateForm,
|
|
||||||
submitOnChange: true,
|
|
||||||
closeOnSubmit: false
|
|
||||||
},
|
|
||||||
dragDrop: [{ dragSelector: null, dropSelector: '.adversary-container' }]
|
|
||||||
};
|
|
||||||
|
|
||||||
static PARTS = {
|
|
||||||
header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' },
|
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
|
||||||
main: { template: 'systems/daggerheart/templates/sheets/actors/environment/main.hbs' },
|
|
||||||
information: { template: 'systems/daggerheart/templates/sheets/actors/environment/information.hbs' }
|
|
||||||
};
|
|
||||||
|
|
||||||
static TABS = {
|
|
||||||
main: {
|
|
||||||
active: true,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'main',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Environment.Tabs.Main'
|
|
||||||
},
|
|
||||||
information: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'information',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Environment.Tabs.Information'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
|
||||||
const context = await super._prepareContext(_options);
|
|
||||||
context.document = this.document;
|
|
||||||
context.tabs = super._getTabs(this.constructor.TABS);
|
|
||||||
context.getEffectDetails = this.getEffectDetails.bind(this);
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async _updateForm(event, _, formData) {
|
|
||||||
await this.document.update(formData.object);
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
getEffectDetails(id) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addAdversary() {
|
|
||||||
await this.document.update({
|
|
||||||
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
|
||||||
'DAGGERHEART.Sheets.Environment.newAdversary'
|
|
||||||
)
|
|
||||||
});
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addFeature() {
|
|
||||||
ui.notifications.error('Not Implemented yet. Awaiting datamodel rework');
|
|
||||||
}
|
|
||||||
|
|
||||||
static async deleteProperty(_, target) {
|
|
||||||
await this.document.update({ [`${target.dataset.path}.-=${target.id}`]: null });
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async viewAdversary(_, button) {
|
|
||||||
const adversary = foundry.utils.getProperty(
|
|
||||||
this.document.system.potentialAdversaries,
|
|
||||||
`${button.dataset.potentialAdversary}.adversaries.${button.dataset.adversary}`
|
|
||||||
);
|
|
||||||
adversary.sheet.render(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _onDrop(event) {
|
|
||||||
const data = TextEditor.getDragEventData(event);
|
|
||||||
const item = await fromUuid(data.uuid);
|
|
||||||
if (item.type === 'adversary') {
|
|
||||||
const target = event.target.closest('.adversary-container');
|
|
||||||
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries.${item.id}`;
|
|
||||||
await this.document.update({
|
|
||||||
[path]: item.uuid
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,11 +6,7 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
||||||
id: 'daggerheart-feature',
|
id: 'daggerheart-feature',
|
||||||
classes: ['feature'],
|
classes: ['feature'],
|
||||||
position: { height: 600 },
|
position: { height: 600 },
|
||||||
window: { resizable: true },
|
window: { resizable: true }
|
||||||
actions: {
|
|
||||||
addEffect: this.addEffect,
|
|
||||||
removeEffect: this.removeEffect
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**@override */
|
/**@override */
|
||||||
|
|
@ -22,27 +18,16 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
||||||
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
|
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
|
||||||
scrollable: ['.actions']
|
scrollable: ['.actions']
|
||||||
},
|
},
|
||||||
settings: {
|
|
||||||
template: 'systems/daggerheart/templates/sheets/items/feature/settings.hbs',
|
|
||||||
scrollable: ['.settings']
|
|
||||||
},
|
|
||||||
effects: {
|
effects: {
|
||||||
template: 'systems/daggerheart/templates/sheets/items/feature/effects.hbs',
|
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs',
|
||||||
scrollable: ['.effects']
|
scrollable: ['.effects']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Internally tracks the selected effect type from the select.
|
|
||||||
* @type {String}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_selectedEffectType;
|
|
||||||
|
|
||||||
/**@override */
|
/**@override */
|
||||||
static TABS = {
|
static TABS = {
|
||||||
primary: {
|
primary: {
|
||||||
tabs: [{ id: 'description' }, { id: 'actions' }, { id: 'settings' }, { id: 'effects' }],
|
tabs: [{ id: 'description' }, { id: 'actions' }, { id: 'effects' }],
|
||||||
initial: 'description',
|
initial: 'description',
|
||||||
labelPrefix: 'DAGGERHEART.Sheets.TABS'
|
labelPrefix: 'DAGGERHEART.Sheets.TABS'
|
||||||
}
|
}
|
||||||
|
|
@ -50,68 +35,10 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**@inheritdoc*/
|
|
||||||
_attachPartListeners(partId, htmlElement, options) {
|
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
|
||||||
if (partId === 'effects')
|
|
||||||
htmlElement.querySelector('.effect-select')?.addEventListener('change', this._effectSelect.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles selection of a new effect type.
|
|
||||||
* @param {Event} event - Change Event
|
|
||||||
*/
|
|
||||||
_effectSelect(event) {
|
|
||||||
const value = event.currentTarget.value;
|
|
||||||
this._selectedEffectType = value;
|
|
||||||
this.render({ parts: ['effects'] });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.properties = CONFIG.daggerheart.ACTOR.featureProperties;
|
|
||||||
context.dice = CONFIG.daggerheart.GENERAL.diceTypes;
|
|
||||||
context.effectConfig = CONFIG.daggerheart.EFFECTS;
|
|
||||||
|
|
||||||
context.selectedEffectType = this._selectedEffectType;
|
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/* Application Clicks Actions */
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new effect to the item, based on the selected effect type.
|
|
||||||
* @param {PointerEvent} _event - The originating click event
|
|
||||||
* @param {HTMLElement} _target - The capturing HTML element which defines the [data-action]
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
static async addEffect(_event, _target) {
|
|
||||||
const type = this._selectedEffectType;
|
|
||||||
if (!type) return;
|
|
||||||
const { id, name, ...rest } = CONFIG.daggerheart.EFFECTS.effectTypes[type];
|
|
||||||
await this.item.update({
|
|
||||||
[`system.effects.${foundry.utils.randomID()}`]: {
|
|
||||||
type,
|
|
||||||
value: '',
|
|
||||||
...rest
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes an effect from the item.
|
|
||||||
* @param {PointerEvent} _event - The originating click event
|
|
||||||
* @param {HTMLElement} target - The capturing HTML element which defines the [data-action]
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
static async removeEffect(_event, target) {
|
|
||||||
const path = `system.effects.-=${target.dataset.effect}`;
|
|
||||||
await this.item.update({ [path]: null });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
module/applications/tooltipManager.mjs
Normal file
16
module/applications/tooltipManager.mjs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
export default class DhTooltipManager extends TooltipManager {
|
||||||
|
async activate(element, options = {}) {
|
||||||
|
let html = options.html;
|
||||||
|
if (element.dataset.tooltip.startsWith('#item#')) {
|
||||||
|
const item = await foundry.utils.fromUuid(element.dataset.tooltip.slice(6));
|
||||||
|
if (item) {
|
||||||
|
html = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/daggerheart/templates/tooltip/${item.type}.hbs`,
|
||||||
|
item
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.activate(element, { ...options, html: html });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,42 +1,42 @@
|
||||||
export const actionTypes = {
|
export const actionTypes = {
|
||||||
attack: {
|
attack: {
|
||||||
id: 'attack',
|
id: 'attack',
|
||||||
name: 'DAGGERHEART.Actions.Types.Attack.Name',
|
name: 'DAGGERHEART.Actions.Types.attack.name',
|
||||||
icon: 'fa-swords'
|
icon: 'fa-swords'
|
||||||
},
|
},
|
||||||
// spellcast: {
|
// spellcast: {
|
||||||
// id: 'spellcast',
|
// id: 'spellcast',
|
||||||
// name: 'DAGGERHEART.Actions.Types.Spellcast.Name',
|
// name: 'DAGGERHEART.Actions.Types.spellcast.name',
|
||||||
// icon: 'fa-book-sparkles'
|
// icon: 'fa-book-sparkles'
|
||||||
// },
|
// },
|
||||||
healing: {
|
healing: {
|
||||||
id: 'healing',
|
id: 'healing',
|
||||||
name: 'DAGGERHEART.Actions.Types.Healing.Name',
|
name: 'DAGGERHEART.Actions.Types.healing.name',
|
||||||
icon: 'fa-kit-medical'
|
icon: 'fa-kit-medical'
|
||||||
},
|
},
|
||||||
// resource: {
|
// resource: {
|
||||||
// id: 'resource',
|
// id: 'resource',
|
||||||
// name: 'DAGGERHEART.Actions.Types.Resource.Name',
|
// name: 'DAGGERHEART.Actions.Types.resource.name',
|
||||||
// icon: 'fa-honey-pot'
|
// icon: 'fa-honey-pot'
|
||||||
// },
|
// },
|
||||||
damage: {
|
damage: {
|
||||||
id: 'damage',
|
id: 'damage',
|
||||||
name: 'DAGGERHEART.Actions.Types.Damage.Name',
|
name: 'DAGGERHEART.Actions.Types.damage.name',
|
||||||
icon: 'fa-bone-break'
|
icon: 'fa-bone-break'
|
||||||
},
|
},
|
||||||
summon: {
|
summon: {
|
||||||
id: 'summon',
|
id: 'summon',
|
||||||
name: 'DAGGERHEART.Actions.Types.Summon.Name',
|
name: 'DAGGERHEART.Actions.Types.summon.name',
|
||||||
icon: 'fa-ghost'
|
icon: 'fa-ghost'
|
||||||
},
|
},
|
||||||
effect: {
|
effect: {
|
||||||
id: 'effect',
|
id: 'effect',
|
||||||
name: 'DAGGERHEART.Actions.Types.Effect.Name',
|
name: 'DAGGERHEART.Actions.Types.effect.name',
|
||||||
icon: 'fa-person-rays'
|
icon: 'fa-person-rays'
|
||||||
},
|
},
|
||||||
macro: {
|
macro: {
|
||||||
id: 'macro',
|
id: 'macro',
|
||||||
name: 'DAGGERHEART.Actions.Types.Macro.Name',
|
name: 'DAGGERHEART.Actions.Types.macro.name',
|
||||||
icon: 'fa-scroll'
|
icon: 'fa-scroll'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -76,7 +76,7 @@ export const damageOnSave = {
|
||||||
label: 'Full damage',
|
label: 'Full damage',
|
||||||
mod: 1
|
mod: 1
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const diceCompare = {
|
export const diceCompare = {
|
||||||
below: {
|
below: {
|
||||||
|
|
@ -104,4 +104,19 @@ export const diceCompare = {
|
||||||
label: 'Above',
|
label: 'Above',
|
||||||
operator: '>'
|
operator: '>'
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const advandtageState = {
|
||||||
|
disadvantage: {
|
||||||
|
label: 'DAGGERHEART.General.Disadvantage.Full',
|
||||||
|
value: -1
|
||||||
|
},
|
||||||
|
neutral: {
|
||||||
|
label: 'DAGGERHEART.General.Neutral.Full',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
advantage: {
|
||||||
|
label: 'DAGGERHEART.General.Advantage.Full',
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,72 +83,72 @@ export const featureProperties = {
|
||||||
export const adversaryTypes = {
|
export const adversaryTypes = {
|
||||||
bruiser: {
|
bruiser: {
|
||||||
id: 'bruiser',
|
id: 'bruiser',
|
||||||
label: 'DAGGERHEART.Adversary.Type.Bruiser.label',
|
label: 'DAGGERHEART.Adversary.Type.bruiser.label',
|
||||||
description: 'DAGGERHEART.Adversary.Bruiser.Description'
|
description: 'DAGGERHEART.Adversary.bruiser.description'
|
||||||
},
|
},
|
||||||
horde: {
|
horde: {
|
||||||
id: 'horde',
|
id: 'horde',
|
||||||
label: 'DAGGERHEART.Adversary.Type.Horde.label',
|
label: 'DAGGERHEART.Adversary.Type.horde.label',
|
||||||
description: 'DAGGERHEART.Adversary.Horde.Description'
|
description: 'DAGGERHEART.Adversary.horde.description'
|
||||||
},
|
},
|
||||||
leader: {
|
leader: {
|
||||||
id: 'leader',
|
id: 'leader',
|
||||||
label: 'DAGGERHEART.Adversary.Type.Leader.label',
|
label: 'DAGGERHEART.Adversary.Type.leader.label',
|
||||||
description: 'DAGGERHEART.Adversary.Leader.Description'
|
description: 'DAGGERHEART.Adversary.leader.description'
|
||||||
},
|
},
|
||||||
minion: {
|
minion: {
|
||||||
id: 'minion',
|
id: 'minion',
|
||||||
label: 'DAGGERHEART.Adversary.Type.Minion.label',
|
label: 'DAGGERHEART.Adversary.Type.minion.label',
|
||||||
description: 'DAGGERHEART.Adversary.Minion.Description'
|
description: 'DAGGERHEART.Adversary.minion.description'
|
||||||
},
|
},
|
||||||
ranged: {
|
ranged: {
|
||||||
id: 'ranged',
|
id: 'ranged',
|
||||||
label: 'DAGGERHEART.Adversary.Type.Ranged.label',
|
label: 'DAGGERHEART.Adversary.Type.ranged.label',
|
||||||
description: 'DAGGERHEART.Adversary.Ranged.Description'
|
description: 'DAGGERHEART.Adversary.ranged.description'
|
||||||
},
|
},
|
||||||
skulk: {
|
skulk: {
|
||||||
id: 'skulk',
|
id: 'skulk',
|
||||||
label: 'DAGGERHEART.Adversary.Type.Skulk.label',
|
label: 'DAGGERHEART.Adversary.Type.skulk.label',
|
||||||
description: 'DAGGERHEART.Adversary.Skulk.Description'
|
description: 'DAGGERHEART.Adversary.skulk.description'
|
||||||
},
|
},
|
||||||
social: {
|
social: {
|
||||||
id: 'social',
|
id: 'social',
|
||||||
label: 'DAGGERHEART.Adversary.Type.Social.label',
|
label: 'DAGGERHEART.Adversary.Type.social.label',
|
||||||
description: 'DAGGERHEART.Adversary.Social.Description'
|
description: 'DAGGERHEART.Adversary.social.description'
|
||||||
},
|
},
|
||||||
solo: {
|
solo: {
|
||||||
id: 'solo',
|
id: 'solo',
|
||||||
label: 'DAGGERHEART.Adversary.Type.Solo.label',
|
label: 'DAGGERHEART.Adversary.Type.solo.label',
|
||||||
description: 'DAGGERHEART.Adversary.Solo.Description'
|
description: 'DAGGERHEART.Adversary.solo.description'
|
||||||
},
|
},
|
||||||
standard: {
|
standard: {
|
||||||
id: 'standard',
|
id: 'standard',
|
||||||
label: 'DAGGERHEART.Adversary.Type.Standard.label',
|
label: 'DAGGERHEART.Adversary.Type.standard.label',
|
||||||
description: 'DAGGERHEART.Adversary.Standard.Description'
|
description: 'DAGGERHEART.Adversary.standard.description'
|
||||||
},
|
},
|
||||||
support: {
|
support: {
|
||||||
id: 'support',
|
id: 'support',
|
||||||
label: 'DAGGERHEART.Adversary.Type.Support.label',
|
label: 'DAGGERHEART.Adversary.Type.support.label',
|
||||||
description: 'DAGGERHEART.Adversary.Support.Description'
|
description: 'DAGGERHEART.Adversary.support.description'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const environmentTypes = {
|
export const environmentTypes = {
|
||||||
exploration: {
|
exploration: {
|
||||||
label: 'DAGGERHEART.Environment.Type.Exploration.label',
|
label: 'DAGGERHEART.Environment.Type.exploration.label',
|
||||||
description: 'DAGGERHEART.Environment.Type.Exploration.description'
|
description: 'DAGGERHEART.Environment.Type.exploration.description'
|
||||||
},
|
},
|
||||||
social: {
|
social: {
|
||||||
label: 'DAGGERHEART.Environment.Type.Social.label',
|
label: 'DAGGERHEART.Environment.Type.social.label',
|
||||||
description: 'DAGGERHEART.Environment.Type.Social.description'
|
description: 'DAGGERHEART.Environment.Type.social.description'
|
||||||
},
|
},
|
||||||
traversal: {
|
traversal: {
|
||||||
label: 'DAGGERHEART.Environment.Type.Traversal.label',
|
label: 'DAGGERHEART.Environment.Type.traversal.label',
|
||||||
description: 'DAGGERHEART.Environment.Type.Traversal.description'
|
description: 'DAGGERHEART.Environment.Type.traversal.description'
|
||||||
},
|
},
|
||||||
event: {
|
event: {
|
||||||
label: 'DAGGERHEART.Environment.Type.Event.label',
|
label: 'DAGGERHEART.Environment.Type.event.label',
|
||||||
description: 'DAGGERHEART.Environment.Type.Event.description'
|
description: 'DAGGERHEART.Environment.Type.event.description'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1,9 @@
|
||||||
export const displayDomainCardsAsList = 'displayDomainCardsAsList';
|
export const displayDomainCardsAsList = 'displayDomainCardsAsList';
|
||||||
|
export const narrativeCountdown = {
|
||||||
|
simple: 'countdown-narrative-simple',
|
||||||
|
position: 'countdown-narrative-position'
|
||||||
|
};
|
||||||
|
export const encounterCountdown = {
|
||||||
|
simple: 'countdown-encounter-simple',
|
||||||
|
position: 'countdown-encounter-position'
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -261,48 +261,6 @@ export const tiers = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const objectTypes = {
|
|
||||||
character: {
|
|
||||||
name: 'TYPES.Actor.character'
|
|
||||||
},
|
|
||||||
npc: {
|
|
||||||
name: 'TYPES.Actor.npc'
|
|
||||||
},
|
|
||||||
adversary: {
|
|
||||||
name: 'TYPES.Actor.adversary'
|
|
||||||
},
|
|
||||||
ancestry: {
|
|
||||||
name: 'TYPES.Item.ancestry'
|
|
||||||
},
|
|
||||||
community: {
|
|
||||||
name: 'TYPES.Item.community'
|
|
||||||
},
|
|
||||||
class: {
|
|
||||||
name: 'TYPES.Item.class'
|
|
||||||
},
|
|
||||||
subclass: {
|
|
||||||
name: 'TYPES.Item.subclass'
|
|
||||||
},
|
|
||||||
feature: {
|
|
||||||
name: 'TYPES.Item.feature'
|
|
||||||
},
|
|
||||||
domainCard: {
|
|
||||||
name: 'TYPES.Item.domainCard'
|
|
||||||
},
|
|
||||||
consumable: {
|
|
||||||
name: 'TYPES.Item.consumable'
|
|
||||||
},
|
|
||||||
miscellaneous: {
|
|
||||||
name: 'TYPES.Item.miscellaneous'
|
|
||||||
},
|
|
||||||
weapon: {
|
|
||||||
name: 'TYPES.Item.weapon'
|
|
||||||
},
|
|
||||||
armor: {
|
|
||||||
name: 'TYPES.Item.armor'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const diceTypes = {
|
export const diceTypes = {
|
||||||
d4: 'd4',
|
d4: 'd4',
|
||||||
d6: 'd6',
|
d6: 'd6',
|
||||||
|
|
@ -325,7 +283,7 @@ export const diceSetNumbers = {
|
||||||
cast: 'Spellcast',
|
cast: 'Spellcast',
|
||||||
scale: 'Cost Scaling',
|
scale: 'Cost Scaling',
|
||||||
flat: 'Flat'
|
flat: 'Flat'
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getDiceSoNicePresets = () => {
|
export const getDiceSoNicePresets = () => {
|
||||||
const { diceSoNice } = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance);
|
const { diceSoNice } = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance);
|
||||||
|
|
@ -384,39 +342,53 @@ export const refreshTypes = {
|
||||||
export const abilityCosts = {
|
export const abilityCosts = {
|
||||||
hope: {
|
hope: {
|
||||||
id: 'hope',
|
id: 'hope',
|
||||||
label: 'Hope'
|
label: 'Hope',
|
||||||
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
stress: {
|
stress: {
|
||||||
id: 'stress',
|
id: 'stress',
|
||||||
label: 'DAGGERHEART.HealingType.Stress.Name'
|
label: 'DAGGERHEART.HealingType.Stress.Name',
|
||||||
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
armor: {
|
armor: {
|
||||||
id: 'armor',
|
id: 'armor',
|
||||||
label: 'Armor Stack'
|
label: 'Armor Stack',
|
||||||
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
hp: {
|
hp: {
|
||||||
id: 'hp',
|
id: 'hp',
|
||||||
label: 'DAGGERHEART.HealingType.HitPoints.Name'
|
label: 'DAGGERHEART.HealingType.HitPoints.Name',
|
||||||
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
prayer: {
|
prayer: {
|
||||||
id: 'prayer',
|
id: 'prayer',
|
||||||
label: 'Prayer Dice'
|
label: 'Prayer Dice',
|
||||||
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
favor: {
|
favor: {
|
||||||
id: 'favor',
|
id: 'favor',
|
||||||
label: 'Favor Points'
|
label: 'Favor Points',
|
||||||
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
slayer: {
|
slayer: {
|
||||||
id: 'slayer',
|
id: 'slayer',
|
||||||
label: 'Slayer Dice'
|
label: 'Slayer Dice',
|
||||||
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
tide: {
|
tide: {
|
||||||
id: 'tide',
|
id: 'tide',
|
||||||
label: 'Tide'
|
label: 'Tide',
|
||||||
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
chaos: {
|
chaos: {
|
||||||
id: 'chaos',
|
id: 'chaos',
|
||||||
label: 'Chaos'
|
label: 'Chaos',
|
||||||
|
group: 'TYPES.Actor.character'
|
||||||
|
},
|
||||||
|
fear: {
|
||||||
|
id: 'fear',
|
||||||
|
label: 'Fear',
|
||||||
|
group: 'TYPES.Actor.adversary'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -712,14 +712,14 @@ export const valueTypes = {
|
||||||
export const actionTypes = {
|
export const actionTypes = {
|
||||||
passive: {
|
passive: {
|
||||||
id: 'passive',
|
id: 'passive',
|
||||||
label: 'DAGGERHEART.ActionType.Passive'
|
label: 'DAGGERHEART.ActionType.passive'
|
||||||
},
|
},
|
||||||
action: {
|
action: {
|
||||||
id: 'action',
|
id: 'action',
|
||||||
label: 'DAGGERHEART.ActionType.Action'
|
label: 'DAGGERHEART.ActionType.action'
|
||||||
},
|
},
|
||||||
reaction: {
|
reaction: {
|
||||||
id: 'reaction',
|
id: 'reaction',
|
||||||
label: 'DAGGERHEART.ActionType.Reaction'
|
label: 'DAGGERHEART.ActionType.reaction'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import CostSelectionDialog from '../../applications/costSelectionDialog.mjs';
|
|
||||||
import { DHActionDiceData, DHActionRollData, DHDamageData, DHDamageField } from './actionDice.mjs';
|
import { DHActionDiceData, DHActionRollData, DHDamageData, DHDamageField } from './actionDice.mjs';
|
||||||
import DhpActor from '../../documents/actor.mjs';
|
import DhpActor from '../../documents/actor.mjs';
|
||||||
import D20RollDialog from '../../dialogs/d20RollDialog.mjs';
|
import D20RollDialog from '../../dialogs/d20RollDialog.mjs';
|
||||||
|
|
@ -73,7 +72,10 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
save: new fields.SchemaField({
|
save: new fields.SchemaField({
|
||||||
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
|
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
|
||||||
difficulty: new fields.NumberField({ nullable: true, initial: 10, integer: true, min: 0 }),
|
difficulty: new fields.NumberField({ nullable: true, initial: 10, integer: true, min: 0 }),
|
||||||
damageMod: new fields.StringField({ initial: SYSTEM.ACTIONS.damageOnSave.none.id, choices: SYSTEM.ACTIONS.damageOnSave })
|
damageMod: new fields.StringField({
|
||||||
|
initial: SYSTEM.ACTIONS.damageOnSave.none.id,
|
||||||
|
choices: SYSTEM.ACTIONS.damageOnSave
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
target: new fields.SchemaField({
|
target: new fields.SchemaField({
|
||||||
type: new fields.StringField({
|
type: new fields.StringField({
|
||||||
|
|
@ -98,9 +100,12 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
initial: SYSTEM.GENERAL.healingTypes.hitPoints.id,
|
initial: SYSTEM.GENERAL.healingTypes.hitPoints.id,
|
||||||
label: 'Healing'
|
label: 'Healing'
|
||||||
}),
|
}),
|
||||||
resultBased: new fields.BooleanField({ initial: false, label: "DAGGERHEART.Actions.Settings.ResultBased.label" }),
|
resultBased: new fields.BooleanField({
|
||||||
|
initial: false,
|
||||||
|
label: 'DAGGERHEART.Actions.Settings.ResultBased.label'
|
||||||
|
}),
|
||||||
value: new fields.EmbeddedDataField(DHActionDiceData),
|
value: new fields.EmbeddedDataField(DHActionDiceData),
|
||||||
valueAlt: new fields.EmbeddedDataField(DHActionDiceData),
|
valueAlt: new fields.EmbeddedDataField(DHActionDiceData)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
extraSchemas = {};
|
extraSchemas = {};
|
||||||
|
|
@ -124,7 +129,11 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
get actor() {
|
get actor() {
|
||||||
return this.item instanceof DhpActor ? this.item : this.item?.actor;
|
return this.item instanceof DhpActor
|
||||||
|
? this.item
|
||||||
|
: this.item?.parent instanceof DhpActor
|
||||||
|
? this.item.parent
|
||||||
|
: this.item?.actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
get chatTemplate() {
|
get chatTemplate() {
|
||||||
|
|
@ -191,12 +200,14 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
|
|
||||||
// Prepare Costs
|
// Prepare Costs
|
||||||
const costsConfig = this.prepareCost();
|
const costsConfig = this.prepareCost();
|
||||||
if(isFastForward && !this.hasCost(costsConfig)) return ui.notifications.warn("You don't have the resources to use that action.");
|
if (isFastForward && !this.hasCost(costsConfig))
|
||||||
|
return ui.notifications.warn("You don't have the resources to use that action.");
|
||||||
// config = this.prepareUseCost(config)
|
// config = this.prepareUseCost(config)
|
||||||
|
|
||||||
// Prepare Uses
|
// Prepare Uses
|
||||||
const usesConfig = this.prepareUse();
|
const usesConfig = this.prepareUse();
|
||||||
if(isFastForward && !this.hasUses(usesConfig)) return ui.notifications.warn("That action doesn't have remaining uses.");
|
if (isFastForward && !this.hasUses(usesConfig))
|
||||||
|
return ui.notifications.warn("That action doesn't have remaining uses.");
|
||||||
// config = this.prepareUseCost(config)
|
// config = this.prepareUseCost(config)
|
||||||
|
|
||||||
// Prepare Roll Data
|
// Prepare Roll Data
|
||||||
|
|
@ -209,7 +220,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
costs: costsConfig,
|
costs: costsConfig,
|
||||||
uses: usesConfig,
|
uses: usesConfig,
|
||||||
data: actorData
|
data: actorData
|
||||||
}
|
};
|
||||||
|
|
||||||
if (Hooks.call(`${SYSTEM.id}.preUseAction`, this, config) === false) return;
|
if (Hooks.call(`${SYSTEM.id}.preUseAction`, this, config) === false) return;
|
||||||
|
|
||||||
|
|
@ -287,7 +298,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
hasHealing: !!this.healing,
|
hasHealing: !!this.healing,
|
||||||
hasEffect: !!this.effects?.length,
|
hasEffect: !!this.effects?.length,
|
||||||
hasSave: this.hasSave
|
hasSave: this.hasSave
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
requireConfigurationDialog(config) {
|
requireConfigurationDialog(config) {
|
||||||
|
|
@ -308,16 +319,15 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
prepareTarget() {
|
prepareTarget() {
|
||||||
let targets;
|
let targets;
|
||||||
if (this.target?.type === SYSTEM.ACTIONS.targetTypes.self.id)
|
if (this.target?.type === SYSTEM.ACTIONS.targetTypes.self.id)
|
||||||
targets = this.formatTarget(this.actor.token ?? this.actor.prototypeToken);
|
targets = this.constructor.formatTarget(this.actor.token ?? this.actor.prototypeToken);
|
||||||
targets = Array.from(game.user.targets);
|
targets = Array.from(game.user.targets);
|
||||||
// foundry.CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
// foundry.CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
||||||
if (this.target?.type && this.target.type !== SYSTEM.ACTIONS.targetTypes.any.id) {
|
if (this.target?.type && this.target.type !== SYSTEM.ACTIONS.targetTypes.any.id) {
|
||||||
targets = targets.filter(t => this.isTargetFriendly(t));
|
targets = targets.filter(t => this.isTargetFriendly(t));
|
||||||
if (this.target.amount && targets.length > this.target.amount) targets = [];
|
if (this.target.amount && targets.length > this.target.amount) targets = [];
|
||||||
}
|
}
|
||||||
targets = targets.map(t => this.formatTarget(t));
|
targets = targets.map(t => this.constructor.formatTarget(t));
|
||||||
return targets;
|
return targets;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareRange() {
|
prepareRange() {
|
||||||
|
|
@ -332,7 +342,9 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
label: 'Attack',
|
label: 'Attack',
|
||||||
type: this.actionType,
|
type: this.actionType,
|
||||||
difficulty: this.roll?.difficulty,
|
difficulty: this.roll?.difficulty,
|
||||||
formula: this.roll.getFormula()
|
formula: this.roll.getFormula(),
|
||||||
|
bonus: this.roll.bonus,
|
||||||
|
advantage: SYSTEM.ACTIONS.advandtageState[this.roll.advState].value
|
||||||
};
|
};
|
||||||
if (this.roll?.type === 'diceSet') roll.lite = true;
|
if (this.roll?.type === 'diceSet') roll.lite = true;
|
||||||
|
|
||||||
|
|
@ -344,7 +356,9 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
async consume(config) {
|
async consume(config) {
|
||||||
const resources = config.costs.filter(c => c.enabled !== false).map(c => {
|
const resources = config.costs
|
||||||
|
.filter(c => c.enabled !== false)
|
||||||
|
.map(c => {
|
||||||
return { type: c.type, value: (c.total ?? c.value) * -1 };
|
return { type: c.type, value: (c.total ?? c.value) * -1 };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -359,7 +373,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
|
|
||||||
/* ROLL */
|
/* ROLL */
|
||||||
get hasRoll() {
|
get hasRoll() {
|
||||||
return !!this.roll?.type;
|
return !!this.roll?.type || !!this.roll?.bonus;
|
||||||
}
|
}
|
||||||
/* ROLL */
|
/* ROLL */
|
||||||
|
|
||||||
|
|
@ -387,8 +401,20 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
hasCost(costs) {
|
hasCost(costs) {
|
||||||
const realCosts = this.getRealCosts(costs);
|
const realCosts = this.getRealCosts(costs),
|
||||||
return realCosts.reduce((a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value), true);
|
hasFearCost = realCosts.findIndex(c => c.type === 'fear');
|
||||||
|
if (hasFearCost > -1) {
|
||||||
|
const fearCost = realCosts.splice(hasFearCost, 1);
|
||||||
|
if (
|
||||||
|
!game.user.isGM ||
|
||||||
|
fearCost[0].total > game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear)
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return realCosts.reduce(
|
||||||
|
(a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
/* COST */
|
/* COST */
|
||||||
|
|
||||||
|
|
@ -419,7 +445,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
formatTarget(actor) {
|
static formatTarget(actor) {
|
||||||
return {
|
return {
|
||||||
id: actor.id,
|
id: actor.id,
|
||||||
actorId: actor.actor.uuid,
|
actorId: actor.actor.uuid,
|
||||||
|
|
@ -436,13 +462,14 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
/* RANGE */
|
/* RANGE */
|
||||||
|
|
||||||
/* EFFECTS */
|
/* EFFECTS */
|
||||||
async applyEffects(event, data, force = false) {
|
async applyEffects(event, data, targets) {
|
||||||
if (!this.effects?.length || !data.system.targets.length) return;
|
targets ??= data.system.targets;
|
||||||
|
if (!this.effects?.length || !targets.length) return;
|
||||||
let effects = this.effects;
|
let effects = this.effects;
|
||||||
data.system.targets.forEach(async token => {
|
targets.forEach(async token => {
|
||||||
if (!token.hit && !force) return;
|
if (!token.hit && !force) return;
|
||||||
if (this.hasSave && token.saved.success === true) {
|
if (this.hasSave && token.saved.success === true) {
|
||||||
effects = this.effects.filter(e => e.onSave === true)
|
effects = this.effects.filter(e => e.onSave === true);
|
||||||
}
|
}
|
||||||
if (!effects.length) return;
|
if (!effects.length) return;
|
||||||
effects.forEach(async e => {
|
effects.forEach(async e => {
|
||||||
|
|
@ -480,18 +507,24 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
/* SAVE */
|
/* SAVE */
|
||||||
async rollSave(target, event, message) {
|
async rollSave(target, event, message) {
|
||||||
if (!target?.actor) return;
|
if (!target?.actor) return;
|
||||||
target.actor.diceRoll({
|
return target.actor
|
||||||
|
.diceRoll({
|
||||||
event,
|
event,
|
||||||
title: 'Roll Save',
|
title: 'Roll Save',
|
||||||
roll: {
|
roll: {
|
||||||
trait: this.save.trait,
|
trait: this.save.trait,
|
||||||
difficulty: this.save.difficulty,
|
difficulty: this.save.difficulty,
|
||||||
type: "reaction"
|
type: 'reaction'
|
||||||
},
|
},
|
||||||
data: target.actor.getRollData()
|
data: target.actor.getRollData()
|
||||||
}).then(async (result) => {
|
|
||||||
if(result) this.updateChatMessage(message, target.id, {result: result.roll.total, success: result.roll.success});
|
|
||||||
})
|
})
|
||||||
|
.then(async result => {
|
||||||
|
if (result)
|
||||||
|
this.updateChatMessage(message, target.id, {
|
||||||
|
result: result.roll.total,
|
||||||
|
success: result.roll.success
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateChatMessage(message, targetId, changes, chain = true) {
|
async updateChatMessage(message, targetId, changes, chain = true) {
|
||||||
|
|
@ -503,26 +536,43 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
await chatMessage.update({ 'system.targets': msgTargets });
|
await chatMessage.update({ 'system.targets': msgTargets });
|
||||||
}, 100);
|
}, 100);
|
||||||
if (chain) {
|
if (chain) {
|
||||||
if(message.system.source.message) this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false);
|
if (message.system.source.message)
|
||||||
|
this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false);
|
||||||
const relatedChatMessages = ui.chat.collection.filter(c => c.system.source.message === message._id);
|
const relatedChatMessages = ui.chat.collection.filter(c => c.system.source.message === message._id);
|
||||||
relatedChatMessages.forEach(c => {
|
relatedChatMessages.forEach(c => {
|
||||||
this.updateChatMessage(c, targetId, changes, false);
|
this.updateChatMessage(c, targetId, changes, false);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* SAVE */
|
/* SAVE */
|
||||||
|
|
||||||
|
async toChat(origin) {
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const systemData = {
|
||||||
|
title: game.i18n.localize('DAGGERHEART.ActionType.action'),
|
||||||
|
origin: origin,
|
||||||
|
img: this.img,
|
||||||
|
name: this.name,
|
||||||
|
description: this.description,
|
||||||
|
actions: []
|
||||||
|
};
|
||||||
|
const msg = new cls({
|
||||||
|
type: 'abilityUse',
|
||||||
|
user: game.user.id,
|
||||||
|
system: systemData,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||||
|
systemData
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DHDamageAction extends DHBaseAction {
|
export class DHDamageAction extends DHBaseAction {
|
||||||
static extraSchemas = ['damage', 'target', 'effects'];
|
static extraSchemas = ['damage', 'target', 'effects'];
|
||||||
|
|
||||||
/* async use(event, ...args) {
|
|
||||||
const config = await super.use(event, args);
|
|
||||||
if (!config || ['error', 'warning'].includes(config.type)) return;
|
|
||||||
if (!this.directDamage) return;
|
|
||||||
return await this.rollDamage(event, config);
|
|
||||||
} */
|
|
||||||
|
|
||||||
getFormulaValue(part, data) {
|
getFormulaValue(part, data) {
|
||||||
let formulaValue = part.value;
|
let formulaValue = part.value;
|
||||||
if (this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt;
|
if (this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt;
|
||||||
|
|
@ -541,7 +591,7 @@ export class DHDamageAction extends DHBaseAction {
|
||||||
const config = {
|
const config = {
|
||||||
title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }),
|
title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }),
|
||||||
roll: { formula },
|
roll: { formula },
|
||||||
targets: (data.system?.targets.filter(t => t.hit) ?? data.targets),
|
targets: data.system?.targets.filter(t => t.hit) ?? data.targets,
|
||||||
hasSave: this.hasSave,
|
hasSave: this.hasSave,
|
||||||
source: data.system?.source,
|
source: data.system?.source,
|
||||||
event
|
event
|
||||||
|
|
@ -597,7 +647,8 @@ export class DHHealingAction extends DHBaseAction {
|
||||||
|
|
||||||
getFormulaValue(data) {
|
getFormulaValue(data) {
|
||||||
let formulaValue = this.healing.value;
|
let formulaValue = this.healing.value;
|
||||||
if(this.hasRoll && this.healing.resultBased && data.system.roll.result.duality === -1) return this.healing.valueAlt;
|
if (this.hasRoll && this.healing.resultBased && data.system.roll.result.duality === -1)
|
||||||
|
return this.healing.valueAlt;
|
||||||
return formulaValue;
|
return formulaValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
||||||
type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }),
|
type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }),
|
||||||
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
|
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
|
||||||
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
||||||
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }),
|
||||||
|
advState: new fields.StringField({ choices: SYSTEM.ACTIONS.advandtageState, initial: 'neutral' }),
|
||||||
diceRolling: new fields.SchemaField({
|
diceRolling: new fields.SchemaField({
|
||||||
multiplier: new fields.StringField({
|
multiplier: new fields.StringField({
|
||||||
choices: SYSTEM.GENERAL.diceSetNumbers,
|
choices: SYSTEM.GENERAL.diceSetNumbers,
|
||||||
|
|
@ -25,7 +26,7 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
||||||
initial: 'above',
|
initial: 'above',
|
||||||
label: 'Should be'
|
label: 'Should be'
|
||||||
}),
|
}),
|
||||||
treshold: new fields.NumberField({ initial: 1, integer: true, min: 1, label: 'Treshold' }),
|
treshold: new fields.NumberField({ initial: 1, integer: true, min: 1, label: 'Treshold' })
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +36,10 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
||||||
let formula = '';
|
let formula = '';
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'diceSet':
|
case 'diceSet':
|
||||||
const multiplier = this.diceRolling.multiplier === 'flat' ? this.diceRolling.flatMultiplier : `@${this.diceRolling.multiplier}`;
|
const multiplier =
|
||||||
|
this.diceRolling.multiplier === 'flat'
|
||||||
|
? this.diceRolling.flatMultiplier
|
||||||
|
: `@${this.diceRolling.multiplier}`;
|
||||||
formula = `${multiplier}${this.diceRolling.dice}cs${SYSTEM.ACTIONS.diceCompare[this.diceRolling.compare].operator}${this.diceRolling.treshold}`;
|
formula = `${multiplier}${this.diceRolling.dice}cs${SYSTEM.ACTIONS.diceCompare[this.diceRolling.compare].operator}${this.diceRolling.treshold}`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -59,7 +63,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||||
label: 'Multiplier'
|
label: 'Multiplier'
|
||||||
}),
|
}),
|
||||||
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
|
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
|
||||||
dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Formula' }),
|
dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Dice' }),
|
||||||
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }),
|
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }),
|
||||||
custom: new fields.SchemaField({
|
custom: new fields.SchemaField({
|
||||||
enabled: new fields.BooleanField({ label: 'Custom Formula' }),
|
enabled: new fields.BooleanField({ label: 'Custom Formula' }),
|
||||||
|
|
@ -75,9 +79,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||||
: `${multiplier ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; */
|
: `${multiplier ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; */
|
||||||
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`,
|
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`,
|
||||||
bonus = this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : '';
|
bonus = this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : '';
|
||||||
return this.custom.enabled
|
return this.custom.enabled ? this.custom.formula : `${multiplier ?? 1}${this.dice}${bonus}`;
|
||||||
? this.custom.formula
|
|
||||||
: `${multiplier ?? 1}${this.dice}${bonus}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,9 +107,12 @@ export class DHDamageData extends foundry.abstract.DataModel {
|
||||||
nullable: false,
|
nullable: false,
|
||||||
required: true
|
required: true
|
||||||
}),
|
}),
|
||||||
resultBased: new fields.BooleanField({ initial: false, label: "DAGGERHEART.Actions.Settings.ResultBased.label" }),
|
resultBased: new fields.BooleanField({
|
||||||
|
initial: false,
|
||||||
|
label: 'DAGGERHEART.Actions.Settings.ResultBased.label'
|
||||||
|
}),
|
||||||
value: new fields.EmbeddedDataField(DHActionDiceData),
|
value: new fields.EmbeddedDataField(DHActionDiceData),
|
||||||
valueAlt: new fields.EmbeddedDataField(DHActionDiceData),
|
valueAlt: new fields.EmbeddedDataField(DHActionDiceData)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import DhCharacter from './character.mjs';
|
import DhCharacter from './character.mjs';
|
||||||
|
import DhCompanion from './companion.mjs';
|
||||||
import DhAdversary from './adversary.mjs';
|
import DhAdversary from './adversary.mjs';
|
||||||
import DhEnvironment from './environment.mjs';
|
import DhEnvironment from './environment.mjs';
|
||||||
|
|
||||||
export { DhCharacter, DhAdversary, DhEnvironment };
|
export { DhCharacter, DhCompanion, DhAdversary, DhEnvironment };
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
character: DhCharacter,
|
character: DhCharacter,
|
||||||
|
companion: DhCompanion,
|
||||||
adversary: DhAdversary,
|
adversary: DhAdversary,
|
||||||
environment: DhEnvironment
|
environment: DhEnvironment
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,11 @@ export default class DhpAdversary extends BaseDataActor {
|
||||||
choices: SYSTEM.ACTOR.adversaryTypes,
|
choices: SYSTEM.ACTOR.adversaryTypes,
|
||||||
initial: SYSTEM.ACTOR.adversaryTypes.standard.id
|
initial: SYSTEM.ACTOR.adversaryTypes.standard.id
|
||||||
}),
|
}),
|
||||||
motivesAndTactics: new fields.HTMLField(),
|
description: new fields.StringField(),
|
||||||
|
motivesAndTactics: new fields.StringField(),
|
||||||
|
notes: new fields.HTMLField(),
|
||||||
difficulty: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
difficulty: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
||||||
|
hordeHp: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
||||||
damageThresholds: new fields.SchemaField({
|
damageThresholds: new fields.SchemaField({
|
||||||
major: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
major: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||||
severe: new fields.NumberField({ required: true, initial: 0, integer: true })
|
severe: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||||
|
|
@ -40,6 +43,7 @@ export default class DhpAdversary extends BaseDataActor {
|
||||||
hitPoints: resourceField(),
|
hitPoints: resourceField(),
|
||||||
stress: resourceField()
|
stress: resourceField()
|
||||||
}),
|
}),
|
||||||
|
actions: new fields.ArrayField(new ActionField()),
|
||||||
attack: new ActionField({
|
attack: new ActionField({
|
||||||
initial: {
|
initial: {
|
||||||
name: 'Attack',
|
name: 'Attack',
|
||||||
|
|
@ -66,7 +70,7 @@ export default class DhpAdversary extends BaseDataActor {
|
||||||
experiences: new fields.TypedObjectField(
|
experiences: new fields.TypedObjectField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
name: new fields.StringField(),
|
name: new fields.StringField(),
|
||||||
value: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
modifier: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
bonuses: new fields.SchemaField({
|
bonuses: new fields.SchemaField({
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { burden } from '../../config/generalConfig.mjs';
|
import { burden } from '../../config/generalConfig.mjs';
|
||||||
|
import ActionField from '../fields/actionField.mjs';
|
||||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||||
import { LevelOptionType } from '../levelTier.mjs';
|
import DhLevelData from '../levelData.mjs';
|
||||||
import BaseDataActor from './base.mjs';
|
import BaseDataActor from './base.mjs';
|
||||||
|
|
||||||
const attributeField = () =>
|
const attributeField = () =>
|
||||||
|
|
@ -96,7 +97,8 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
||||||
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
|
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
|
||||||
}),
|
}),
|
||||||
levelData: new fields.EmbeddedDataField(DhPCLevelData),
|
actions: new fields.ArrayField(new ActionField()),
|
||||||
|
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||||
bonuses: new fields.SchemaField({
|
bonuses: new fields.SchemaField({
|
||||||
armorScore: new fields.NumberField({ integer: true, initial: 0 }),
|
armorScore: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
damageThresholds: new fields.SchemaField({
|
damageThresholds: new fields.SchemaField({
|
||||||
|
|
@ -115,6 +117,7 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
magic: new fields.NumberField({ integer: true, initial: 0 })
|
magic: new fields.NumberField({ integer: true, initial: 0 })
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
|
||||||
rules: new fields.SchemaField({
|
rules: new fields.SchemaField({
|
||||||
maxArmorMarked: new fields.SchemaField({
|
maxArmorMarked: new fields.SchemaField({
|
||||||
value: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
value: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||||
|
|
@ -154,10 +157,25 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
return this.parent.items.find(x => x.type === 'community') ?? null;
|
return this.parent.items.find(x => x.type === 'community') ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get features() {
|
||||||
|
return this.parent.items.filter(x => x.type === 'feature') ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get companionFeatures() {
|
||||||
|
return this.companion ? this.companion.items.filter(x => x.type === 'feature') : [];
|
||||||
|
}
|
||||||
|
|
||||||
get needsCharacterSetup() {
|
get needsCharacterSetup() {
|
||||||
return !this.class.value || !this.class.subclass;
|
return !this.class.value || !this.class.subclass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get spellcastingModifiers() {
|
||||||
|
return {
|
||||||
|
main: this.class.subclass?.system?.spellcastingTrait,
|
||||||
|
multiclass: this.multiclass.subclass?.system?.spellcastingTrait
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
get domains() {
|
get domains() {
|
||||||
const classDomains = this.class.value ? this.class.value.system.domains : [];
|
const classDomains = this.class.value ? this.class.value.system.domains : [];
|
||||||
const multiclassDomains = this.multiclass.value ? this.multiclass.value.system.domains : [];
|
const multiclassDomains = this.multiclass.value ? this.multiclass.value.system.domains : [];
|
||||||
|
|
@ -197,6 +215,12 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get deathMoveViable() {
|
||||||
|
return (
|
||||||
|
this.resources.hitPoints.maxTotal > 0 && this.resources.hitPoints.value >= this.resources.hitPoints.maxTotal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static async unequipBeforeEquip(itemToEquip) {
|
static async unequipBeforeEquip(itemToEquip) {
|
||||||
const primary = this.primaryWeapon,
|
const primary = this.primaryWeapon,
|
||||||
secondary = this.secondaryWeapon;
|
secondary = this.secondaryWeapon;
|
||||||
|
|
@ -307,58 +331,10 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
level: this.levelData.level.current
|
level: this.levelData.level.current
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class DhPCLevelData extends foundry.abstract.DataModel {
|
async _preDelete() {
|
||||||
static defineSchema() {
|
if (this.companion) {
|
||||||
const fields = foundry.data.fields;
|
this.companion.updateLevel(1);
|
||||||
|
}
|
||||||
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.TypedObjectField(new fields.StringField({ required: true })),
|
|
||||||
itemUuid: new fields.StringField({ required: true })
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get canLevelUp() {
|
|
||||||
return this.level.current < this.level.changed;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
139
module/data/actor/companion.mjs
Normal file
139
module/data/actor/companion.mjs
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
import BaseDataActor from './base.mjs';
|
||||||
|
import DhLevelData from '../levelData.mjs';
|
||||||
|
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||||
|
import ActionField from '../fields/actionField.mjs';
|
||||||
|
import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
|
||||||
|
|
||||||
|
export default class DhCompanion extends BaseDataActor {
|
||||||
|
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Companion'];
|
||||||
|
|
||||||
|
static get metadata() {
|
||||||
|
return foundry.utils.mergeObject(super.metadata, {
|
||||||
|
label: 'TYPES.Actor.companion',
|
||||||
|
type: 'companion'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
||||||
|
resources: new fields.SchemaField({
|
||||||
|
stress: new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 3, integer: true })
|
||||||
|
}),
|
||||||
|
hope: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
evasion: new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ required: true, min: 1, initial: 10, integer: true }),
|
||||||
|
bonus: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
experiences: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
name: new fields.StringField({}),
|
||||||
|
value: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
|
bonus: new fields.NumberField({ integer: true, initial: 0 })
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
initial: {
|
||||||
|
experience1: { value: 2 },
|
||||||
|
experience2: { value: 2 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
attack: new ActionField({
|
||||||
|
initial: {
|
||||||
|
name: 'Attack',
|
||||||
|
_id: foundry.utils.randomID(),
|
||||||
|
systemPath: 'attack',
|
||||||
|
type: 'attack',
|
||||||
|
range: 'melee',
|
||||||
|
target: {
|
||||||
|
type: 'any',
|
||||||
|
amount: 1
|
||||||
|
},
|
||||||
|
roll: {
|
||||||
|
type: 'weapon',
|
||||||
|
bonus: 0
|
||||||
|
},
|
||||||
|
damage: {
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
multiplier: 'flat',
|
||||||
|
value: {
|
||||||
|
dice: 'd6',
|
||||||
|
multiplier: 'flat'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
actions: new fields.ArrayField(new ActionField()),
|
||||||
|
levelData: new fields.EmbeddedDataField(DhLevelData)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get attackBonus() {
|
||||||
|
return this.attack.roll.bonus ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareBaseData() {
|
||||||
|
const partnerSpellcastingModifier = this.partner?.system?.spellcastingModifiers?.main;
|
||||||
|
const spellcastingModifier = this.partner?.system?.traits?.[partnerSpellcastingModifier]?.total;
|
||||||
|
this.attack.roll.bonus = spellcastingModifier ?? 0; // Needs to expand on which modifier it is that should be used because of multiclassing;
|
||||||
|
|
||||||
|
for (let levelKey in this.levelData.levelups) {
|
||||||
|
const level = this.levelData.levelups[levelKey];
|
||||||
|
for (let selection of level.selections) {
|
||||||
|
switch (selection.type) {
|
||||||
|
case 'hope':
|
||||||
|
this.resources.hope += selection.value;
|
||||||
|
break;
|
||||||
|
case 'vicious':
|
||||||
|
if (selection.data[0] === 'damage') {
|
||||||
|
this.attack.damage.parts[0].value.dice = adjustDice(this.attack.damage.parts[0].value.dice);
|
||||||
|
} else {
|
||||||
|
this.attack.range = adjustRange(this.attack.range);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'stress':
|
||||||
|
this.resources.stress.bonus += selection.value;
|
||||||
|
break;
|
||||||
|
case 'evasion':
|
||||||
|
this.evasion.bonus += selection.value;
|
||||||
|
break;
|
||||||
|
case 'experience':
|
||||||
|
Object.keys(this.experiences).forEach(key => {
|
||||||
|
const experience = this.experiences[key];
|
||||||
|
experience.bonus += selection.value;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareDerivedData() {
|
||||||
|
for (var experienceKey in this.experiences) {
|
||||||
|
var experience = this.experiences[experienceKey];
|
||||||
|
experience.total = experience.value + experience.bonus;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.partner) {
|
||||||
|
this.partner.system.resources.hope.max += this.resources.hope;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resources.stress.maxTotal = this.resources.stress.max + this.resources.stress.bonus;
|
||||||
|
this.evasion.total = this.evasion.value + this.evasion.bonus;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _preDelete() {
|
||||||
|
if (this.partner) {
|
||||||
|
await this.partner.update({ 'system.companion': null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { environmentTypes } from '../../config/actorConfig.mjs';
|
import { environmentTypes } from '../../config/actorConfig.mjs';
|
||||||
import BaseDataActor from './base.mjs';
|
import BaseDataActor from './base.mjs';
|
||||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||||
|
import ActionField from '../fields/actionField.mjs';
|
||||||
|
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
||||||
|
|
||||||
export default class DhEnvironment extends BaseDataActor {
|
export default class DhEnvironment extends BaseDataActor {
|
||||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Environment'];
|
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Environment'];
|
||||||
|
|
@ -21,15 +23,17 @@ export default class DhEnvironment extends BaseDataActor {
|
||||||
initial: SYSTEM.GENERAL.tiers.tier1.id
|
initial: SYSTEM.GENERAL.tiers.tier1.id
|
||||||
}),
|
}),
|
||||||
type: new fields.StringField({ choices: environmentTypes }),
|
type: new fields.StringField({ choices: environmentTypes }),
|
||||||
impulses: new fields.HTMLField(),
|
description: new fields.StringField(),
|
||||||
|
impulses: new fields.StringField(),
|
||||||
difficulty: new fields.NumberField({ required: true, initial: 11, integer: true }),
|
difficulty: new fields.NumberField({ required: true, initial: 11, integer: true }),
|
||||||
potentialAdversaries: new fields.TypedObjectField(
|
potentialAdversaries: new fields.TypedObjectField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
label: new fields.StringField(),
|
label: new fields.StringField(),
|
||||||
adversaries: new fields.TypedObjectField(new ForeignDocumentUUIDField({ type: 'Actor' }))
|
adversaries: new ForeignDocumentUUIDArrayField({ type: 'Actor' })
|
||||||
})
|
})
|
||||||
)
|
),
|
||||||
/* Features pending datamodel rework */
|
actions: new fields.ArrayField(new ActionField()),
|
||||||
|
notes: new fields.HTMLField()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
|
import { DHBaseAction } from '../action/action.mjs';
|
||||||
static defineSchema() {
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
title: new fields.StringField(),
|
title: new fields.StringField(),
|
||||||
roll: new fields.DataField(),
|
roll: new fields.DataField(),
|
||||||
|
|
@ -20,6 +22,7 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
targetSelection: new fields.BooleanField({ initial: true }),
|
||||||
hasDamage: new fields.BooleanField({ initial: false }),
|
hasDamage: new fields.BooleanField({ initial: false }),
|
||||||
hasHealing: new fields.BooleanField({ initial: false }),
|
hasHealing: new fields.BooleanField({ initial: false }),
|
||||||
hasEffect: new fields.BooleanField({ initial: false }),
|
hasEffect: new fields.BooleanField({ initial: false }),
|
||||||
|
|
@ -28,11 +31,20 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
|
||||||
actor: new fields.StringField(),
|
actor: new fields.StringField(),
|
||||||
item: new fields.StringField(),
|
item: new fields.StringField(),
|
||||||
action: new fields.StringField()
|
action: new fields.StringField()
|
||||||
})
|
}),
|
||||||
|
damage: new fields.ObjectField()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get messageTemplate() {
|
get messageTemplate() {
|
||||||
return 'systems/daggerheart/templates/chat/adversary-roll.hbs';
|
return 'systems/daggerheart/templates/chat/adversary-roll.hbs';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareDerivedData() {
|
||||||
|
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
|
||||||
|
this.currentTargets =
|
||||||
|
this.targetSelection !== true
|
||||||
|
? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t))
|
||||||
|
: this.targets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
targetSelection: new fields.BooleanField({ initial: true }),
|
||||||
hasSave: new fields.BooleanField({ initial: false }),
|
hasSave: new fields.BooleanField({ initial: false }),
|
||||||
onSave: new fields.StringField(),
|
onSave: new fields.StringField(),
|
||||||
source: new fields.SchemaField({
|
source: new fields.SchemaField({
|
||||||
|
|
@ -34,4 +35,12 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
|
||||||
get messageTemplate() {
|
get messageTemplate() {
|
||||||
return `systems/daggerheart/templates/chat/${this.messageType}-roll.hbs`;
|
return `systems/daggerheart/templates/chat/${this.messageType}-roll.hbs`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareDerivedData() {
|
||||||
|
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
|
||||||
|
this.currentTargets =
|
||||||
|
this.targetSelection !== true
|
||||||
|
? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t))
|
||||||
|
: this.targets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,6 @@
|
||||||
const fields = foundry.data.fields;
|
import DHAdversaryRoll from './adversaryRoll.mjs';
|
||||||
|
|
||||||
export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
|
|
||||||
static dualityResult = {
|
|
||||||
hope: 1,
|
|
||||||
fear: 2,
|
|
||||||
critical: 3
|
|
||||||
};
|
|
||||||
|
|
||||||
static defineSchema() {
|
|
||||||
return {
|
|
||||||
title: new fields.StringField(),
|
|
||||||
roll: new fields.DataField({}),
|
|
||||||
targets: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
id: new fields.StringField({}),
|
|
||||||
actorId: new fields.StringField({}),
|
|
||||||
name: new fields.StringField({}),
|
|
||||||
img: new fields.StringField({}),
|
|
||||||
difficulty: new fields.NumberField({ integer: true, nullable: true }),
|
|
||||||
evasion: new fields.NumberField({ integer: true }),
|
|
||||||
hit: new fields.BooleanField({ initial: false }),
|
|
||||||
saved: new fields.SchemaField({
|
|
||||||
result: new fields.NumberField(),
|
|
||||||
success: new fields.BooleanField({ nullable: true, initial: null })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
),
|
|
||||||
costs: new fields.ArrayField(new fields.ObjectField()),
|
|
||||||
hasDamage: new fields.BooleanField({ initial: false }),
|
|
||||||
hasHealing: new fields.BooleanField({ initial: false }),
|
|
||||||
hasEffect: new fields.BooleanField({ initial: false }),
|
|
||||||
hasSave: new fields.BooleanField({ initial: false }),
|
|
||||||
source: new fields.SchemaField({
|
|
||||||
actor: new fields.StringField(),
|
|
||||||
item: new fields.StringField(),
|
|
||||||
action: new fields.StringField()
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export default class DHDualityRoll extends DHAdversaryRoll {
|
||||||
get messageTemplate() {
|
get messageTemplate() {
|
||||||
return 'systems/daggerheart/templates/chat/duality-roll.hbs';
|
return 'systems/daggerheart/templates/chat/duality-roll.hbs';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
module/data/chat-message/hitRoll.mjs
Normal file
0
module/data/chat-message/hitRoll.mjs
Normal file
|
|
@ -36,7 +36,8 @@ class DhCountdownData extends foundry.abstract.DataModel {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
}),
|
||||||
|
window: new fields.SchemaField({})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { actionsTypes } from '../action/_module.mjs';
|
// import { actionsTypes } from '../action/_module.mjs';
|
||||||
|
|
||||||
// Temporary Solution
|
// Temporary Solution
|
||||||
export default class ActionField extends foundry.data.fields.ObjectField {
|
export default class ActionField extends foundry.data.fields.ObjectField {
|
||||||
getModel(value) {
|
getModel(value) {
|
||||||
return actionsTypes[value.type] ?? actionsTypes.attack;
|
return game.system.api.models.actionsTypes[value.type] ?? game.system.api.models.actionsTypes.attack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export default class DHArmor extends BaseDataItem {
|
||||||
type: 'armor',
|
type: 'armor',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true,
|
isQuantifiable: true,
|
||||||
isInventoryItem: true,
|
isInventoryItem: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { actionsTypes } from '../action/_module.mjs';
|
// import { actionsTypes } from '../action/_module.mjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes metadata about the item data model type
|
* Describes metadata about the item data model type
|
||||||
|
|
@ -20,7 +20,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
type: 'base',
|
type: 'base',
|
||||||
hasDescription: false,
|
hasDescription: false,
|
||||||
isQuantifiable: false,
|
isQuantifiable: false,
|
||||||
isInventoryItem: false,
|
isInventoryItem: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +60,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
const actionType = {
|
const actionType = {
|
||||||
weapon: 'attack'
|
weapon: 'attack'
|
||||||
}[this.constructor.metadata.type],
|
}[this.constructor.metadata.type],
|
||||||
cls = actionsTypes.attack,
|
cls = game.system.api.models.actionsTypes[actionType],
|
||||||
|
// cls = actionsTypes.attack,
|
||||||
action = new cls(
|
action = new cls(
|
||||||
{
|
{
|
||||||
_id: foundry.utils.randomID(),
|
_id: foundry.utils.randomID(),
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export default class DHConsumable extends BaseDataItem {
|
||||||
type: 'consumable',
|
type: 'consumable',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true,
|
isQuantifiable: true,
|
||||||
isInventoryItem: true,
|
isInventoryItem: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { getTier } from '../../helpers/utils.mjs';
|
|
||||||
import BaseDataItem from './base.mjs';
|
import BaseDataItem from './base.mjs';
|
||||||
import ActionField from '../fields/actionField.mjs';
|
import ActionField from '../fields/actionField.mjs';
|
||||||
|
|
||||||
|
|
@ -17,135 +16,7 @@ export default class DHFeature extends BaseDataItem {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
|
|
||||||
//A type of feature seems unnecessary
|
|
||||||
type: new fields.StringField({ choices: SYSTEM.ITEM.featureTypes }),
|
|
||||||
|
|
||||||
//TODO: remove actionType field
|
|
||||||
actionType: new fields.StringField({
|
|
||||||
choices: SYSTEM.ITEM.actionTypes,
|
|
||||||
initial: SYSTEM.ITEM.actionTypes.passive.id
|
|
||||||
}),
|
|
||||||
//TODO: remove featureType field
|
|
||||||
featureType: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({
|
|
||||||
choices: SYSTEM.ITEM.valueTypes,
|
|
||||||
initial: Object.keys(SYSTEM.ITEM.valueTypes).find(x => x === 'normal')
|
|
||||||
}),
|
|
||||||
data: new fields.SchemaField({
|
|
||||||
value: new fields.StringField({}),
|
|
||||||
property: new fields.StringField({
|
|
||||||
choices: SYSTEM.ACTOR.featureProperties,
|
|
||||||
initial: Object.keys(SYSTEM.ACTOR.featureProperties).find(x => x === 'spellcastingTrait')
|
|
||||||
}),
|
|
||||||
max: new fields.NumberField({ initial: 1, integer: true }),
|
|
||||||
numbers: new fields.TypedObjectField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
value: new fields.NumberField({ integer: true }),
|
|
||||||
used: new fields.BooleanField({ initial: false })
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
refreshData: new fields.SchemaField(
|
|
||||||
{
|
|
||||||
type: new fields.StringField({ choices: SYSTEM.GENERAL.refreshTypes }),
|
|
||||||
uses: new fields.NumberField({ initial: 1, integer: true }),
|
|
||||||
//TODO: remove refreshed field
|
|
||||||
refreshed: new fields.BooleanField({ initial: true })
|
|
||||||
},
|
|
||||||
{ nullable: true, initial: null }
|
|
||||||
),
|
|
||||||
//TODO: remove refreshed field
|
|
||||||
multiclass: new fields.NumberField({ initial: null, nullable: true, integer: true }),
|
|
||||||
disabled: new fields.BooleanField({ initial: false }),
|
|
||||||
|
|
||||||
//TODO: re do it completely or just remove it
|
|
||||||
effects: new fields.TypedObjectField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
type: new fields.StringField({ choices: SYSTEM.EFFECTS.effectTypes }),
|
|
||||||
valueType: new fields.StringField({ choices: SYSTEM.EFFECTS.valueTypes }),
|
|
||||||
parseType: new fields.StringField({ choices: SYSTEM.EFFECTS.parseTypes }),
|
|
||||||
initiallySelected: new fields.BooleanField({ initial: true }),
|
|
||||||
options: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
name: new fields.StringField({}),
|
|
||||||
value: new fields.StringField({})
|
|
||||||
}),
|
|
||||||
{ nullable: true, initial: null }
|
|
||||||
),
|
|
||||||
dataField: new fields.StringField({}),
|
|
||||||
appliesOn: new fields.StringField(
|
|
||||||
{
|
|
||||||
choices: SYSTEM.EFFECTS.applyLocations
|
|
||||||
},
|
|
||||||
{ nullable: true, initial: null }
|
|
||||||
),
|
|
||||||
applyLocationChoices: new fields.TypedObjectField(new fields.StringField({}), {
|
|
||||||
nullable: true,
|
|
||||||
initial: null
|
|
||||||
}),
|
|
||||||
valueData: new fields.SchemaField({
|
|
||||||
value: new fields.StringField({}),
|
|
||||||
fromValue: new fields.StringField({ initial: null, nullable: true }),
|
|
||||||
type: new fields.StringField({ initial: null, nullable: true }),
|
|
||||||
hopeIncrease: new fields.StringField({ initial: null, nullable: true })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
),
|
|
||||||
actions: new fields.ArrayField(new ActionField())
|
actions: new fields.ArrayField(new ActionField())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get multiclassTier() {
|
|
||||||
return getTier(this.multiclass);
|
|
||||||
}
|
|
||||||
|
|
||||||
async refresh() {
|
|
||||||
if (this.refreshData) {
|
|
||||||
if (this.featureType.type === SYSTEM.ITEM.valueTypes.dice.id) {
|
|
||||||
const update = { 'system.refreshData.refreshed': true };
|
|
||||||
Object.keys(this.featureType.data.numbers).forEach(
|
|
||||||
x => (update[`system.featureType.data.numbers.-=${x}`] = null)
|
|
||||||
);
|
|
||||||
await this.parent.update(update);
|
|
||||||
} else {
|
|
||||||
await this.parent.update({ 'system.refreshData.refreshed': true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get effectData() {
|
|
||||||
const effectValues = Object.values(this.effects);
|
|
||||||
const effectCategories = Object.keys(SYSTEM.EFFECTS.effectTypes).reduce((acc, effectType) => {
|
|
||||||
acc[effectType] = effectValues.reduce((acc, effect) => {
|
|
||||||
if (effect.type === effectType) {
|
|
||||||
acc.push({ ...effect, valueData: this.#parseValues(effect.parseType, effect.valueData) });
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
return effectCategories;
|
|
||||||
}
|
|
||||||
|
|
||||||
#parseValues(parseType, values) {
|
|
||||||
return Object.keys(values).reduce((acc, prop) => {
|
|
||||||
acc[prop] = this.#parseValue(parseType, values[prop]);
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
#parseValue(parseType, value) {
|
|
||||||
switch (parseType) {
|
|
||||||
case SYSTEM.EFFECTS.parseTypes.number.id:
|
|
||||||
return Number.parseInt(value);
|
|
||||||
default:
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export default class DHMiscellaneous extends BaseDataItem {
|
||||||
type: 'miscellaneous',
|
type: 'miscellaneous',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true,
|
isQuantifiable: true,
|
||||||
isInventoryItem: true,
|
isInventoryItem: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export default class DHWeapon extends BaseDataItem {
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true,
|
isQuantifiable: true,
|
||||||
isInventoryItem: true,
|
isInventoryItem: true,
|
||||||
hasInitialAction: true,
|
hasInitialAction: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
61
module/data/levelData.mjs
Normal file
61
module/data/levelData.mjs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { LevelOptionType } from './levelTier.mjs';
|
||||||
|
|
||||||
|
export default class DhLevelData extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
level: new fields.SchemaField({
|
||||||
|
current: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||||
|
changed: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||||
|
bonuses: new fields.TypedObjectField(new fields.NumberField({ integer: true, nullable: false }))
|
||||||
|
}),
|
||||||
|
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.TypedObjectField(new fields.StringField({ required: true })),
|
||||||
|
itemUuid: new fields.DocumentUUIDField({ required: true }),
|
||||||
|
featureIds: new fields.ArrayField(new fields.StringField())
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get actions() {
|
||||||
|
return Object.values(this.levelups).flatMap(level => level.selections.flatMap(s => s.actions));
|
||||||
|
}
|
||||||
|
|
||||||
|
get canLevelUp() {
|
||||||
|
return this.level.current < this.level.changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -58,6 +58,58 @@ class DhLevelOption extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const CompanionLevelOptionType = {
|
||||||
|
hope: {
|
||||||
|
id: 'hope',
|
||||||
|
label: 'Light In The Dark'
|
||||||
|
},
|
||||||
|
creatureComfort: {
|
||||||
|
id: 'creatureComfort',
|
||||||
|
label: 'Creature Comfort',
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
name: 'DAGGERHEART.LevelUp.Actions.CreatureComfort.Name',
|
||||||
|
img: 'icons/magic/life/heart-cross-purple-orange.webp',
|
||||||
|
description: 'DAGGERHEART.LevelUp.Actions.CreatureComfort.Description'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
armored: {
|
||||||
|
id: 'armored',
|
||||||
|
label: 'Armored',
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
name: 'DAGGERHEART.LevelUp.Actions.Armored.Name',
|
||||||
|
img: 'icons/equipment/shield/kite-wooden-oak-glow.webp',
|
||||||
|
description: 'DAGGERHEART.LevelUp.Actions.Armored.Description'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
vicious: {
|
||||||
|
id: 'vicious',
|
||||||
|
label: 'Viscious'
|
||||||
|
},
|
||||||
|
resilient: {
|
||||||
|
id: 'resilient',
|
||||||
|
label: 'Resilient'
|
||||||
|
},
|
||||||
|
bonded: {
|
||||||
|
id: 'bonded',
|
||||||
|
label: 'Bonded',
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
name: 'DAGGERHEART.LevelUp.Actions.Bonded.Name',
|
||||||
|
img: 'icons/magic/life/heart-red-blue.webp',
|
||||||
|
description: 'DAGGERHEART.LevelUp.Actions.Bonded.Description'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
aware: {
|
||||||
|
id: 'aware',
|
||||||
|
label: 'Aware'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const LevelOptionType = {
|
export const LevelOptionType = {
|
||||||
trait: {
|
trait: {
|
||||||
id: 'trait',
|
id: 'trait',
|
||||||
|
|
@ -106,7 +158,8 @@ export const LevelOptionType = {
|
||||||
multiclass: {
|
multiclass: {
|
||||||
id: 'multiclass',
|
id: 'multiclass',
|
||||||
label: 'Multiclass'
|
label: 'Multiclass'
|
||||||
}
|
},
|
||||||
|
...CompanionLevelOptionType
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultLevelTiers = {
|
export const defaultLevelTiers = {
|
||||||
|
|
@ -338,3 +391,80 @@ export const defaultLevelTiers = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const defaultCompanionTier = {
|
||||||
|
tiers: {
|
||||||
|
2: {
|
||||||
|
tier: 2,
|
||||||
|
name: 'Companion Choices',
|
||||||
|
levels: {
|
||||||
|
start: 2,
|
||||||
|
end: 10
|
||||||
|
},
|
||||||
|
initialAchievements: {},
|
||||||
|
availableOptions: 1,
|
||||||
|
domainCardByLevel: 0,
|
||||||
|
options: {
|
||||||
|
experience: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.intelligent',
|
||||||
|
checkboxSelections: 3,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.experience.id,
|
||||||
|
value: 1,
|
||||||
|
amount: 1
|
||||||
|
},
|
||||||
|
hope: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.lightInTheDark',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: CompanionLevelOptionType.hope.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
creatureComfort: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.creatureComfort',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: CompanionLevelOptionType.creatureComfort.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
armored: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.armored',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: CompanionLevelOptionType.armored.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
vicious: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.vicious',
|
||||||
|
checkboxSelections: 3,
|
||||||
|
minCost: 1,
|
||||||
|
type: CompanionLevelOptionType.vicious.id,
|
||||||
|
value: 1,
|
||||||
|
amount: 1
|
||||||
|
},
|
||||||
|
stress: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.resilient',
|
||||||
|
checkboxSelections: 3,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.stress.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
bonded: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.bonded',
|
||||||
|
checkboxSelections: 1,
|
||||||
|
minCost: 1,
|
||||||
|
type: CompanionLevelOptionType.bonded.id,
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
evasion: {
|
||||||
|
label: 'DAGGERHEART.LevelUp.Options.aware',
|
||||||
|
checkboxSelections: 3,
|
||||||
|
minCost: 1,
|
||||||
|
type: LevelOptionType.evasion.id,
|
||||||
|
value: 2,
|
||||||
|
amount: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export class DhLevelup extends foundry.abstract.DataModel {
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
levels[i] = DhLevelupLevel.initializeData(pcLevelData.levelups[i], tier.availableOptions, {
|
levels[i] = DhLevelupLevel.initializeData(pcLevelData.levelups[i], tier.maxSelections[i], {
|
||||||
...initialAchievements,
|
...initialAchievements,
|
||||||
experiences,
|
experiences,
|
||||||
domainCards
|
domainCards
|
||||||
|
|
@ -46,7 +46,7 @@ export class DhLevelup extends foundry.abstract.DataModel {
|
||||||
name: tier.name,
|
name: tier.name,
|
||||||
belongingLevels: belongingLevels,
|
belongingLevels: belongingLevels,
|
||||||
options: Object.keys(tier.options).reduce((acc, key) => {
|
options: Object.keys(tier.options).reduce((acc, key) => {
|
||||||
acc[key] = tier.options[key].toObject();
|
acc[key] = tier.options[key].toObject?.() ?? tier.options[key];
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
};
|
};
|
||||||
|
|
@ -98,6 +98,7 @@ export class DhLevelup extends foundry.abstract.DataModel {
|
||||||
case 'experience':
|
case 'experience':
|
||||||
case 'domainCard':
|
case 'domainCard':
|
||||||
case 'subclass':
|
case 'subclass':
|
||||||
|
case 'vicious':
|
||||||
return checkbox.data.length === (checkbox.amount ?? 1);
|
return checkbox.data.length === (checkbox.amount ?? 1);
|
||||||
case 'multiclass':
|
case 'multiclass':
|
||||||
const classSelected = checkbox.data.length === 1;
|
const classSelected = checkbox.data.length === 1;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
this.config.experiences = [];
|
this.config.experiences = [];
|
||||||
|
|
||||||
if (config.source?.action) {
|
if (config.source?.action) {
|
||||||
this.item = config.data.parent.items.get(config.source.item);
|
console.log(config)
|
||||||
|
this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent;
|
||||||
this.action =
|
this.action =
|
||||||
config.data.attack?._id == config.source.action
|
config.data.attack?._id == config.source.action
|
||||||
? config.data.attack
|
? config.data.attack
|
||||||
|
|
@ -50,15 +51,18 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
};
|
};
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
|
console.log(this.config, this.roll)
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.hasRoll = !!this.config.roll;
|
context.hasRoll = !!this.config.roll;
|
||||||
|
context.roll = this.roll;
|
||||||
|
context.rollType = this.roll?.constructor.name;
|
||||||
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
|
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
|
||||||
id,
|
id,
|
||||||
...this.config.data.experiences[id]
|
...this.config.data.experiences[id]
|
||||||
}));
|
}));
|
||||||
context.selectedExperiences = this.config.experiences;
|
context.selectedExperiences = this.config.experiences;
|
||||||
context.advantage = this.config.advantage;
|
context.advantage = this.config.roll?.advantage;
|
||||||
/* context.diceOptions = this.diceOptions; */
|
context.diceOptions = SYSTEM.GENERAL.diceTypes;
|
||||||
context.canRoll = true;
|
context.canRoll = true;
|
||||||
context.isLite = this.config.roll?.lite;
|
context.isLite = this.config.roll?.lite;
|
||||||
if (this.config.costs?.length) {
|
if (this.config.costs?.length) {
|
||||||
|
|
@ -71,7 +75,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
context.uses = this.action.calcUses(this.config.uses);
|
context.uses = this.action.calcUses(this.config.uses);
|
||||||
context.canRoll = context.canRoll && this.action.hasUses(context.uses);
|
context.canRoll = context.canRoll && this.action.hasUses(context.uses);
|
||||||
}
|
}
|
||||||
|
context.extraFormula = this.config.extraFormula;
|
||||||
context.formula = this.roll.constructFormula(this.config);
|
context.formula = this.roll.constructFormula(this.config);
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,12 +87,18 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs);
|
this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs);
|
||||||
}
|
}
|
||||||
if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
|
if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
|
||||||
|
if(rest.roll?.dice) {
|
||||||
|
Object.entries(rest.roll.dice).forEach(([key, value]) => {
|
||||||
|
this.roll[key] = value;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.config.extraFormula = rest.extraFormula;
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateIsAdvantage(_, button) {
|
static updateIsAdvantage(_, button) {
|
||||||
const advantage = Number(button.dataset.advantage);
|
const advantage = Number(button.dataset.advantage);
|
||||||
this.config.advantage = this.config.advantage === advantage ? 0 : advantage;
|
this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +108,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
} else {
|
} else {
|
||||||
this.config.experiences = [...this.config.experiences, button.dataset.key];
|
this.config.experiences = [...this.config.experiences, button.dataset.key];
|
||||||
} */
|
} */
|
||||||
this.config.experiences = this.config.experiences.indexOf(button.dataset.key) > -1 ? this.config.experiences.filter(x => x !== button.dataset.key) : [...this.config.experiences, button.dataset.key];
|
this.config.experiences =
|
||||||
|
this.config.experiences.indexOf(button.dataset.key) > -1
|
||||||
|
? this.config.experiences.filter(x => x !== button.dataset.key)
|
||||||
|
: [...this.config.experiences, button.dataset.key];
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,17 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.title = this.config.title;
|
context.title = this.config.title;
|
||||||
context.formula = this.config.roll.formula;
|
context.extraFormula = this.config.extraFormula;
|
||||||
|
context.formula = this.roll.constructFormula(this.config);;
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static updateRollConfiguration(event, _, formData) {
|
||||||
|
const { ...rest } = foundry.utils.expandObject(formData.object);
|
||||||
|
this.config.extraFormula = rest.extraFormula;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
static async submitRoll() {
|
static async submitRoll() {
|
||||||
await this.close({ submitted: true });
|
await this.close({ submitted: true });
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +60,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
static async configure(roll, config = {}) {
|
static async configure(roll, config = {}) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const app = new this(roll, config);
|
const app = new this(roll, config);
|
||||||
app.addEventListener("close", () => resolve(app.config), { once: true });
|
app.addEventListener('close', () => resolve(app.config), { once: true });
|
||||||
app.render({ force: true });
|
app.render({ force: true });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import DamageSelectionDialog from '../applications/damageSelectionDialog.mjs';
|
import DamageSelectionDialog from '../applications/damageSelectionDialog.mjs';
|
||||||
import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs';
|
import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs';
|
||||||
import DamageReductionDialog from '../applications/damageReductionDialog.mjs';
|
import DamageReductionDialog from '../applications/damageReductionDialog.mjs';
|
||||||
|
import { LevelOptionType } from '../data/levelTier.mjs';
|
||||||
|
import DHFeature from '../data/item/feature.mjs';
|
||||||
|
|
||||||
export default class DhpActor extends Actor {
|
export default class DhpActor extends Actor {
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
|
|
@ -8,7 +10,7 @@ export default class DhpActor extends Actor {
|
||||||
|
|
||||||
// Configure prototype token settings
|
// Configure prototype token settings
|
||||||
const prototypeToken = {};
|
const prototypeToken = {};
|
||||||
if (this.type === 'character')
|
if (['character', 'companion'].includes(this.type))
|
||||||
Object.assign(prototypeToken, {
|
Object.assign(prototypeToken, {
|
||||||
sight: { enabled: true },
|
sight: { enabled: true },
|
||||||
actorLink: true,
|
actorLink: true,
|
||||||
|
|
@ -18,7 +20,7 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateLevel(newLevel) {
|
async updateLevel(newLevel) {
|
||||||
if (this.type !== 'character' || newLevel === this.system.levelData.level.changed) return;
|
if (!['character', 'companion'].includes(this.type) || newLevel === this.system.levelData.level.changed) return;
|
||||||
|
|
||||||
if (newLevel > this.system.levelData.level.current) {
|
if (newLevel > this.system.levelData.level.current) {
|
||||||
const maxLevel = Object.values(
|
const maxLevel = Object.values(
|
||||||
|
|
@ -41,6 +43,7 @@ export default class DhpActor extends Actor {
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
const featureIds = [];
|
||||||
const domainCards = [];
|
const domainCards = [];
|
||||||
const experiences = [];
|
const experiences = [];
|
||||||
const subclassFeatureState = { class: null, multiclass: null };
|
const subclassFeatureState = { class: null, multiclass: null };
|
||||||
|
|
@ -53,6 +56,7 @@ export default class DhpActor extends Actor {
|
||||||
const advancementCards = level.selections.filter(x => x.type === 'domainCard').map(x => x.itemUuid);
|
const advancementCards = level.selections.filter(x => x.type === 'domainCard').map(x => x.itemUuid);
|
||||||
domainCards.push(...achievementCards, ...advancementCards);
|
domainCards.push(...achievementCards, ...advancementCards);
|
||||||
experiences.push(...Object.keys(level.achievements.experiences));
|
experiences.push(...Object.keys(level.achievements.experiences));
|
||||||
|
featureIds.push(...level.selections.flatMap(x => x.featureIds));
|
||||||
|
|
||||||
const subclass = level.selections.find(x => x.type === 'subclass');
|
const subclass = level.selections.find(x => x.type === 'subclass');
|
||||||
if (subclass) {
|
if (subclass) {
|
||||||
|
|
@ -66,13 +70,21 @@ export default class DhpActor extends Actor {
|
||||||
multiclass = level.selections.find(x => x.type === 'multiclass');
|
multiclass = level.selections.find(x => x.type === 'multiclass');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (let featureId of featureIds) {
|
||||||
|
this.items.get(featureId).delete();
|
||||||
|
}
|
||||||
|
|
||||||
if (experiences.length > 0) {
|
if (experiences.length > 0) {
|
||||||
this.update({
|
const getUpdate = () => ({
|
||||||
'system.experiences': experiences.reduce((acc, key) => {
|
'system.experiences': experiences.reduce((acc, key) => {
|
||||||
acc[`-=${key}`] = null;
|
acc[`-=${key}`] = null;
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
});
|
});
|
||||||
|
this.update(getUpdate());
|
||||||
|
if (this.system.companion) {
|
||||||
|
this.system.companion.update(getUpdate());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subclassFeatureState.class) {
|
if (subclassFeatureState.class) {
|
||||||
|
|
@ -114,10 +126,15 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.system.companion) {
|
||||||
|
this.system.companion.updateLevel(newLevel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async levelUp(levelupData) {
|
async levelUp(levelupData) {
|
||||||
|
const actions = [];
|
||||||
const levelups = {};
|
const levelups = {};
|
||||||
for (var levelKey of Object.keys(levelupData)) {
|
for (var levelKey of Object.keys(levelupData)) {
|
||||||
const level = levelupData[levelKey];
|
const level = levelupData[levelKey];
|
||||||
|
|
@ -126,13 +143,23 @@ export default class DhpActor extends Actor {
|
||||||
const experience = level.achievements.experiences[experienceKey];
|
const experience = level.achievements.experiences[experienceKey];
|
||||||
await this.update({
|
await this.update({
|
||||||
[`system.experiences.${experienceKey}`]: {
|
[`system.experiences.${experienceKey}`]: {
|
||||||
description: experience.name,
|
name: experience.name,
|
||||||
|
value: experience.modifier
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.system.companion) {
|
||||||
|
await this.system.companion.update({
|
||||||
|
[`system.experiences.${experienceKey}`]: {
|
||||||
|
name: '',
|
||||||
value: experience.modifier
|
value: experience.modifier
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let multiclass = null;
|
let multiclass = null;
|
||||||
|
const featureAdditions = [];
|
||||||
const domainCards = [];
|
const domainCards = [];
|
||||||
const subclassFeatureState = { class: null, multiclass: null };
|
const subclassFeatureState = { class: null, multiclass: null };
|
||||||
const selections = [];
|
const selections = [];
|
||||||
|
|
@ -141,7 +168,18 @@ export default class DhpActor extends Actor {
|
||||||
for (var checkboxNr of Object.keys(selection)) {
|
for (var checkboxNr of Object.keys(selection)) {
|
||||||
const checkbox = selection[checkboxNr];
|
const checkbox = selection[checkboxNr];
|
||||||
|
|
||||||
if (checkbox.type === 'multiclass') {
|
const tierOption = LevelOptionType[checkbox.type];
|
||||||
|
if (tierOption.features?.length > 0) {
|
||||||
|
featureAdditions.push({
|
||||||
|
checkbox: {
|
||||||
|
...checkbox,
|
||||||
|
level: Number(levelKey),
|
||||||
|
optionKey: optionKey,
|
||||||
|
checkboxNr: Number(checkboxNr)
|
||||||
|
},
|
||||||
|
features: tierOption.features
|
||||||
|
});
|
||||||
|
} else if (checkbox.type === 'multiclass') {
|
||||||
multiclass = {
|
multiclass = {
|
||||||
...checkbox,
|
...checkbox,
|
||||||
level: Number(levelKey),
|
level: Number(levelKey),
|
||||||
|
|
@ -174,6 +212,28 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var addition of featureAdditions) {
|
||||||
|
for (var featureData of addition.features) {
|
||||||
|
const feature = new DHFeature({
|
||||||
|
...featureData,
|
||||||
|
description: game.i18n.localize(featureData.description)
|
||||||
|
});
|
||||||
|
const embeddedItem = await this.createEmbeddedDocuments('Item', [
|
||||||
|
{
|
||||||
|
...featureData,
|
||||||
|
name: game.i18n.localize(featureData.name),
|
||||||
|
type: 'feature',
|
||||||
|
system: feature
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
addition.checkbox.featureIds = !addition.checkbox.featureIds
|
||||||
|
? [embeddedItem[0].id]
|
||||||
|
: [...addition.checkbox.featureIds, embeddedItem[0].id];
|
||||||
|
}
|
||||||
|
|
||||||
|
selections.push(addition.checkbox);
|
||||||
|
}
|
||||||
|
|
||||||
if (multiclass) {
|
if (multiclass) {
|
||||||
const subclassItem = await foundry.utils.fromUuid(multiclass.secondaryData.subclass);
|
const subclassItem = await foundry.utils.fromUuid(multiclass.secondaryData.subclass);
|
||||||
const subclassData = subclassItem.toObject();
|
const subclassData = subclassItem.toObject();
|
||||||
|
|
@ -238,6 +298,7 @@ export default class DhpActor extends Actor {
|
||||||
|
|
||||||
await this.update({
|
await this.update({
|
||||||
system: {
|
system: {
|
||||||
|
actions: [...this.system.actions, ...actions],
|
||||||
levelData: {
|
levelData: {
|
||||||
level: {
|
level: {
|
||||||
current: this.system.levelData.level.changed
|
current: this.system.levelData.level.changed
|
||||||
|
|
@ -246,6 +307,10 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.system.companion) {
|
||||||
|
this.system.companion.updateLevel(this.system.levelData.level.changed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -364,6 +429,11 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
async takeDamage(damage, type) {
|
async takeDamage(damage, type) {
|
||||||
|
if (this.type === 'companion') {
|
||||||
|
await this.modifyResource([{ value: 1, type: 'stress' }]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const hpDamage =
|
const hpDamage =
|
||||||
damage >= this.system.damageThresholds.severe
|
damage >= this.system.damageThresholds.severe
|
||||||
? 3
|
? 3
|
||||||
|
|
@ -414,6 +484,11 @@ export default class DhpActor extends Actor {
|
||||||
let updates = { actor: { target: this, resources: {} }, armor: { target: this.system.armor, resources: {} } };
|
let updates = { actor: { target: this, resources: {} }, armor: { target: this.system.armor, resources: {} } };
|
||||||
resources.forEach(r => {
|
resources.forEach(r => {
|
||||||
switch (r.type) {
|
switch (r.type) {
|
||||||
|
case 'fear':
|
||||||
|
ui.resources.updateFear(
|
||||||
|
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear) + r.value
|
||||||
|
);
|
||||||
|
break;
|
||||||
case 'armorStack':
|
case 'armorStack':
|
||||||
updates.armor.resources['system.marks.value'] = Math.max(
|
updates.armor.resources['system.marks.value'] = Math.max(
|
||||||
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
||||||
|
|
@ -422,14 +497,16 @@ export default class DhpActor extends Actor {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
updates.actor.resources[`system.resources.${r.type}.value`] = Math.max(
|
updates.actor.resources[`system.resources.${r.type}.value`] = Math.max(
|
||||||
Math.min(this.system.resources[r.type].value + r.value, (this.system.resources[r.type].maxTotal ?? this.system.resources[r.type].max)),
|
Math.min(
|
||||||
|
this.system.resources[r.type].value + r.value,
|
||||||
|
this.system.resources[r.type].maxTotal ?? this.system.resources[r.type].max
|
||||||
|
),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Object.values(updates).forEach(async u => {
|
Object.values(updates).forEach(async u => {
|
||||||
console.log(updates, u)
|
|
||||||
if (Object.keys(u.resources).length > 0) {
|
if (Object.keys(u.resources).length > 0) {
|
||||||
if (game.user.isGM) {
|
if (game.user.isGM) {
|
||||||
await u.target.update(u.resources);
|
await u.target.update(u.resources);
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,8 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
content,
|
content,
|
||||||
ok: {
|
ok: {
|
||||||
label: title,
|
label: title,
|
||||||
callback: (event, button, dialog) => this.system.actions.find(a => a._id === button.form.elements.actionId.value)
|
callback: (event, button, dialog) =>
|
||||||
|
this.system.actions.find(a => a._id === button.form.elements.actionId.value)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -116,6 +117,8 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.AncestryTitle')
|
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.AncestryTitle')
|
||||||
: this.type === 'community'
|
: this.type === 'community'
|
||||||
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.CommunityTitle')
|
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.CommunityTitle')
|
||||||
|
: this.type === 'feature'
|
||||||
|
? game.i18n.localize('TYPES.Item.feature')
|
||||||
: game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
|
: game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
|
||||||
origin: origin,
|
origin: origin,
|
||||||
img: this.img,
|
img: this.img,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
|
import { diceTypes, getDiceSoNicePresets, range } from '../config/generalConfig.mjs';
|
||||||
import Tagify from '@yaireo/tagify';
|
import Tagify from '@yaireo/tagify';
|
||||||
|
|
||||||
export const loadCompendiumOptions = async compendiums => {
|
export const loadCompendiumOptions = async compendiums => {
|
||||||
|
|
@ -124,13 +124,13 @@ export const getCommandTarget = () => {
|
||||||
|
|
||||||
export const setDiceSoNiceForDualityRoll = (rollResult, advantageState) => {
|
export const setDiceSoNiceForDualityRoll = (rollResult, advantageState) => {
|
||||||
const diceSoNicePresets = getDiceSoNicePresets();
|
const diceSoNicePresets = getDiceSoNicePresets();
|
||||||
rollResult.dice[0].options.appearance = diceSoNicePresets.hope;
|
rollResult.dice[0].options = { appearance: diceSoNicePresets.hope };
|
||||||
rollResult.dice[1].options.appearance = diceSoNicePresets.fear;
|
rollResult.dice[1].options = { appearance: diceSoNicePresets.fear }; //diceSoNicePresets.fear;
|
||||||
if (rollResult.dice[2]) {
|
if (rollResult.dice[2]) {
|
||||||
if (advantageState === true) {
|
if (advantageState === true) {
|
||||||
rollResult.dice[2].options.appearance = diceSoNicePresets.advantage;
|
rollResult.dice[2].options = { appearance: diceSoNicePresets.advantage };
|
||||||
} else if (advantageState === false) {
|
} else if (advantageState === false) {
|
||||||
rollResult.dice[2].options.appearance = diceSoNicePresets.disadvantage;
|
rollResult.dice[2].options = { appearance: diceSoNicePresets.disadvantage };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -227,8 +227,8 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue
|
||||||
const nativeReplaceFormulaData = Roll.replaceFormulaData;
|
const nativeReplaceFormulaData = Roll.replaceFormulaData;
|
||||||
Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false } = {}) {
|
Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false } = {}) {
|
||||||
const terms = Object.keys(SYSTEM.GENERAL.multiplierTypes).map(type => {
|
const terms = Object.keys(SYSTEM.GENERAL.multiplierTypes).map(type => {
|
||||||
return { term: type, default: 1}
|
return { term: type, default: 1 };
|
||||||
})
|
});
|
||||||
formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula);
|
formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula);
|
||||||
return nativeReplaceFormulaData(formula, data, { missing, warn });
|
return nativeReplaceFormulaData(formula, data, { missing, warn });
|
||||||
};
|
};
|
||||||
|
|
@ -258,3 +258,39 @@ export const damageKeyToNumber = key => {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default function constructHTMLButton({
|
||||||
|
label,
|
||||||
|
dataset = {},
|
||||||
|
classes = [],
|
||||||
|
icon = '',
|
||||||
|
type = 'button',
|
||||||
|
disabled = false
|
||||||
|
}) {
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.type = type;
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(dataset)) {
|
||||||
|
button.dataset[key] = value;
|
||||||
|
}
|
||||||
|
button.classList.add(...classes);
|
||||||
|
if (icon) icon = `<i class="${icon}"></i> `;
|
||||||
|
if (disabled) button.disabled = true;
|
||||||
|
button.innerHTML = `${icon}${label}`;
|
||||||
|
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const adjustDice = (dice, decrease) => {
|
||||||
|
const diceKeys = Object.keys(diceTypes);
|
||||||
|
const index = diceKeys.indexOf(dice);
|
||||||
|
const newIndex = decrease ? Math.max(index - 1, 0) : Math.min(index + 1, diceKeys.length - 1);
|
||||||
|
return diceTypes[diceKeys[newIndex]];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const adjustRange = (rangeVal, decrease) => {
|
||||||
|
const rangeKeys = Object.keys(range);
|
||||||
|
const index = rangeKeys.indexOf(rangeVal);
|
||||||
|
const newIndex = decrease ? Math.max(index - 1, 0) : Math.min(index + 1, rangeKeys.length - 1);
|
||||||
|
return range[rangeKeys[newIndex]];
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
html.querySelectorAll('.target-save-container').forEach(element =>
|
html.querySelectorAll('.target-save-container').forEach(element =>
|
||||||
element.addEventListener('click', event => this.onRollSave(event, data.message))
|
element.addEventListener('click', event => this.onRollSave(event, data.message))
|
||||||
);
|
);
|
||||||
|
html.querySelectorAll('.roll-all-save-button').forEach(element =>
|
||||||
|
element.addEventListener('click', event => this.onRollAllSave(event, data.message))
|
||||||
|
);
|
||||||
html.querySelectorAll('.duality-action-effect').forEach(element =>
|
html.querySelectorAll('.duality-action-effect').forEach(element =>
|
||||||
element.addEventListener('click', event => this.onApplyEffect(event, data.message))
|
element.addEventListener('click', event => this.onApplyEffect(event, data.message))
|
||||||
);
|
);
|
||||||
|
|
@ -33,6 +36,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
element.addEventListener('mouseleave', this.unhoverTarget);
|
element.addEventListener('mouseleave', this.unhoverTarget);
|
||||||
element.addEventListener('click', this.clickTarget);
|
element.addEventListener('click', this.clickTarget);
|
||||||
});
|
});
|
||||||
|
html.querySelectorAll('.button-target-selection').forEach(element => {
|
||||||
|
element.addEventListener('click', event => this.onTargetSelection(event, data.message));
|
||||||
|
});
|
||||||
html.querySelectorAll('.damage-button').forEach(element =>
|
html.querySelectorAll('.damage-button').forEach(element =>
|
||||||
element.addEventListener('click', event => this.onDamage(event, data.message))
|
element.addEventListener('click', event => this.onDamage(event, data.message))
|
||||||
);
|
);
|
||||||
|
|
@ -107,7 +113,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
tokenId = event.target.closest('[data-token]')?.dataset.token,
|
tokenId = event.target.closest('[data-token]')?.dataset.token,
|
||||||
token = game.canvas.tokens.get(tokenId);
|
token = game.canvas.tokens.get(tokenId);
|
||||||
if (!token?.actor || !token.isOwner) return true;
|
if (!token?.actor || !token.isOwner) return true;
|
||||||
console.log(token.actor.canUserModify(game.user, 'update'));
|
|
||||||
if (message.system.source.item && message.system.source.action) {
|
if (message.system.source.item && message.system.source.action) {
|
||||||
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
|
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
|
||||||
if (!action || !action?.hasSave) return;
|
if (!action || !action?.hasSave) return;
|
||||||
|
|
@ -115,6 +120,16 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onRollAllSave = async (event, message) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const targets = event.target.parentElement.querySelectorAll(
|
||||||
|
'.target-section > [data-token] .target-save-container'
|
||||||
|
);
|
||||||
|
targets.forEach(el => {
|
||||||
|
el.dispatchEvent(new PointerEvent('click', { shiftKey: true }));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onApplyEffect = async (event, message) => {
|
onApplyEffect = async (event, message) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const actor = await this.getActor(message.system.source.actor);
|
const actor = await this.getActor(message.system.source.actor);
|
||||||
|
|
@ -122,10 +137,38 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
if (message.system.source.item && message.system.source.action) {
|
if (message.system.source.item && message.system.source.action) {
|
||||||
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
|
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
|
||||||
if (!action || !action?.applyEffects) return;
|
if (!action || !action?.applyEffects) return;
|
||||||
await action.applyEffects(event, message);
|
const { isHit, targets } = this.getTargetList(event, message);
|
||||||
|
if (targets.length === 0)
|
||||||
|
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
||||||
|
await action.applyEffects(event, message, targets);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onTargetSelection = async (event, message) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const targetSelection = Boolean(event.target.dataset.targetHit),
|
||||||
|
msg = ui.chat.collection.get(message._id);
|
||||||
|
if (msg.system.targetSelection === targetSelection) return;
|
||||||
|
if (targetSelection !== true && !Array.from(game.user.targets).length)
|
||||||
|
return ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
||||||
|
msg.system.targetSelection = targetSelection;
|
||||||
|
msg.system.prepareDerivedData();
|
||||||
|
ui.chat.updateMessage(msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
getTargetList = (event, message) => {
|
||||||
|
const targetSelection = event.target
|
||||||
|
.closest('.message-content')
|
||||||
|
.querySelector('.button-target-selection.target-selected'),
|
||||||
|
isHit = Boolean(targetSelection.dataset.targetHit);
|
||||||
|
return {
|
||||||
|
isHit,
|
||||||
|
targets: isHit
|
||||||
|
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id))
|
||||||
|
: Array.from(game.user.targets)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
hoverTarget = event => {
|
hoverTarget = event => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const token = canvas.tokens.get(event.currentTarget.dataset.token);
|
const token = canvas.tokens.get(event.currentTarget.dataset.token);
|
||||||
|
|
@ -150,15 +193,15 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
|
|
||||||
onDamage = async (event, message) => {
|
onDamage = async (event, message) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const targets = event.currentTarget.dataset.targetHit
|
const { isHit, targets } = this.getTargetList(event, message);
|
||||||
? message.system.targets.map(target => game.canvas.tokens.get(target.id))
|
|
||||||
: Array.from(game.user.targets);
|
|
||||||
|
|
||||||
if(message.system.onSave && event.currentTarget.dataset.targetHit) {
|
if (message.system.onSave && isHit) {
|
||||||
const pendingingSaves = message.system.targets.filter(target => target.hit && target.saved.success === null);
|
const pendingingSaves = message.system.targets.filter(
|
||||||
|
target => target.hit && target.saved.success === null
|
||||||
|
);
|
||||||
if (pendingingSaves.length) {
|
if (pendingingSaves.length) {
|
||||||
const confirm = await foundry.applications.api.DialogV2.confirm({
|
const confirm = await foundry.applications.api.DialogV2.confirm({
|
||||||
window: {title: "Pending Reaction Rolls found"},
|
window: { title: 'Pending Reaction Rolls found' },
|
||||||
content: `<p>Some Tokens still need to roll their Reaction Roll.</p><p>Are you sure you want to continue ?</p><p><i>Undone reaction rolls will be considered as failed</i></p>`
|
content: `<p>Some Tokens still need to roll their Reaction Roll.</p><p>Are you sure you want to continue ?</p><p><i>Undone reaction rolls will be considered as failed</i></p>`
|
||||||
});
|
});
|
||||||
if (!confirm) return;
|
if (!confirm) return;
|
||||||
|
|
@ -169,7 +212,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
||||||
for (let target of targets) {
|
for (let target of targets) {
|
||||||
let damage = message.system.roll.total;
|
let damage = message.system.roll.total;
|
||||||
if(message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true) damage = Math.ceil(damage * (SYSTEM.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1));
|
if (message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true)
|
||||||
|
damage = Math.ceil(damage * (SYSTEM.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1));
|
||||||
|
|
||||||
await target.actor.takeDamage(damage, message.system.roll.type);
|
await target.actor.takeDamage(damage, message.system.roll.type);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,25 @@ fieldset.daggerheart.chat {
|
||||||
border-left-width: 0;
|
border-left-width: 0;
|
||||||
border-right-width: 0;
|
border-right-width: 0;
|
||||||
border-bottom-width: 0;
|
border-bottom-width: 0;
|
||||||
|
legend {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
content: '\f0d8';
|
||||||
|
font-family: 'Font Awesome 6 Pro';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.expanded {
|
||||||
|
legend:before,
|
||||||
|
legend:after {
|
||||||
|
content: '\f0d7';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.daggerheart.chat {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.daggerheart.chat {
|
.daggerheart.chat {
|
||||||
|
|
@ -209,6 +228,28 @@ fieldset.daggerheart.chat {
|
||||||
margin: -@fullMargin 0;
|
margin: -@fullMargin 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.target-selection {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
input[type='radio'] {
|
||||||
|
display: none;
|
||||||
|
&:checked + label {
|
||||||
|
text-shadow: 0px 0px 4px #ce5937;
|
||||||
|
}
|
||||||
|
&:not(:checked) + label {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.75;
|
||||||
|
&.target-selected {
|
||||||
|
text-shadow: 0px 0px 4px #ce5937;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.target-section {
|
.target-section {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
|
|
||||||
|
|
@ -234,7 +275,8 @@ fieldset.daggerheart.chat {
|
||||||
background: @miss;
|
background: @miss;
|
||||||
}
|
}
|
||||||
|
|
||||||
img, .target-save-container {
|
img,
|
||||||
|
.target-save-container {
|
||||||
width: 22px;
|
width: 22px;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
|
|
@ -284,6 +326,10 @@ fieldset.daggerheart.chat {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:not(.expanded) .dice-tooltip {
|
||||||
|
grid-template-rows: 0fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.domain-card {
|
&.domain-card {
|
||||||
|
|
@ -337,6 +383,17 @@ fieldset.daggerheart.chat {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
button {
|
||||||
|
&.inner-button {
|
||||||
|
--button-size: 1.25rem;
|
||||||
|
--input-height: 1.25rem;
|
||||||
|
padding: 0 0.25rem;
|
||||||
|
margin: 5px 1px -4px auto;
|
||||||
|
&.inner-button-right {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[data-use-perm='false'] {
|
[data-use-perm='false'] {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
@ -347,7 +404,7 @@ fieldset.daggerheart.chat {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
&::after {
|
&::after {
|
||||||
content: "??";
|
content: '??';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -359,6 +416,12 @@ fieldset.daggerheart.chat {
|
||||||
fieldset.daggerheart.chat {
|
fieldset.daggerheart.chat {
|
||||||
border-top-width: 0;
|
border-top-width: 0;
|
||||||
display: contents;
|
display: contents;
|
||||||
|
legend {
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.message-header {
|
.message-header {
|
||||||
color: var(--color-light-3);
|
color: var(--color-light-3);
|
||||||
|
|
@ -436,6 +499,7 @@ fieldset.daggerheart.chat {
|
||||||
.dice {
|
.dice {
|
||||||
.dice-rolls {
|
.dice-rolls {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
&.duality {
|
||||||
li {
|
li {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -447,6 +511,7 @@ fieldset.daggerheart.chat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.duality-modifier {
|
.duality-modifier {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
|
|
@ -457,6 +522,11 @@ fieldset.daggerheart.chat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.target-selection {
|
||||||
|
label {
|
||||||
|
color: var(--color-light-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
.target-section {
|
.target-section {
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
border: 2px solid;
|
border: 2px solid;
|
||||||
|
|
@ -500,6 +570,31 @@ fieldset.daggerheart.chat {
|
||||||
margin-right: -8px;
|
margin-right: -8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.duality-result {
|
||||||
|
display: flex;
|
||||||
|
color: var(--color-light-1);
|
||||||
|
text-shadow: 0 0 1px black;
|
||||||
|
font-weight: bold;
|
||||||
|
background: var(--color-dark-1);
|
||||||
|
padding: 4px;
|
||||||
|
border-color: black;
|
||||||
|
min-height: unset;
|
||||||
|
height: 26px;
|
||||||
|
flex: unset;
|
||||||
|
margin: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
align-self: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
&.inner-button {
|
||||||
|
color: var(--color-light-1);
|
||||||
|
text-shadow: 0 0 1px black;
|
||||||
|
font-weight: bold;
|
||||||
|
background: var(--color-dark-1);
|
||||||
|
border-color: black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
.theme-light {
|
.theme-light {
|
||||||
.daggerheart.dh-style.countdown {
|
.daggerheart.dh-style.countdown {
|
||||||
&.minimized .minimized-view .mini-countdown-container {
|
.minimized-view .mini-countdown-container {
|
||||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -26,18 +26,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.minimized {
|
|
||||||
height: auto !important;
|
|
||||||
max-height: unset !important;
|
|
||||||
max-width: 740px !important;
|
|
||||||
width: auto !important;
|
|
||||||
|
|
||||||
.window-content {
|
|
||||||
display: flex;
|
|
||||||
padding: 4px 8px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.minimized-view {
|
.minimized-view {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
@ -73,7 +61,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -25,6 +25,23 @@
|
||||||
@import './less/actors/character/biography.less';
|
@import './less/actors/character/biography.less';
|
||||||
@import './less/actors/character/features.less';
|
@import './less/actors/character/features.less';
|
||||||
|
|
||||||
|
@import './less/actors/adversary/header.less';
|
||||||
|
@import './less/actors/adversary/sheet.less';
|
||||||
|
@import './less/actors/adversary/sidebar.less';
|
||||||
|
|
||||||
|
@import './less/actors/environment/header.less';
|
||||||
|
@import './less/actors/environment/sheet.less';
|
||||||
|
|
||||||
|
@import './less/applications/header.less';
|
||||||
|
@import './less/applications/adversary-settings/sheet.less';
|
||||||
|
@import './less/applications/adversary-settings/experiences.less';
|
||||||
|
@import './less/applications/adversary-settings/actions.less';
|
||||||
|
|
||||||
|
@import './less/applications/environment-settings/actions.less';
|
||||||
|
@import './less/applications/environment-settings/adversaries.less';
|
||||||
|
|
||||||
|
@import './less/actors/companion/sheet.less';
|
||||||
|
|
||||||
@import './less/actors/adversary.less';
|
@import './less/actors/adversary.less';
|
||||||
@import './less/actors/environment.less';
|
@import './less/actors/environment.less';
|
||||||
|
|
||||||
|
|
@ -36,6 +53,7 @@
|
||||||
@import './less/utils/fonts.less';
|
@import './less/utils/fonts.less';
|
||||||
|
|
||||||
@import './less/global/sheet.less';
|
@import './less/global/sheet.less';
|
||||||
|
@import './less/global/dialog.less';
|
||||||
@import './less/global/elements.less';
|
@import './less/global/elements.less';
|
||||||
@import './less/global/tab-navigation.less';
|
@import './less/global/tab-navigation.less';
|
||||||
@import './less/global/tab-form-footer.less';
|
@import './less/global/tab-form-footer.less';
|
||||||
|
|
@ -45,6 +63,7 @@
|
||||||
@import './less/global/feature-section.less';
|
@import './less/global/feature-section.less';
|
||||||
@import './less/global/inventory-item.less';
|
@import './less/global/inventory-item.less';
|
||||||
@import './less/global/inventory-fieldset-items.less';
|
@import './less/global/inventory-fieldset-items.less';
|
||||||
|
@import './less/global/prose-mirror.less';
|
||||||
@import "./less/global/filter-menu.less";
|
@import "./less/global/filter-menu.less";
|
||||||
|
|
||||||
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
||||||
|
|
|
||||||
20
styles/less/actors/adversary/actions.less
Normal file
20
styles/less/actors/adversary/actions.less
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.sheet.daggerheart.actor.dh-style.adversary {
|
||||||
|
.tab.actions {
|
||||||
|
.action-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
overflow-y: auto;
|
||||||
|
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
||||||
|
padding: 20px 0;
|
||||||
|
padding-top: 10px;
|
||||||
|
height: 95%;
|
||||||
|
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
80
styles/less/actors/adversary/header.less
Normal file
80
styles/less/actors/adversary/header.less
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.sheet.daggerheart.actor.dh-style.adversary {
|
||||||
|
.adversary-header-sheet {
|
||||||
|
padding: 0 15px;
|
||||||
|
padding-top: 36px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.name-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
input[type='text'] {
|
||||||
|
font-size: 32px;
|
||||||
|
height: 42px;
|
||||||
|
text-align: start;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
outline: 2px solid light-dark(@dark, @golden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
font: @font-body;
|
||||||
|
|
||||||
|
background: light-dark(@dark-15, @beige-15);
|
||||||
|
border: 1px solid light-dark(@dark, @beige);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.adversary-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px 0;
|
||||||
|
|
||||||
|
.description,
|
||||||
|
.motives-and-tatics {
|
||||||
|
font-family: @font-body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.adversary-navigation {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
styles/less/actors/adversary/sheet.less
Normal file
28
styles/less/actors/adversary/sheet.less
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.sheet.daggerheart.actor.dh-style.adversary {
|
||||||
|
.window-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 275px 1fr;
|
||||||
|
grid-template-rows: 283px 1fr;
|
||||||
|
gap: 15px 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.adversary-sidebar-sheet {
|
||||||
|
grid-row: 1 / span 2;
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adversary-header-sheet {
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
grid-row: 2;
|
||||||
|
grid-column: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
341
styles/less/actors/adversary/sidebar.less
Normal file
341
styles/less/actors/adversary/sidebar.less
Normal file
|
|
@ -0,0 +1,341 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.sheet.daggerheart.actor.dh-style.adversary {
|
||||||
|
.adversary-sidebar-sheet {
|
||||||
|
width: 275px;
|
||||||
|
min-width: 275px;
|
||||||
|
border-right: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
|
|
||||||
|
.theme-light & {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portrait {
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 235px;
|
||||||
|
width: 275px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.death-roll-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.death-roll {
|
||||||
|
filter: grayscale(1);
|
||||||
|
|
||||||
|
.death-roll-btn {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
top: 30%;
|
||||||
|
right: 30%;
|
||||||
|
font-size: 6rem;
|
||||||
|
color: @beige;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-shadow: 0 0 8px @beige;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.threshold-section {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
background-color: light-dark(transparent, @dark-blue);
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
border-radius: 6px;
|
||||||
|
align-items: center;
|
||||||
|
width: fit-content;
|
||||||
|
height: 30px;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
|
||||||
|
&.threshold-value {
|
||||||
|
color: light-dark(@dark, @beige);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.threshold-legend {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -21px;
|
||||||
|
color: light-dark(@golden, @dark-blue);
|
||||||
|
background-color: light-dark(@dark-blue, @golden);
|
||||||
|
padding: 3px;
|
||||||
|
justify-self: anchor-center;
|
||||||
|
border-radius: 0 0 3px 3px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hope-value {
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
top: -20px;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
|
||||||
|
.resources-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
.status-bar {
|
||||||
|
position: relative;
|
||||||
|
width: 100px;
|
||||||
|
height: 40px;
|
||||||
|
justify-items: center;
|
||||||
|
|
||||||
|
.status-label {
|
||||||
|
position: relative;
|
||||||
|
top: 40px;
|
||||||
|
height: 22px;
|
||||||
|
width: 79px;
|
||||||
|
clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z');
|
||||||
|
background: light-dark(@dark-blue, @golden);
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 18px;
|
||||||
|
color: light-dark(@beige, @dark-blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.status-value {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
padding: 0 6px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
align-items: center;
|
||||||
|
width: 100px;
|
||||||
|
height: 40px;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 2;
|
||||||
|
color: @beige;
|
||||||
|
|
||||||
|
input[type='number'] {
|
||||||
|
background: transparent;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
width: 40px;
|
||||||
|
height: 30px;
|
||||||
|
text-align: center;
|
||||||
|
border: none;
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
color: @beige;
|
||||||
|
|
||||||
|
&.bar-input {
|
||||||
|
padding: 0;
|
||||||
|
color: @beige;
|
||||||
|
backdrop-filter: none;
|
||||||
|
background: transparent;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background: @semi-transparent-dark-blue;
|
||||||
|
backdrop-filter: blur(9.5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-label {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.progress-bar {
|
||||||
|
position: absolute;
|
||||||
|
appearance: none;
|
||||||
|
width: 100px;
|
||||||
|
height: 40px;
|
||||||
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
border-radius: 6px;
|
||||||
|
z-index: 1;
|
||||||
|
background: @dark-blue;
|
||||||
|
|
||||||
|
&::-webkit-progress-bar {
|
||||||
|
border: none;
|
||||||
|
background: @dark-blue;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
&::-webkit-progress-value {
|
||||||
|
background: @gradient-hp;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
&.stress-color::-webkit-progress-value {
|
||||||
|
background: @gradient-stress;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
&::-moz-progress-bar {
|
||||||
|
background: @gradient-hp;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
&.stress-color::-moz-progress-bar {
|
||||||
|
background: @gradient-stress;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-section {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.status-number {
|
||||||
|
justify-items: center;
|
||||||
|
|
||||||
|
.status-value {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 50px;
|
||||||
|
height: 30px;
|
||||||
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
border-bottom: none;
|
||||||
|
border-radius: 6px 6px 0 0;
|
||||||
|
padding: 0 6px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: light-dark(transparent, @dark-blue);
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
&.armor-slots {
|
||||||
|
width: 80px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-label {
|
||||||
|
padding: 2px 10px;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: light-dark(@dark-blue, @golden);
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 18px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: light-dark(@beige, @dark-blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-sidebar-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.inventory-item {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attack-section {
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.items-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.experience-section {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.experience-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.experience-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
width: 250px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.experience-name {
|
||||||
|
width: 180px;
|
||||||
|
text-align: start;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: @font-body;
|
||||||
|
color: light-dark(@dark, @beige);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.experience-value {
|
||||||
|
height: 25px;
|
||||||
|
width: 35px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: @font-body;
|
||||||
|
color: light-dark(@dark, @beige);
|
||||||
|
align-content: center;
|
||||||
|
text-align: center;
|
||||||
|
background: url(../assets/svg/experience-shield.svg) no-repeat;
|
||||||
|
|
||||||
|
.theme-light & {
|
||||||
|
background: url('../assets/svg/experience-shield-light.svg') no-repeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reaction-section {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,9 +24,5 @@
|
||||||
grid-row: 2;
|
grid-row: 2;
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.old-sheet {
|
|
||||||
width: 500px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@
|
||||||
|
|
||||||
.portrait {
|
.portrait {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 235px;
|
|
||||||
width: 275px;
|
|
||||||
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|
@ -130,6 +128,7 @@
|
||||||
border: 1px solid light-dark(@dark-blue, @golden);
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
background: @dark-blue;
|
||||||
|
|
||||||
&::-webkit-progress-bar {
|
&::-webkit-progress-bar {
|
||||||
border: none;
|
border: none;
|
||||||
|
|
@ -144,13 +143,9 @@
|
||||||
background: @gradient-stress;
|
background: @gradient-stress;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
&::-moz-progress-value,
|
|
||||||
&::-moz-progress-bar {
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-progress-bar {
|
&::-moz-progress-bar {
|
||||||
background: @gradient-hp;
|
background: @gradient-hp;
|
||||||
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
&.stress-color::-moz-progress-bar {
|
&.stress-color::-moz-progress-bar {
|
||||||
background: @gradient-stress;
|
background: @gradient-stress;
|
||||||
|
|
|
||||||
11
styles/less/actors/companion/sheet.less
Normal file
11
styles/less/actors/companion/sheet.less
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
.application.sheet.daggerheart.actor.dh-style.companion {
|
||||||
|
.profile {
|
||||||
|
height: 80px;
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.temp-container {
|
||||||
|
position: relative;
|
||||||
|
top: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
140
styles/less/actors/environment/header.less
Normal file
140
styles/less/actors/environment/header.less
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.sheet.daggerheart.actor.dh-style.environment {
|
||||||
|
.environment-header-sheet {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: start;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.profile {
|
||||||
|
width: 100%;
|
||||||
|
height: 235px;
|
||||||
|
object-fit: cover;
|
||||||
|
mask-image: linear-gradient(0deg, transparent 0%, black 10%);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
top: -45px;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 0 20px;
|
||||||
|
margin-bottom: -30px;
|
||||||
|
|
||||||
|
.item-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
padding-bottom: 0;
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
font: @font-body;
|
||||||
|
|
||||||
|
background: light-dark(@dark-15, @beige-15);
|
||||||
|
border: 1px solid light-dark(@dark, @beige);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-number {
|
||||||
|
justify-items: center;
|
||||||
|
|
||||||
|
.status-value {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 50px;
|
||||||
|
height: 30px;
|
||||||
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
border-bottom: none;
|
||||||
|
border-radius: 6px 6px 0 0;
|
||||||
|
padding: 0 6px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: light-dark(transparent, @dark-blue);
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
&.armor-slots {
|
||||||
|
width: 80px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-label {
|
||||||
|
padding: 2px 10px;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: light-dark(@dark-blue, @golden);
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 18px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: light-dark(@beige, @dark-blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
input[type='text'] {
|
||||||
|
font-size: 32px;
|
||||||
|
height: 42px;
|
||||||
|
text-align: start;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
|
||||||
|
&:hover[type='text'],
|
||||||
|
&:focus[type='text'] {
|
||||||
|
box-shadow: none;
|
||||||
|
outline: 2px solid light-dark(@dark-blue, @golden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.environment-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
|
||||||
|
.description,
|
||||||
|
.impulses {
|
||||||
|
text-align: start;
|
||||||
|
font-family: @font-body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.environment-navigation {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
styles/less/actors/environment/sheet.less
Normal file
18
styles/less/actors/environment/sheet.less
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.sheet.daggerheart.actor.dh-style.environment {
|
||||||
|
.theme-light & {
|
||||||
|
background: url('../assets/parchments/dh-parchment-light.png');
|
||||||
|
}
|
||||||
|
.theme-dark & {
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
styles/less/applications/adversary-settings/actions.less
Normal file
74
styles/less/applications/adversary-settings/actions.less
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.daggerheart.dh-style.dialog {
|
||||||
|
.tab.actions {
|
||||||
|
max-height: 450px;
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
|
|
||||||
|
.add-action-btn {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.action-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 40px 1fr auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-family: @font-body;
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
background: light-dark(@dark-15, @beige-15);
|
||||||
|
border: 1px solid light-dark(@dark, @beige);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
a {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
styles/less/applications/adversary-settings/experiences.less
Normal file
28
styles/less/applications/adversary-settings/experiences.less
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.daggerheart.dh-style.dialog {
|
||||||
|
.tab.experiences {
|
||||||
|
.add-experience-btn {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.experience-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.experience-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 3fr 1fr 30px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
styles/less/applications/adversary-settings/sheet.less
Normal file
18
styles/less/applications/adversary-settings/sheet.less
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.daggerheart.dh-style.dialog {
|
||||||
|
.tab {
|
||||||
|
&.details.active,
|
||||||
|
&.attack.active {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldsets-section {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
74
styles/less/applications/environment-settings/actions.less
Normal file
74
styles/less/applications/environment-settings/actions.less
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.daggerheart.dh-style.dialog.environment-settings {
|
||||||
|
.tab.actions {
|
||||||
|
max-height: 450px;
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
|
|
||||||
|
.add-action-btn {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.action-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 40px 1fr auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-family: @font-body;
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
background: light-dark(@dark-15, @beige-15);
|
||||||
|
border: 1px solid light-dark(@dark, @beige);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
a {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.daggerheart.dh-style.dialog {
|
||||||
|
.tab.adversaries {
|
||||||
|
max-height: 450px;
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
|
|
||||||
|
.add-action-btn {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: start;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.category-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adversaries-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.adversaries-dragger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
border: 1px dashed light-dark(@dark-blue-50, @beige-50);
|
||||||
|
border-radius: 3px;
|
||||||
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
|
font-family: @font-body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
styles/less/applications/header.less
Normal file
20
styles/less/applications/header.less
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
@import '../utils/colors.less';
|
||||||
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.daggerheart.dh-style.dialog {
|
||||||
|
.window-content {
|
||||||
|
.dialog-header {
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
h1 {
|
||||||
|
font-family: @font-subtitle;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 24px;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
styles/less/global/dialog.less
Normal file
45
styles/less/global/dialog.less
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
@import '../utils/colors.less';
|
||||||
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
|
.theme-light {
|
||||||
|
.application.dialog.dh-style {
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark {
|
||||||
|
.application.dialog.dh-style {
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.application.dialog.dh-style {
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
.window-header {
|
||||||
|
background: transparent;
|
||||||
|
border-bottom: none;
|
||||||
|
color: light-dark(@dark-blue, @beige);
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: light-dark(@dark-blue, @beige);
|
||||||
|
font-family: @font-body;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: light-dark(@dark-blue, @beige);
|
||||||
|
background: light-dark(transparent, @deep-black);
|
||||||
|
border: 1px solid light-dark(@dark-blue, transparent);
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.dh-style {
|
.application.dh-style {
|
||||||
border: 1px solid light-dark(@dark-blue, @golden);
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
|
||||||
input[type='text'],
|
input[type='text'],
|
||||||
|
|
@ -90,6 +90,7 @@
|
||||||
|
|
||||||
a:hover,
|
a:hover,
|
||||||
a.active {
|
a.active {
|
||||||
|
font-weight: bold;
|
||||||
text-shadow: 0 0 8px light-dark(@dark-blue, @golden);
|
text-shadow: 0 0 8px light-dark(@dark-blue, @golden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,9 +112,25 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.fit-height {
|
||||||
|
height: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
&.flex {
|
&.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
&.wrap {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px 20px;
|
||||||
|
}
|
||||||
|
.inline-child {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.checkbox {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.one-column {
|
&.one-column {
|
||||||
|
|
@ -160,10 +177,12 @@
|
||||||
.nest-inputs {
|
.nest-inputs {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
|
width: 100%;
|
||||||
label {
|
label {
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
@ -206,6 +225,8 @@
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
|
|
||||||
|
grid-column: 1/-1; //For item descriptions in sheet elements
|
||||||
|
|
||||||
transition:
|
transition:
|
||||||
opacity 0.3s ease-out,
|
opacity 0.3s ease-out,
|
||||||
transform 0.3s ease-out;
|
transform 0.3s ease-out;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.daggerheart.dh-style {
|
||||||
.items-list {
|
.items-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.daggerheart.dh-style {
|
||||||
.inventory-item {
|
.inventory-item {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 40px 1fr 60px;
|
grid-template-columns: 40px 1fr 60px;
|
||||||
|
|
@ -15,6 +15,10 @@
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
|
||||||
|
&.actor-img {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-label {
|
.item-label {
|
||||||
|
|
|
||||||
24
styles/less/global/prose-mirror.less
Normal file
24
styles/less/global/prose-mirror.less
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
@import '../utils/colors.less';
|
||||||
|
|
||||||
|
.application {
|
||||||
|
prose-mirror {
|
||||||
|
height: 100% !important;
|
||||||
|
|
||||||
|
.editor-menu {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.editor-content {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
|
h1 {
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,13 @@
|
||||||
grid-template-columns: 1fr 4fr 1fr;
|
grid-template-columns: 1fr 4fr 1fr;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.sheet.daggerheart.dh-style {
|
.daggerheart.dh-style {
|
||||||
.tab-navigation {
|
.tab-navigation {
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.feature-tab {
|
.feature-tab {
|
||||||
border: none;
|
border: none;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,6 +218,15 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.levelup-radio-choices {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.levelup-summary-container {
|
.levelup-summary-container {
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"character": {
|
"character": {
|
||||||
"htmlFields": ["story", "description", "scars.*.description"]
|
"htmlFields": ["story", "description", "scars.*.description"]
|
||||||
},
|
},
|
||||||
|
"companion": {},
|
||||||
"adversary": {
|
"adversary": {
|
||||||
"htmlFields": ["description", "motivesAndTactics"]
|
"htmlFields": ["description", "motivesAndTactics"]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<section class="tooltip-part">
|
<section class="tooltip-part">
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
{{#each roll.dice}}
|
{{#each roll.dice as | dice index |}}
|
||||||
<header class="part-header flexrow">
|
<header class="part-header flexrow">
|
||||||
<span class="part-formula">{{formula}}</span>
|
<span class="part-formula">{{formula}}</span>
|
||||||
<span class="part-total">{{total}}</span>
|
<span class="part-total">{{total}}</span>
|
||||||
|
|
@ -17,9 +17,11 @@
|
||||||
<li class="roll die {{../dice}}{{#if discarded}} discarded{{/if}} min">{{result}}</li>
|
<li class="roll die {{../dice}}{{#if discarded}} discarded{{/if}} min">{{result}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ol>
|
</ol>
|
||||||
|
{{#if (eq index 0)}}
|
||||||
<div class="attack-roll-advantage-container">
|
<div class="attack-roll-advantage-container">
|
||||||
{{#if (eq ../roll.advantage.type 1)}}{{localize "DAGGERHEART.General.Advantage.Full"}}{{/if}}{{#if (eq ../roll.advantage.type -1)}}{{localize "DAGGERHEART.General.Disadvantage.Full"}}{{/if}}
|
{{#if (eq ../roll.advantage.type 1)}}{{localize "DAGGERHEART.General.Advantage.Full"}}{{/if}}{{#if (eq ../roll.advantage.type -1)}}{{localize "DAGGERHEART.General.Disadvantage.Full"}}{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -31,13 +33,30 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<fieldset class="dice-roll daggerheart chat roll expanded{{#unless damage.roll}} hidden{{/unless}}" data-action="expandRoll">
|
||||||
|
<legend class="dice-flavor">Damage</legend>
|
||||||
|
<div class="dice-result">
|
||||||
|
<div class="dice-tooltip">
|
||||||
|
<div class="wrapper">
|
||||||
|
{{> 'systems/daggerheart/templates/chat/parts/damage-chat.hbs' damage=damage noTitle=true}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}}
|
{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}}
|
||||||
{{#if hasDamage}}
|
{{#if hasDamage}}
|
||||||
<div class="dice-roll daggerheart chat roll">
|
<div class="dice-roll daggerheart chat roll">
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="flexrow">
|
{{#if damage.roll}}
|
||||||
<button class="duality-action duality-action-damage" data-value="{{roll.total}}"><span>Roll Damage</span></button>
|
<div class="dice-actions">
|
||||||
|
{{!-- <button class="damage-button" data-target-hit="true" {{#if (eq targets.length 0)}}disabled{{/if}}>{{localize "DAGGERHEART.Chat.DamageRoll.DealDamageToTargets"}}</button> --}}
|
||||||
|
<button class="damage-button">{{localize "DAGGERHEART.Chat.DamageRoll.DealDamage"}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="flexrow">
|
||||||
|
<button class="duality-action duality-action-damage" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.Chat.DamageRoll.RollDamage"}}</span></button>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
@ -1,29 +1,4 @@
|
||||||
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
{{> 'systems/daggerheart/templates/chat/parts/damage-chat.hbs' damage=this}}
|
||||||
<div class="dice-flavor">{{title}}</div>
|
|
||||||
<div class="dice-result">
|
|
||||||
<div class="dice-formula">{{roll.formula}}</div>
|
|
||||||
<div class="dice-tooltip">
|
|
||||||
<div class="wrapper">
|
|
||||||
<section class="tooltip-part">
|
|
||||||
{{#each roll.dice}}
|
|
||||||
<div class="dice">
|
|
||||||
<header class="part-header flexrow">
|
|
||||||
<span class="part-formula">{{formula}}</span>
|
|
||||||
<span class="part-total">{{total}}</span>
|
|
||||||
</header>
|
|
||||||
<ol class="dice-rolls">
|
|
||||||
{{#each results}}
|
|
||||||
<li class="roll die {{../denomination}} min">{{result}}</li>
|
|
||||||
{{/each}}
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="dice-total">{{roll.total}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}}
|
{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}}
|
||||||
<div class="dice-roll daggerheart chat roll">
|
<div class="dice-roll daggerheart chat roll">
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,30 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#each roll.extra as | extra | }}
|
||||||
|
<div class="dice">
|
||||||
|
<header class="part-header flexrow">
|
||||||
|
<span class="part-formula">
|
||||||
|
<span>1{{extra.dice}}</span>
|
||||||
|
</span>
|
||||||
|
<span class="part-total">{{extra.value}}</span>
|
||||||
|
</header>
|
||||||
|
<div class="flexrow">
|
||||||
|
<ol class="dice-rolls">
|
||||||
|
<li class="roll die {{extra.dice}}">
|
||||||
|
<div class="dice-container">
|
||||||
|
<div class="dice-inner-container">
|
||||||
|
<div class="dice-wrapper">
|
||||||
|
<img class="dice" src="../icons/svg/{{extra.dice}}-grey.svg"/>
|
||||||
|
</div>
|
||||||
|
<div class="dice-value">{{extra.value}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
{{#if roll.modifierTotal}}<div class="duality-modifier">{{#if (gt roll.modifierTotal 0)}}+{{/if}}{{roll.modifierTotal}}</div>{{/if}}
|
{{#if roll.modifierTotal}}<div class="duality-modifier">{{#if (gt roll.modifierTotal 0)}}+{{/if}}{{roll.modifierTotal}}</div>{{/if}}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -94,12 +118,26 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<fieldset class="dice-roll daggerheart chat roll expanded{{#unless damage.roll}} hidden{{/unless}}" data-action="expandRoll">
|
||||||
|
<legend class="dice-flavor">Damage</legend>
|
||||||
|
<div class="dice-result">
|
||||||
|
<div class="dice-tooltip">
|
||||||
|
<div class="wrapper">
|
||||||
|
{{!-- {{> 'systems/daggerheart/templates/chat/parts/damage-chat.hbs' damage=damage noTitle=true}} --}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}}
|
{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}}
|
||||||
<div class="dice-roll daggerheart chat roll">
|
<div class="dice-roll daggerheart chat roll">
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-actions{{#unless (or hasDamage hasHealing)}} duality-alone{{/unless}}">
|
<div class="dice-actions{{#unless (or hasDamage hasHealing)}} duality-alone{{/unless}}">
|
||||||
{{#if hasDamage}}
|
{{#if hasDamage}}
|
||||||
|
{{#if damage.roll}}
|
||||||
|
<button class="duality-action damage-button" data-target-hit="true" data-value="{{roll.total}}"><span>Deal Damage</span></button>
|
||||||
|
{{else}}
|
||||||
<button class="duality-action duality-action-damage" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollDamage"}}</span></button>
|
<button class="duality-action duality-action-damage" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollDamage"}}</span></button>
|
||||||
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if hasHealing}}
|
{{#if hasHealing}}
|
||||||
<button class="duality-action duality-action-healing" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollHealing"}}</span></button>
|
<button class="duality-action duality-action-healing" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollHealing"}}</span></button>
|
||||||
|
|
|
||||||
27
templates/chat/parts/damage-chat.hbs
Normal file
27
templates/chat/parts/damage-chat.hbs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
|
{{#unless noTitle}}<div class="dice-flavor">{{damage.title}}</div>{{/unless}}
|
||||||
|
<div class="dice-result">
|
||||||
|
<div class="dice-formula">{{damage.roll.formula}}</div>
|
||||||
|
<div class="dice-tooltip">
|
||||||
|
<div class="wrapper">
|
||||||
|
<section class="tooltip-part">
|
||||||
|
{{#each damage.roll.dice}}
|
||||||
|
<div class="dice">
|
||||||
|
<header class="part-header flexrow">
|
||||||
|
<span class="part-formula">{{formula}}</span>
|
||||||
|
<span class="part-total">{{total}}</span>
|
||||||
|
</header>
|
||||||
|
<ol class="dice-rolls">
|
||||||
|
{{#each results}}
|
||||||
|
<li class="roll die {{../dice}} min">{{result}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
<div class="duality-result">Total: {{damage.roll.total}}</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dice-total">{{damage.roll.total}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -1,22 +1,29 @@
|
||||||
{{#if (gt targets.length 0)}}
|
{{#if (gt currentTargets.length 0)}}
|
||||||
<fieldset class="dice-roll daggerheart chat roll expanded" data-action="expandRoll">
|
<fieldset class="dice-roll daggerheart chat roll expanded" data-action="expandRoll">
|
||||||
<legend class="dice-flavor">Targets</legend>
|
<legend class="dice-flavor">Targets</legend>
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-tooltip">
|
<div class="dice-tooltip">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
|
<div class="target-selection">
|
||||||
|
<label class="button-target-selection{{#if @root.targetSelection}} target-selected{{/if}}" data-target-hit="true">{{localize "DAGGERHEART.Chat.DamageRoll.HitTarget"}}</label>
|
||||||
|
<label class="button-target-selection{{#unless @root.targetSelection}} target-selected{{/unless}}">{{localize "DAGGERHEART.Chat.DamageRoll.SelectedTarget"}}</label>
|
||||||
|
</div>
|
||||||
|
{{#if (and hasSave @root.targetSelection @root.hasHitTarget)}}
|
||||||
|
<button class="inner-button inner-button-right roll-all-save-button">Roll All <i class="fa-solid fa-shield"></i></button>
|
||||||
|
{{/if}}
|
||||||
<div class="target-section">
|
<div class="target-section">
|
||||||
{{#each targets as |target|}}
|
{{#each currentTargets as |target|}}
|
||||||
<div class="dice-total target-container {{#if target.hit}}hit{{else}}{{#if (not ../total.alternate)}}miss{{/if}}{{/if}}" data-token="{{target.id}}">
|
<div class="dice-total target-container {{#if target.hit}}hit{{else}}{{#if (not ../total.alternate)}}miss{{/if}}{{/if}}" data-token="{{target.id}}">
|
||||||
<img src="{{target.img}}" />
|
<img src="{{target.img}}" />
|
||||||
<div class="target-inner-container">
|
<div class="target-inner-container">
|
||||||
{{#if ../directDamage}}
|
{{#if (or ../directDamage (not @root.targetSelection))}}
|
||||||
<div data-perm-id="{{target.actorId}}"><span>{{target.name}}</span></div>
|
<div data-perm-id="{{target.actorId}}"><span>{{target.name}}</span></div>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if target.hit}}{{localize "Hit"}}{{else}}{{#if (not ../total.alternate)}}{{localize "Miss"}}{{else}}?{{/if}}{{/if}}
|
{{#if target.hit}}{{localize "Hit"}}{{else}}{{#if (not ../total.alternate)}}{{localize "Miss"}}{{else}}?{{/if}}{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{#if ../hasSave}}
|
{{#if (and ../hasSave target.hit @root.targetSelection)}}
|
||||||
<button class="target-save-container{{#if target.saved.result includeZero=true}} is-rolled{{/if}}"{{#unless target.hit}} style="visibility: hidden;"{{/unless}} data-perm-id="{{target.actorId}}">
|
<button class="target-save-container{{#if target.saved.result includeZero=true}} is-rolled{{/if}}" data-perm-id="{{target.actorId}}">
|
||||||
{{!-- {{target.saved.result}} --}}
|
{{!-- {{target.saved.result}} --}}
|
||||||
<i class="fa-solid {{#if target.saved.result includeZero=true}}{{#if target.saved.success}}fa-check{{else}}fa-xmark{{/if}}{{else}}fa-shield{{/if}}">
|
<i class="fa-solid {{#if target.saved.result includeZero=true}}{{#if target.saved.success}}fa-check{{else}}fa-xmark{{/if}}{{else}}fa-shield{{/if}}">
|
||||||
</i>
|
</i>
|
||||||
|
|
|
||||||
11
templates/sheets/actors/adversary/actions.hbs
Normal file
11
templates/sheets/actors/adversary/actions.hbs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<section
|
||||||
|
class='tab {{tabs.actions.cssClass}} {{tabs.actions.id}}'
|
||||||
|
data-tab='{{tabs.actions.id}}'
|
||||||
|
data-group='{{tabs.actions.group}}'
|
||||||
|
>
|
||||||
|
<div class="action-section">
|
||||||
|
{{#if document.system.actions}}
|
||||||
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize tabs.actions.label) type='action'}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
8
templates/sheets/actors/adversary/effects.hbs
Normal file
8
templates/sheets/actors/adversary/effects.hbs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<section
|
||||||
|
class='tab {{tabs.effects.cssClass}} {{tabs.effects.id}}'
|
||||||
|
data-tab='{{tabs.effects.id}}'
|
||||||
|
data-group='{{tabs.effects.group}}'
|
||||||
|
>
|
||||||
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.Global.activeEffects') type='effect'}}
|
||||||
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.Global.inativeEffects') type='effect'}}
|
||||||
|
</section>
|
||||||
|
|
@ -1,9 +1,43 @@
|
||||||
<header class='item-card-header'>
|
<header class='adversary-header-sheet'>
|
||||||
<img class='profile' src='{{source.img}}' data-action='editImage' data-edit='img' />
|
<line-div></line-div>
|
||||||
<div class='item-info'>
|
<div class="name-row">
|
||||||
<h1 class='item-name'><input type='text' name='name' value='{{source.name}}' /></h1>
|
<h1 class='actor-name'>
|
||||||
<div class='item-description'>
|
<input type='text' name='name' value='{{source.name}}' placeholder='Actor Name'
|
||||||
<h3>{{localize 'TYPES.Actor.adversary'}}</h3>
|
/>
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tags">
|
||||||
|
<div class="tag">
|
||||||
|
<span>
|
||||||
|
{{localize (concat 'DAGGERHEART.Tiers.' source.system.tier)}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="tag">
|
||||||
|
<span>
|
||||||
|
{{localize (concat 'DAGGERHEART.Adversary.Type.' source.system.type '.label')}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{#if (eq source.system.type 'horde')}}
|
||||||
|
<div class="tag">
|
||||||
|
<span>{{source.system.hordeHp}}</span>
|
||||||
|
<span>/HP</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<line-div></line-div>
|
||||||
|
<div class="adversary-info">
|
||||||
|
<div class="description">
|
||||||
|
<i>{{source.system.description}}</i>
|
||||||
|
</div>
|
||||||
|
<div class="motives-and-tatics">
|
||||||
|
<b>{{localize 'DAGGERHEART.Sheets.Adversary.FIELDS.motivesAndTactics.label'}}: </b>{{{source.system.motivesAndTactics}}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="adversary-navigation">
|
||||||
|
{{> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}}
|
||||||
|
<button data-action="openSettings">
|
||||||
|
<i class="fa-solid fa-wrench"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
<section
|
|
||||||
class='tab {{tabs.information.cssClass}} {{tabs.information.id}}'
|
|
||||||
data-tab='{{tabs.information.id}}'
|
|
||||||
data-group='{{tabs.information.group}}'
|
|
||||||
>
|
|
||||||
{{!-- <fieldset>
|
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.FIELDS.description.label" }}</legend>
|
|
||||||
|
|
||||||
{{formInput systemFields.description value=source.system.description}}
|
|
||||||
</fieldset> --}}
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.FIELDS.motivesAndTactics.label" }}</legend>
|
|
||||||
|
|
||||||
{{formInput systemFields.motivesAndTactics value=source.system.motivesAndTactics}}
|
|
||||||
</fieldset>
|
|
||||||
</section>
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
<section
|
|
||||||
class='tab {{tabs.main.cssClass}} {{tabs.main.id}}'
|
|
||||||
data-tab='{{tabs.main.id}}'
|
|
||||||
data-group='{{tabs.main.group}}'
|
|
||||||
>
|
|
||||||
<div class="adversary-container">
|
|
||||||
<button data-action="reactionRoll">Reaction Test</button>
|
|
||||||
<fieldset class="two-columns even">
|
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.General"}}</legend>
|
|
||||||
|
|
||||||
{{formGroup systemFields.tier value=source.system.tier localize=true}}
|
|
||||||
{{formGroup systemFields.type value=source.system.type localize=true}}
|
|
||||||
<div class="full-width">{{formGroup systemFields.difficulty value=source.system.difficulty}}</div>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.DamageThresholds"}}</legend>
|
|
||||||
|
|
||||||
{{formGroup systemFields.damageThresholds.fields.major value=source.system.damageThresholds.major}}
|
|
||||||
{{formGroup systemFields.damageThresholds.fields.severe value=source.system.damageThresholds.severe}}
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.HitPoints"}}</legend>
|
|
||||||
|
|
||||||
{{formGroup systemFields.resources.fields.hitPoints.fields.value value=source.system.resources.hitPoints.value}}
|
|
||||||
{{formGroup systemFields.resources.fields.hitPoints.fields.max value=source.system.resources.hitPoints.max}}
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Stress"}}</legend>
|
|
||||||
|
|
||||||
{{formGroup systemFields.resources.fields.stress.fields.value value=source.system.resources.stress.value}}
|
|
||||||
{{formGroup systemFields.resources.fields.stress.fields.max value=source.system.resources.stress.max}}
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Experiences"}} <a><i class="fa-solid fa-plus icon-button" data-action="addExperience"></i></a></legend>
|
|
||||||
|
|
||||||
{{#each source.system.experiences}}
|
|
||||||
<fieldset class="one-column">
|
|
||||||
<legend>{{this.name}} <a><i class="fa-solid fa-trash icon-button" data-action="removeExperience" data-experience="{{@key}}"></i></a></legend>
|
|
||||||
|
|
||||||
{{formGroup @root.systemFields.experiences.element.fields.name name=(concat "system.experiences." @key ".name") value=this.name }}
|
|
||||||
{{formGroup @root.systemFields.experiences.element.fields.value name=(concat "system.experiences." @key ".value") value=this.value }}
|
|
||||||
</fieldset>
|
|
||||||
{{/each}}
|
|
||||||
</fieldset>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset class="two-columns even">
|
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Attack"}}</legend>
|
|
||||||
<button data-action="attackConfigure">Configure</button>
|
|
||||||
<button data-action="attackRoll">Attack</button>
|
|
||||||
<fieldset class="action-category" style="grid-column: 1 / -1;">
|
|
||||||
<legend class="action-category-label" data-action="toggleSection" data-section="range">
|
|
||||||
<div>Name</div>
|
|
||||||
</legend>
|
|
||||||
<div class="action-category-data open">
|
|
||||||
{{formGroup systemFields.attack.fields.name value=source.system.attack.name name="system.attack.name"}}
|
|
||||||
{{formGroup systemFields.attack.fields.img value=source.img label="Icon" name="system.attack.img"}}
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div>
|
|
||||||
<fieldset class="action-category">
|
|
||||||
<legend class="action-category-label" data-action="toggleSection" data-section="range">
|
|
||||||
<div>Bonus to Hit</div>
|
|
||||||
</legend>
|
|
||||||
<div class="action-category-data open">
|
|
||||||
{{formField systemFields.attack.fields.roll.fields.bonus value=source.system.attack.roll.bonus name="system.attack.roll.bonus"}}
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
{{> 'systems/daggerheart/templates/views/actionTypes/range-target.hbs' fields=(object range=systemFields.attack.fields.range target=systemFields.attack.fields.target.fields) source=(object target=source.system.attack.target range=source.system.attack.range) path="system.attack."}}
|
|
||||||
</div>
|
|
||||||
{{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=source.system.attack.damage path="system.attack."}}
|
|
||||||
<div style="grid-column: 1 / -1;">
|
|
||||||
{{> 'systems/daggerheart/templates/views/actionTypes/effect.hbs' fields=systemFields.attack.fields.effects.element.fields source=source.system.attack.effects}}
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
10
templates/sheets/actors/adversary/notes.hbs
Normal file
10
templates/sheets/actors/adversary/notes.hbs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<section
|
||||||
|
class='tab {{tabs.notes.cssClass}} {{tabs.notes.id}}'
|
||||||
|
data-tab='{{tabs.notes.id}}'
|
||||||
|
data-group='{{tabs.notes.group}}'
|
||||||
|
>
|
||||||
|
<fieldset class="fit-height">
|
||||||
|
<legend>{{localize tabs.notes.label}}</legend>
|
||||||
|
{{formInput systemFields.notes value=document.system.notes enriched=document.system.notes localize=true toggled=true}}
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
111
templates/sheets/actors/adversary/sidebar.hbs
Normal file
111
templates/sheets/actors/adversary/sidebar.hbs
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
<aside class="adversary-sidebar-sheet">
|
||||||
|
<div class="portrait {{#if (gte source.system.resources.hitPoints.value source.system.resources.hitPoints.maxTotal)}}death-roll{{/if}}">
|
||||||
|
<img src="{{source.img}}" alt="{{source.name}}" data-action='editImage' data-edit="img">
|
||||||
|
<a class="death-roll-btn" data-tooltip="{{localize "DAGGERHEART.Sheets.PC.Health.DeathMoveTooltip"}}" data-action="makeDeathMove"><i class="fas fa-skull death-save" ></i></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-section">
|
||||||
|
<div class="resources-section">
|
||||||
|
<div class="status-bar">
|
||||||
|
<div class='status-value'>
|
||||||
|
<p><input class="bar-input" name="system.resources.hitPoints.value" value="{{source.system.resources.hitPoints.value}}" type="number"></p>
|
||||||
|
<p>/</p>
|
||||||
|
<p class="bar-label">{{source.system.resources.hitPoints.max}}</p>
|
||||||
|
</div>
|
||||||
|
<progress
|
||||||
|
class='progress-bar'
|
||||||
|
value='{{source.system.resources.hitPoints.value}}'
|
||||||
|
max='{{source.system.resources.hitPoints.max}}'
|
||||||
|
></progress>
|
||||||
|
<div class="status-label">
|
||||||
|
<h4>HP</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="status-bar">
|
||||||
|
<div class='status-value'>
|
||||||
|
<p><input class="bar-input" name="system.resources.stress.value" value="{{source.system.resources.stress.value}}" type="number"></p>
|
||||||
|
<p>/</p>
|
||||||
|
<p class="bar-label">{{source.system.resources.stress.max}}</p>
|
||||||
|
</div>
|
||||||
|
<progress
|
||||||
|
class='progress-bar stress-color'
|
||||||
|
value='{{source.system.resources.stress.value}}'
|
||||||
|
max='{{source.system.resources.stress.max}}'
|
||||||
|
></progress>
|
||||||
|
<div class="status-label">
|
||||||
|
<h4>Stress</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-section">
|
||||||
|
<div class="threshold-section">
|
||||||
|
<h4 class="threshold-label">{{localize "DAGGERHEART.Sheets.PC.Health.Minor"}}</h4>
|
||||||
|
<h4 class="threshold-value">{{document.system.damageThresholds.major}}</h4>
|
||||||
|
<h4 class="threshold-label">{{localize "DAGGERHEART.Sheets.PC.Health.Major"}}</h4>
|
||||||
|
<h4 class="threshold-value">{{document.system.damageThresholds.severe}}</h4>
|
||||||
|
<h4 class="threshold-label">{{localize "DAGGERHEART.Sheets.PC.Health.Severe"}}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-section">
|
||||||
|
<div class="status-number">
|
||||||
|
<div class='status-value armor-slots'>
|
||||||
|
{{#if source.system.difficulty}}
|
||||||
|
<p>{{source.system.difficulty}}</p>
|
||||||
|
{{else}}
|
||||||
|
<p>-</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<div class="status-label">
|
||||||
|
<h4>Difficulty</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-number">
|
||||||
|
<div class='status-value armor-slots'>
|
||||||
|
{{#if source.system.attack.target.amount}}
|
||||||
|
<p>{{source.system.attack.target.amount}}</p>
|
||||||
|
{{else}}
|
||||||
|
<p>-</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<div class="status-label">
|
||||||
|
<h4>Attack</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="attack-section">
|
||||||
|
<div class="title">
|
||||||
|
<side-line-div class="invert"></side-line-div>
|
||||||
|
<h3>Attack</h3>
|
||||||
|
<side-line-div></side-line-div>
|
||||||
|
</div>
|
||||||
|
<ul class="items-sidebar-list">
|
||||||
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=source.system.attack type=source.system.attack.systemPath isSidebar=true}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="experience-section">
|
||||||
|
<div class="title">
|
||||||
|
<side-line-div class="invert"></side-line-div>
|
||||||
|
<h3>Experience</h3>
|
||||||
|
<side-line-div></side-line-div>
|
||||||
|
</div>
|
||||||
|
<div class="experience-list">
|
||||||
|
{{#each source.system.experiences as |experience id|}}
|
||||||
|
<div class="experience-row">
|
||||||
|
<div class="experience-value">
|
||||||
|
+{{experience.modifier}}
|
||||||
|
</div>
|
||||||
|
<span class="experience-name">{{experience.name}}</span>
|
||||||
|
<div class="controls">
|
||||||
|
<a data-action="toChat" data-type="experience" data-uuid="{{id}}"><i class="fa-regular fa-message"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<line-div></line-div>
|
||||||
|
<div class="reaction-section">
|
||||||
|
<button data-action="reactionRoll">Reaction Test</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
@ -10,6 +10,12 @@
|
||||||
{{#if document.system.class.subclass}}
|
{{#if document.system.class.subclass}}
|
||||||
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(concat (localize 'TYPES.Item.subclass') ' - ' document.system.class.subclass.name) type='subclass'}}
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(concat (localize 'TYPES.Item.subclass') ' - ' document.system.class.subclass.name) type='subclass'}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if document.system.features}}
|
||||||
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize "DAGGERHEART.Sheets.PC.Features") type='features'}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if document.system.companionFeatures}}
|
||||||
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize "DAGGERHEART.Sheets.PC.CompanionFeatures") type='companionFeatures'}}
|
||||||
|
{{/if}}
|
||||||
{{#if document.system.community}}
|
{{#if document.system.community}}
|
||||||
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(concat (localize 'TYPES.Item.community') ' - ' document.system.community.name) type='community'}}
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(concat (localize 'TYPES.Item.community') ' - ' document.system.community.name) type='community'}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue