Fixed normal levelup

This commit is contained in:
WBHarry 2025-06-30 13:43:12 +02:00
parent 0aa08deaa3
commit b18f072c64
14 changed files with 159 additions and 104 deletions

View file

@ -1,12 +1,13 @@
import LevelUpBase from './levelup.mjs'; import LevelUpBase from './levelup.mjs';
import { DhLevelup } from '../../data/levelup.mjs'; import { DhLevelup } from '../../data/levelup.mjs';
import { domains } from '../../config/domainConfig.mjs'; import { domains } from '../../config/domainConfig.mjs';
import { abilities } from '../../config/actorConfig.mjs';
export default class DhCharacterLevelUp extends LevelUpBase { export default class DhCharacterLevelUp extends LevelUpBase {
constructor(actor) { constructor(actor) {
super(actor); super(actor);
this.levelTiers = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers); this.levelTiers = this.addBonusChoices(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers));
const playerLevelupData = actor.system.levelData; const playerLevelupData = actor.system.levelData;
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData)); this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData));
} }
@ -50,7 +51,7 @@ export default class DhCharacterLevelUp extends LevelUpBase {
.flatMap(exp => .flatMap(exp =>
exp.data.map(data => { exp.data.map(data => {
const experience = Object.keys(this.actor.system.experiences).find(x => x === data); const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
return this.actor.system.experiences[experience].description; return this.actor.system.experiences[experience].name;
}) })
); );
context.experienceIncreases = { context.experienceIncreases = {

View file

@ -7,7 +7,7 @@ export default class DhCompanionLevelUp extends BaseLevelUp {
constructor(actor) { constructor(actor) {
super(actor); super(actor);
this.levelTiers = defaultCompanionTier; this.levelTiers = this.addBonusChoices(defaultCompanionTier);
const playerLevelupData = actor.system.levelData; const playerLevelupData = actor.system.levelData;
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData)); this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData));
} }
@ -40,7 +40,7 @@ export default class DhCompanionLevelUp extends BaseLevelUp {
.flatMap(exp => .flatMap(exp =>
exp.data.map(data => { exp.data.map(data => {
const experience = Object.keys(this.actor.system.experiences).find(x => x === data); const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
return this.actor.system.experiences[experience].description; return this.actor.system.experiences[experience].name;
}) })
); );
context.experienceIncreases = { context.experienceIncreases = {
@ -69,23 +69,7 @@ export default class DhCompanionLevelUp extends BaseLevelUp {
break; break;
case 'summary': case 'summary':
const { current: currentActorLevel, changed: changedActorLevel } = this.actor.system.levelData.level;
const levelKeys = Object.keys(this.levelup.levels); const levelKeys = Object.keys(this.levelup.levels);
let achievementExperiences = [];
for (var levelKey of levelKeys) {
const level = this.levelup.levels[levelKey];
if (Number(levelKey) < this.levelup.startLevel) continue;
achievementExperiences = level.achievements.experiences
? Object.values(level.achievements.experiences).reduce((acc, experience) => {
if (experience.name) acc.push(experience);
return acc;
}, [])
: [];
}
context.achievements = {};
const actorDamageDice = this.actor.system.attack.damage.parts[0].value.dice; const actorDamageDice = this.actor.system.attack.damage.parts[0].value.dice;
const actorRange = this.actor.system.attack.range; const actorRange = this.actor.system.attack.range;
const advancement = {}; const advancement = {};
@ -97,13 +81,13 @@ export default class DhCompanionLevelUp extends BaseLevelUp {
const choice = level.choices[choiceKey]; const choice = level.choices[choiceKey];
for (var checkbox of Object.values(choice)) { for (var checkbox of Object.values(choice)) {
switch (choiceKey) { switch (choiceKey) {
case 'resilient': case 'stress':
case 'aware': case 'evasion':
advancement[choiceKey] = advancement[choiceKey] advancement[choiceKey] = advancement[choiceKey]
? advancement[choiceKey] + Number(checkbox.value) ? advancement[choiceKey] + Number(checkbox.value)
: Number(checkbox.value); : Number(checkbox.value);
break; break;
case 'intelligent': case 'experience':
if (!advancement[choiceKey]) advancement[choiceKey] = []; if (!advancement[choiceKey]) advancement[choiceKey] = [];
const data = checkbox.data.map(data => { const data = checkbox.data.map(data => {
const experience = Object.keys(this.actor.system.experiences).find( const experience = Object.keys(this.actor.system.experiences).find(

View file

@ -76,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;
@ -336,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;
}, {}), }, {}),

View file

@ -1,3 +1,4 @@
import { GMUpdateEvent, socketEvent } from '../../helpers/socket.mjs';
import DhCompanionlevelUp from '../levelup/companionLevelup.mjs'; import DhCompanionlevelUp from '../levelup/companionLevelup.mjs';
import DaggerheartSheet from './daggerheart-sheet.mjs'; import DaggerheartSheet from './daggerheart-sheet.mjs';
@ -31,9 +32,15 @@ export default class DhCompanionSheet extends DaggerheartSheet(ActorSheetV2) {
async _prepareContext(_options) { async _prepareContext(_options) {
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.document = this.document; context.document = this.document;
context.playerCharacters = game.users context.playerCharacters = game.actors
.filter(x => !x.isGM && x.character) .filter(
.map(x => ({ key: x.character.uuid, name: x.character.name })); 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; return context;
} }
@ -48,7 +55,22 @@ export default class DhCompanionSheet extends DaggerheartSheet(ActorSheetV2) {
const partner = game.actors.find(a => a.uuid === event.target.value); const partner = game.actors.find(a => a.uuid === event.target.value);
await partner.update({ 'system.companion': this.document.uuid }); await partner.update({ 'system.companion': this.document.uuid });
} else { } else {
await this.document.system.partner.update({ 'system.companion': null }); const update = { 'system.companion': null };
if (
this.document.system.partner.ownership.default !== 3 &&
this.document.system.partner.ownership[game.user.id] !== 3
) {
await game.socket.emit(`system.${SYSTEM.id}`, {
action: socketEvent.GMUpdate,
data: {
action: GMUpdateEvent.UpdateDocument,
uuid: this.document.system.partner.uuid,
update: update
}
});
} else {
await this.document.system.partner.update(update);
}
} }
await this.document.update({ 'system.partner': event.target.value }); await this.document.update({ 'system.partner': event.target.value });

View file

@ -115,7 +115,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 }), 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 }),
@ -315,4 +315,10 @@ export default class DhCharacter extends BaseDataActor {
level: this.levelData.level.current level: this.levelData.level.current
}; };
} }
async _preDelete() {
if (this.companion) {
this.companion.updateLevel(1);
}
}
} }

View file

@ -85,23 +85,23 @@ export default class DhCompanion extends BaseDataActor {
const level = this.levelData.levelups[levelKey]; const level = this.levelData.levelups[levelKey];
for (let selection of level.selections) { for (let selection of level.selections) {
switch (selection.type) { switch (selection.type) {
case 'lightInTheDark': case 'hope':
this.resources.hope += selection.value; this.resources.hope += selection.value;
break; break;
case 'vicious': case 'vicious':
if (selection.data === 'damage') { if (selection.data[0] === 'damage') {
this.attack.damage.parts[0].value.dice = adjustDice(this.attack.damage.parts[0].value.dice); this.attack.damage.parts[0].value.dice = adjustDice(this.attack.damage.parts[0].value.dice);
} else { } else {
this.attack.range = adjustRange(this.attack.range); this.attack.range = adjustRange(this.attack.range);
} }
break; break;
case 'resilient': case 'stress':
this.resources.stress.bonus += selection.value; this.resources.stress.bonus += selection.value;
break; break;
case 'aware': case 'evasion':
this.evasion.bonus += selection.value; this.evasion.bonus += selection.value;
break; break;
case 'intelligent': case 'experience':
Object.keys(this.experiences).forEach(key => { Object.keys(this.experiences).forEach(key => {
const experience = this.experiences[key]; const experience = this.experiences[key];
experience.bonus += selection.value; experience.bonus += selection.value;
@ -118,6 +118,10 @@ export default class DhCompanion extends BaseDataActor {
experience.total = experience.value + experience.bonus; 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.resources.stress.maxTotal = this.resources.stress.max + this.resources.stress.bonus;
this.evasion.total = this.evasion.value + this.evasion.bonus; this.evasion.total = this.evasion.value + this.evasion.bonus;
} }
@ -129,7 +133,9 @@ export default class DhCompanion extends BaseDataActor {
}; };
} }
_preDelete() { async _preDelete() {
/* Null Character Companion field */ if (this.partner) {
await this.partner.update({ 'system.companion': null });
}
} }
} }

View file

@ -7,7 +7,8 @@ export default class DhLevelData extends foundry.abstract.DataModel {
return { return {
level: new fields.SchemaField({ level: new fields.SchemaField({
current: new fields.NumberField({ required: true, integer: true, initial: 1 }), current: new fields.NumberField({ required: true, integer: true, initial: 1 }),
changed: 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( levelups: new fields.TypedObjectField(
new fields.SchemaField({ new fields.SchemaField({

View file

@ -59,13 +59,16 @@ class DhLevelOption extends foundry.abstract.DataModel {
} }
export const CompanionLevelOptionType = { export const CompanionLevelOptionType = {
lightInTheDark: { hope: {
id: 'lightInTheDark', id: 'hope',
label: 'Light In The Dark' label: 'Light In The Dark'
}, },
createComfort: { creatureComfort: {
id: 'createComfort', id: 'creatureComfort',
label: 'Create Comfort' label: 'Creature Comfort'
// actions: [
// ],
}, },
armored: { armored: {
id: 'armored', id: 'armored',
@ -390,20 +393,20 @@ export const defaultCompanionTier = {
minCost: 1, minCost: 1,
type: LevelOptionType.experience.id, type: LevelOptionType.experience.id,
value: 1, value: 1,
amount: 2 amount: 1
}, },
lightInTheDark: { hope: {
label: 'DAGGERHEART.LevelUp.Options.lightInTheDark', label: 'DAGGERHEART.LevelUp.Options.lightInTheDark',
checkboxSelections: 1, checkboxSelections: 1,
minCost: 1, minCost: 1,
type: CompanionLevelOptionType.lightInTheDark.id, type: CompanionLevelOptionType.hope.id,
value: 1 value: 1
}, },
creatureComfort: { creatureComfort: {
label: 'DAGGERHEART.LevelUp.Options.creatureComfort', label: 'DAGGERHEART.LevelUp.Options.creatureComfort',
checkboxSelections: 1, checkboxSelections: 1,
minCost: 1, minCost: 1,
type: CompanionLevelOptionType.createComfort.id, type: CompanionLevelOptionType.creatureComfort.id,
value: 1 value: 1
}, },
armored: { armored: {
@ -421,11 +424,11 @@ export const defaultCompanionTier = {
value: 1, value: 1,
amount: 1 amount: 1
}, },
resilient: { stress: {
label: 'DAGGERHEART.LevelUp.Options.resilient', label: 'DAGGERHEART.LevelUp.Options.resilient',
checkboxSelections: 3, checkboxSelections: 3,
minCost: 1, minCost: 1,
type: CompanionLevelOptionType.resilient.id, type: LevelOptionType.stress.id,
value: 1 value: 1
}, },
bonded: { bonded: {
@ -435,11 +438,11 @@ export const defaultCompanionTier = {
type: CompanionLevelOptionType.bonded.id, type: CompanionLevelOptionType.bonded.id,
value: 1 value: 1
}, },
aware: { evasion: {
label: 'DAGGERHEART.LevelUp.Options.aware', label: 'DAGGERHEART.LevelUp.Options.aware',
checkboxSelections: 3, checkboxSelections: 3,
minCost: 1, minCost: 1,
type: CompanionLevelOptionType.aware.id, type: LevelOptionType.evasion.id,
value: 2, value: 2,
amount: 1 amount: 1
} }

View file

@ -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
@ -99,7 +99,6 @@ export class DhLevelup extends foundry.abstract.DataModel {
case 'domainCard': case 'domainCard':
case 'subclass': case 'subclass':
case 'vicious': case 'vicious':
case 'intelligent':
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;

View file

@ -18,7 +18,11 @@ 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 (this.system.companion) {
this.system.companion.updateLevel(newLevel);
}
if (newLevel > this.system.levelData.level.current) { if (newLevel > this.system.levelData.level.current) {
const maxLevel = Object.values( const maxLevel = Object.values(
@ -67,12 +71,16 @@ export default class DhpActor extends Actor {
}); });
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) {
@ -126,10 +134,19 @@ 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 value: experience.modifier
} }
}); });
if (this.system.companion) {
await this.system.companion.update({
[`system.experiences.${experienceKey}`]: {
name: '',
value: experience.modifier
}
});
}
} }
let multiclass = null; let multiclass = null;

View file

@ -113,7 +113,7 @@
<div class="experience-value"> <div class="experience-value">
+{{experience.total}} +{{experience.total}}
</div> </div>
<input name="{{concat "system.experiences." id ".description"}}" data-experience={{id}} value="{{experience.description}}" type="text" /> <input name="{{concat "system.experiences." id ".name"}}" data-experience={{id}} value="{{experience.name}}" type="text" />
<div class="controls"> <div class="controls">
<a data-action="toChat" data-type="experience" data-uuid="{{id}}"><i class="fa-regular fa-message"></i></a> <a data-action="toChat" data-type="experience" data-uuid="{{id}}"><i class="fa-regular fa-message"></i></a>
</div> </div>

View file

@ -35,7 +35,7 @@
<div class="form-group"> <div class="form-group">
<div class="form-fields"> <div class="form-fields">
<label>{{localize "DAGGERHEART.Sheets.Companion.Level"}}</label> <label>{{localize "DAGGERHEART.Sheets.Companion.Level"}}</label>
<input type="text" name="system.levelData.level.changed" value="{{source.system.levelData.level.changed}}" /> <div>{{source.system.levelData.level.changed}}</div>
<button data-action="levelUp" {{#if (not source.system.levelData.canLevelUp)}}disabled{{/if}}>Level Up</button> <button data-action="levelUp" {{#if (not source.system.levelData.canLevelUp)}}disabled{{/if}}>Level Up</button>
</div> </div>
</div> </div>

View file

@ -117,7 +117,7 @@
<h3>{{localize "DAGGERHEART.Application.LevelUp.summary.vicious"}}</h3> <h3>{{localize "DAGGERHEART.Application.LevelUp.summary.vicious"}}</h3>
{{#each this.vicious}} {{#each this.vicious}}
<div class="levelup-radio-choices"> <div class="levelup-radio-choices">
{{radioBoxes (concat "levelup." this.path ".data") @root.viciousChoices checked=this.data}} {{radioBoxes (concat "levelup." this.path ".data") @root.viciousChoices checked=(lookup this.data 0)}}
</div> </div>
{{/each}} {{/each}}
</div> </div>

View file

@ -4,57 +4,58 @@
data-group='{{tabs.summary.group}}' data-group='{{tabs.summary.group}}'
> >
<div class="section-container levelup-summary-container"> <div class="section-container levelup-summary-container">
<fieldset> {{#if this.achievements}}
<legend>{{localize "DAGGERHEART.Application.LevelUp.summary.levelAchievements"}}</legend> <fieldset>
<legend>{{localize "DAGGERHEART.Application.LevelUp.summary.levelAchievements"}}</legend>
<div class="level-achievements-container"> <div class="level-achievements-container">
{{#if this.achievements.proficiency.shown}} {{#if this.achievements.proficiency.shown}}
<div> <div>
<div class="increase-container"> <div class="increase-container">
{{localize "DAGGERHEART.Application.LevelUp.summary.proficiencyIncrease" proficiency=this.achievements.proficiency.old }} {{localize "DAGGERHEART.Application.LevelUp.summary.proficiencyIncrease" proficiency=this.achievements.proficiency.old }}
<i class="fa-solid fa-arrow-right-long"></i> <i class="fa-solid fa-arrow-right-long"></i>
{{this.achievements.proficiency.new}} {{this.achievements.proficiency.new}}
</div>
</div> </div>
</div> {{/if}}
{{/if}} {{#if this.achievements.damageThresholds}}
{{#if this.achievements.damageThresholds}} <div>
<div> <h5 class="summary-section">{{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholds"}}{{#if this.levelAchievements.damageThresholds.unarmored}}({{localize "DAGGERHEART.General.unarmored"}}){{/if}}</h5>
<h5 class="summary-section">{{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholds"}}{{#if this.levelAchievements.damageThresholds.unarmored}}({{localize "DAGGERHEART.General.unarmored"}}){{/if}}</h5> <div class="increase-container">
<div class="increase-container"> {{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholdMajorIncrease" threshold=this.achievements.damageThresholds.major.old }}
{{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholdMajorIncrease" threshold=this.achievements.damageThresholds.major.old }} <i class="fa-solid fa-arrow-right-long"></i>
<i class="fa-solid fa-arrow-right-long"></i> {{this.achievements.damageThresholds.major.new}}
{{this.achievements.damageThresholds.major.new}} </div>
<div class="increase-container">
{{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholdSevereIncrease" threshold=this.achievements.damageThresholds.severe.old }}
<i class="fa-solid fa-arrow-right-long"></i>
{{this.achievements.damageThresholds.severe.new}}
</div>
</div> </div>
<div class="increase-container"> {{/if}}
{{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholdSevereIncrease" threshold=this.achievements.damageThresholds.severe.old }} {{#if this.achievements.domainCards.shown}}
<i class="fa-solid fa-arrow-right-long"></i> <div>
{{this.achievements.damageThresholds.severe.new}} <h5>{{localize "DAGGERHEART.Application.LevelUp.summary.domainCards"}}</h5>
<div class="summary-selection-container">
{{#each this.achievements.domainCards.values}}
<div class="summary-selection">{{this.name}}</div>
{{/each}}
</div>
</div> </div>
</div> {{/if}}
{{/if}} {{#if this.achievements.experiences.shown}}
{{#if this.achievements.domainCards.shown}} <div>
<div> <h5>{{localize "DAGGERHEART.Application.LevelUp.summary.newExperiences"}}</h5>
<h5>{{localize "DAGGERHEART.Application.LevelUp.summary.domainCards"}}</h5> <div class="summary-selection-container">
<div class="summary-selection-container"> {{#each this.achievements.experiences.values}}
{{#each this.achievements.domainCards.values}} <div class="summary-selection">{{this.name}} {{signedNumber this.modifier}}</div>
<div class="summary-selection">{{this.name}}</div> {{/each}}
{{/each}} </div>
</div> </div>
</div> {{/if}}
{{/if}} </div>
{{#if this.achievements.experiences.shown}} </fieldset>
<div> {{/if}}
<h5>{{localize "DAGGERHEART.Application.LevelUp.summary.newExperiences"}}</h5>
<div class="summary-selection-container">
{{#each this.achievements.experiences.values}}
<div class="summary-selection">{{this.name}} {{signedNumber this.modifier}}</div>
{{/each}}
</div>
</div>
{{/if}}
</div>
</fieldset>
<fieldset> <fieldset>
<legend>{{localize "DAGGERHEART.Application.LevelUp.summary.levelAdvancements"}}</legend> <legend>{{localize "DAGGERHEART.Application.LevelUp.summary.levelAdvancements"}}</legend>