daggerheart/daggerheart.mjs
WBHarry 187ee3e1bd
Refactor/84 data models structure (#131)
* - Move all DataModel item files to a new 'items' subfolder for better organization
- Add _module.mjs file to simplify imports
- Update all import paths
- Rename class for use the new acronym DH

* FIX: remove unnecessary import

* FEAT: BaseDataItem class
add TODO comments for future improvements
FIX: Remove effect field on template
FIX: remove unused DhpEffects file

* FEAT: new FormulaField class
FEAT: add getRollData on BaseDataItem Class
FEAT: weapon
FIX: remove inventoryWeapon field on Weapon Data Model

* FEAT: add class prepareBaseData for domains

* FEAT: new ForeignDocumentUUIDField
FIX: Remove unnecessary fields
FEAT: use ForeignDocumentUUIDField in the Item Class DataModel

* FIX: remove wrong option in String Field

* FIX: remove unused import

* FIX: ADD htmlFields description in manifest

* FIX: minor fixes

* REFACTOR: rename folder `data/items` -> `data/item`
REFACTOR: rename folder `data/messages` -> `data/chat-message`.

* FIX: imports
FIX: items sheet new paths
FIX: ItemDataModelMetadata type jsdoc

* FEAT: formatting code
FIX: fix fields used
FEAT: add jsdoc

* 110 - Class Data Model (#111)

* Added PreCreate/Create/Delete logic for Class/Subclass and set it as foreignUUID fields in PC

* Moved methods into TypedModelData

* Simplified Subclass

* Fixed up data model and a basic placeholder template (#117)

* 118 - adversary data model (#119)

* Fixed datamodel and set up basic template in new style

* Added in a temp attack button, because why not

* Restored HitPoints counting up

* 113 - Character Data Model (#114)

* Improved Character datamodel

* Removed additional unneccessary getters

* Preliminary cleanup in the class sheet

* Cleanup of 'pc' references

* Corrected Duality rolling from Character

* Fix to damage roll

* Added a basic BaseDataActor data model

* Gathered exports

* getRollData recursion fix

* Feature/112 items use action datamodel (#127)

* Create new actions classes

* actions types - attack roll

* fixes before merge

* First PR

* Add daggerheart.css to gitignore

* Update ToDo

* Remove console log

* Fixed chat /dr roll

* Remove jQuery

* Fixed so the different chat themes work again

* Fixed duality roll buttons

* Fix to advantage/disadvantage shortcut

* Extand action to other item types

* Roll fixes

* Fixes to adversary rolls

* resources

* Fixed adversary dice

---------

Co-authored-by: WBHarry <williambjrklund@gmail.com>

* Feature/116-implementation-of-pseudo-documents (#125)

* FEAT: add baseDataModel logic

* FEAT: new PseudoDocumentsField
FIX: BasePseudoDocument 's getEmbeddedDocument

* FEAT: PseudoDocument class

* FEAT: add TypedPseudoDocument
REFACTOR: PreudoDocument
FIX: Typos Bug

* FIX: CONFIG types

* FEAT: basic PseudoDocumentSheet

* FIX: remove schema
ADD: input of example

---------

Co-authored-by: Joaquin Pereyra <joaquinpereyra98@users.noreply.github.com>
Co-authored-by: WBHarry <williambjrklund@gmail.com>

* Levelup Followup (#126)

* Levelup applies bonuses to character

* Added visualisation of domain card levels

* Fixed domaincard level max for selections in a tier

* A trait can now only be level up once within the same tier

---------

Co-authored-by: Joaquin Pereyra <joaquinpereyra98@users.noreply.github.com>
Co-authored-by: joaquinpereyra98 <24190917+joaquinpereyra98@users.noreply.github.com>
Co-authored-by: Dapoulp <74197441+Dapoulp@users.noreply.github.com>
2025-06-13 14:17:13 +02:00

284 lines
12 KiB
JavaScript

import { SYSTEM } from './module/config/system.mjs';
import * as applications from './module/applications/_module.mjs';
import * as models from './module/data/_module.mjs';
import * as documents from './module/documents/_module.mjs';
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
import DhCombatTracker from './module/ui/combatTracker.mjs';
import { GMUpdateEvent, handleSocketEvent, socketEvent } from './module/helpers/socket.mjs';
import { registerDHSettings } from './module/applications/settings.mjs';
import DhpChatLog from './module/ui/chatLog.mjs';
import DhpRuler from './module/ui/ruler.mjs';
import DhpTokenRuler from './module/ui/tokenRuler.mjs';
import { dualityRollEnricher } from './module/enrichers/DualityRollEnricher.mjs';
import { getCommandTarget, rollCommandToJSON, setDiceSoNiceForDualityRoll } from './module/helpers/utils.mjs';
import { abilities } from './module/config/actorConfig.mjs';
import Resources from './module/applications/resources.mjs';
import DHDualityRoll from './module/data/chat-message/dualityRoll.mjs';
globalThis.SYSTEM = SYSTEM;
Hooks.once('init', () => {
CONFIG.daggerheart = SYSTEM;
game.system.api = {
applications,
models,
documents
};
CONFIG.TextEditor.enrichers.push({
pattern: /\[\[\/dr\s?(.*?)\]\]/g,
enricher: dualityRollEnricher
});
CONFIG.statusEffects = Object.values(SYSTEM.GENERAL.conditions).map(x => ({
...x,
name: game.i18n.localize(x.name)
}));
CONFIG.Item.documentClass = documents.DhpItem;
//Registering the Item DataModel
CONFIG.Item.dataModels = models.items.config;
const { Items, Actors } = foundry.documents.collections;
Items.unregisterSheet('core', foundry.applications.sheets.ItemSheetV2);
Items.registerSheet(SYSTEM.id, applications.DhpAncestry, { types: ['ancestry'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.DhpCommunity, { types: ['community'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.DhpClassSheet, { types: ['class'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.DhpSubclass, { types: ['subclass'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.DhpFeatureSheet, { types: ['feature'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.DhpDomainCardSheet, { types: ['domainCard'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.DhpMiscellaneous, { types: ['miscellaneous'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.DhpConsumable, { types: ['consumable'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.DhpWeapon, { types: ['weapon'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.DhpArmor, { types: ['armor'], makeDefault: true });
CONFIG.Actor.documentClass = documents.DhpActor;
CONFIG.Actor.dataModels = models.actors.config;
Actors.unregisterSheet('core', foundry.applications.sheets.ActorSheetV2);
Actors.registerSheet(SYSTEM.id, applications.DhCharacterSheet, { types: ['character'], makeDefault: true });
Actors.registerSheet(SYSTEM.id, applications.DhpAdversarySheet, { types: ['adversary'], makeDefault: true });
Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true });
CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect;
DocumentSheetConfig.unregisterSheet(
CONFIG.ActiveEffect.documentClass,
'core',
foundry.applications.sheets.ActiveEffectConfig
);
DocumentSheetConfig.registerSheet(CONFIG.ActiveEffect.documentClass, SYSTEM.id, applications.DhActiveEffectConfig, {
makeDefault: true
});
CONFIG.Combat.dataModels = {
base: models.DhCombat
};
CONFIG.Combatant.dataModels = {
base: models.DhCombatant
};
CONFIG.ChatMessage.dataModels = models.messages.config;
CONFIG.ChatMessage.documentClass = applications.DhpChatMessage;
CONFIG.Canvas.rulerClass = DhpRuler;
CONFIG.Combat.documentClass = documents.DhpCombat;
CONFIG.ui.combat = DhCombatTracker;
CONFIG.ui.chat = DhpChatLog;
// CONFIG.ui.players = DhpPlayers;
CONFIG.Token.rulerClass = DhpTokenRuler;
CONFIG.ui.resources = Resources;
game.socket.on(`system.${SYSTEM.id}`, handleSocketEvent);
// Make Compendium Dialog resizable
foundry.applications.sidebar.apps.Compendium.DEFAULT_OPTIONS.window.resizable = true;
registerDHSettings();
RegisterHandlebarsHelpers.registerHelpers();
return preloadHandlebarsTemplates();
});
Hooks.on('ready', () => {
ui.resources = new CONFIG.ui.resources();
if(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear) !== 'hide') ui.resources.render({ force: true });
});
Hooks.once('dicesoniceready', () => {});
Hooks.on(socketEvent.GMUpdate, async (action, uuid, update) => {
if (game.user.isGM) {
const document = uuid ? await fromUuid(uuid) : null;
switch (action) {
case GMUpdateEvent.UpdateDocument:
if (document && update) {
await document.update(update);
}
break;
case GMUpdateEvent.UpdateFear:
if (game.user.isGM) {
await game.settings.set(
SYSTEM.id,
SYSTEM.SETTINGS.gameSettings.Resources.Fear,
Math.max(Math.min(update, 6), 0)
);
Hooks.callAll(socketEvent.DhpFearUpdate);
await game.socket.emit(`system.${SYSTEM.id}`, { action: socketEvent.DhpFearUpdate });
}
break;
}
}
});
const renderDualityButton = async event => {
const button = event.currentTarget,
traitValue = button.dataset.trait?.toLowerCase(),
target = getCommandTarget();
if (!target) return;
const config = {
event: event,
title: button.dataset.title,
roll: {
modifier: traitValue ? target.system.traits[traitValue].value : null,
label: button.dataset.label,
type: button.dataset.actionType ?? null // Need check
},
chatMessage: {
template: 'systems/daggerheart/templates/chat/duality-roll.hbs'
}
};
await target.diceRoll(config);
};
Hooks.on('renderChatMessageHTML', (_, element) => {
element
.querySelectorAll('.duality-roll-button')
.forEach(element => element.addEventListener('click', renderDualityButton));
});
Hooks.on('renderJournalEntryPageProseMirrorSheet', (_, element) => {
element
.querySelectorAll('.duality-roll-button')
.forEach(element => element.addEventListener('click', renderDualityButton));
});
Hooks.on('renderHandlebarsApplication', (_, element) => {
element
.querySelectorAll('.duality-roll-button')
.forEach(element => element.addEventListener('click', renderDualityButton));
});
Hooks.on('chatMessage', (_, message) => {
if (message.startsWith('/dr')) {
const rollCommand = rollCommandToJSON(message.replace(/\/dr\s?/, ''));
if (!rollCommand) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.DualityParsing'));
return false;
}
const traitValue = rollCommand.trait?.toLowerCase();
const advantageState = rollCommand.advantage ? true : rollCommand.disadvantage ? false : null;
// Target not required if an attribute is not used.
const target = traitValue ? getCommandTarget() : undefined;
if (target || !traitValue) {
new Promise(async (resolve, reject) => {
const trait = target ? target.system.traits[traitValue] : undefined;
if (traitValue && !trait) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.AttributeFaulty'));
reject();
return;
}
const title = traitValue
? game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
ability: game.i18n.localize(abilities[traitValue].label)
})
: game.i18n.localize('DAGGERHEART.General.Duality');
const hopeAndFearRoll = `1${rollCommand.hope ?? 'd12'}+1${rollCommand.fear ?? 'd12'}`;
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,
origin: target?.id,
roll: roll,
modifiers: trait ? [trait] : [],
hope: { dice: rollCommand.hope ?? 'd12', value: roll.dice[0].total },
fear: { dice: rollCommand.fear ?? 'd12', value: roll.dice[1].total },
advantage: advantageState !== null ? { dice: 'd6', value: roll.dice[2].total } : undefined,
advantageState
});
const msgData = {
type: 'dualityRoll',
sound: CONFIG.sounds.dice,
system: systemData,
user: game.user.id,
content: 'systems/daggerheart/templates/chat/duality-roll.hbs',
rolls: [roll]
};
cls.create(msgData);
});
}
return false;
}
});
const preloadHandlebarsTemplates = async function () {
return foundry.applications.handlebars.loadTemplates([
'systems/daggerheart/templates/sheets/parts/attributes.hbs',
'systems/daggerheart/templates/sheets/parts/defense.hbs',
'systems/daggerheart/templates/sheets/parts/armor.hbs',
'systems/daggerheart/templates/sheets/parts/experience.hbs',
'systems/daggerheart/templates/sheets/parts/features.hbs',
'systems/daggerheart/templates/sheets/parts/gold.hbs',
'systems/daggerheart/templates/sheets/parts/health.hbs',
'systems/daggerheart/templates/sheets/parts/hope.hbs',
'systems/daggerheart/templates/sheets/parts/weapons.hbs',
'systems/daggerheart/templates/sheets/parts/domainCard.hbs',
'systems/daggerheart/templates/sheets/parts/heritage.hbs',
'systems/daggerheart/templates/sheets/parts/subclassFeature.hbs',
'systems/daggerheart/templates/sheets/parts/effects.hbs',
'systems/daggerheart/templates/sheets/character/sections/inventory.hbs',
'systems/daggerheart/templates/sheets/character/sections/loadout.hbs',
'systems/daggerheart/templates/sheets/character/parts/heritageCard.hbs',
'systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs',
'systems/daggerheart/templates/components/card-preview.hbs',
'systems/daggerheart/templates/views/levelup/parts/selectable-card-preview.hbs',
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
'systems/daggerheart/templates/ui/combat/combatTrackerSection.hbs',
'systems/daggerheart/templates/views/actionTypes/damage.hbs',
'systems/daggerheart/templates/views/actionTypes/healing.hbs',
'systems/daggerheart/templates/views/actionTypes/resource.hbs',
'systems/daggerheart/templates/views/actionTypes/uuid.hbs',
'systems/daggerheart/templates/views/actionTypes/uses.hbs',
'systems/daggerheart/templates/views/actionTypes/roll.hbs',
'systems/daggerheart/templates/views/actionTypes/cost.hbs',
'systems/daggerheart/templates/views/actionTypes/range-target.hbs',
'systems/daggerheart/templates/views/actionTypes/effect.hbs'
]);
};