[Feature] Manual Character Editing (#490)

* Initial

* Added Character-Settings

* Finalized Character-Settings

* Hide CharacterSetup if any part is done manually

* Fixed class/subclass drag-drop

* Fixed relinking of Features from items created on Character

* Adding features on CharacterItems now adds them on the Character and relinks

* Made suggested items inactive in the Class sheet if rendered from inside a Character

* Added hope to CharacterSetting

* add style to textarea element, add spellcasting and domain class into char sheet and move rest buttons to another place

* Fixed characterCreation experience description

---------

Co-authored-by: moliloo <dev.murilobrito@gmail.com>
This commit is contained in:
WBHarry 2025-08-01 17:16:35 +02:00 committed by GitHub
parent 263dfa69ae
commit e1d8f8784a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
49 changed files with 1205 additions and 386 deletions

View file

@ -716,10 +716,14 @@ export default class CharacterSheet extends DHBaseActorSheet {
});
}
/**
* Open the downtime application.
* @type {ApplicationClickAction}
*/
static useDowntime(_, button) {
new game.system.api.applications.dialogs.Downtime(this.document, button.dataset.type === 'shortRest').render(
true
);
new game.system.api.applications.dialogs.Downtime(this.document, button.dataset.type === 'shortRest').render({
force: true
});
}
async _onDragStart(event) {

View file

@ -417,17 +417,29 @@ export default function DHApplicationMixin(Base) {
const { documentClass, type, inVault, disabled } = target.dataset;
const parentIsItem = this.document.documentName === 'Item';
const parent =
parentIsItem && documentClass === 'Item'
? type === 'action'
? this.document.system
: null
: this.document;
this.document.parent?.type === 'character'
? this.document.parent
: parentIsItem && documentClass === 'Item'
? type === 'action'
? this.document.system
: null
: this.document;
let systemData = {};
if (parent?.type === 'character' && type === 'feature') {
systemData = {
originItemType: this.document.type,
originId: this.document.id,
identifier: this.document.system.isMulticlass ? 'multiclass' : null
};
}
const cls =
type === 'action' ? game.system.api.models.actions.actionsTypes.base : getDocumentClass(documentClass);
const data = {
name: cls.defaultName({ type, parent }),
type
type,
system: systemData
};
if (inVault) data['system.inVault'] = true;
if (disabled) data.disabled = true;

View file

@ -150,10 +150,24 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
static async #addFeature(_, target) {
const { type } = target.dataset;
const cls = foundry.documents.Item.implementation;
const item = await cls.create({
type: 'feature',
name: cls.defaultName({ type: 'feature' })
});
let systemData = {};
if (this.document.parent?.type === 'character') {
systemData = {
originItemType: this.document.type,
originId: this.document.id,
identifier: this.document.system.isMulticlass ? 'multiclass' : null
};
}
const item = await cls.create(
{
type: 'feature',
name: cls.defaultName({ type: 'feature' }),
system: systemData
},
{ parent: this.document.parent?.type === 'character' ? this.document.parent : undefined }
);
await this.document.update({
'system.features': [...this.document.system.features, { type, item }].map(x => ({
...x,

View file

@ -113,45 +113,47 @@ export default class ClassSheet extends DHBaseItemSheet {
});
} else if (item.type === 'feature') {
super._onDrop(event);
} else if (item.type === 'weapon') {
if (target.classList.contains('primary-weapon-section')) {
if (!item.system.secondary)
} else if (this.document.parent?.type !== 'character') {
if (item.type === 'weapon') {
if (target.classList.contains('primary-weapon-section')) {
if (!item.system.secondary)
await this.document.update({
'system.characterGuide.suggestedPrimaryWeapon': item.uuid
});
} else if (target.classList.contains('secondary-weapon-section')) {
if (item.system.secondary)
await this.document.update({
'system.characterGuide.suggestedSecondaryWeapon': item.uuid
});
}
} else if (item.type === 'armor') {
if (target.classList.contains('armor-section')) {
await this.document.update({
'system.characterGuide.suggestedPrimaryWeapon': item.uuid
});
} else if (target.classList.contains('secondary-weapon-section')) {
if (item.system.secondary)
await this.document.update({
'system.characterGuide.suggestedSecondaryWeapon': item.uuid
});
}
} else if (item.type === 'armor') {
if (target.classList.contains('armor-section')) {
await this.document.update({
'system.characterGuide.suggestedArmor': item.uuid
});
}
} else if (target.classList.contains('choice-a-section')) {
if (item.type === 'loot' || item.type === 'consumable') {
const filteredChoiceA = this.document.system.inventory.choiceA;
if (filteredChoiceA.length < 2)
await this.document.update({
'system.inventory.choiceA': [...filteredChoiceA.map(x => x.uuid), item.uuid]
});
}
} else if (item.type === 'loot') {
if (target.classList.contains('take-section')) {
const filteredTake = this.document.system.inventory.take.filter(x => x);
if (filteredTake.length < 3)
await this.document.update({
'system.inventory.take': [...filteredTake.map(x => x.uuid), item.uuid]
});
} else if (target.classList.contains('choice-b-section')) {
const filteredChoiceB = this.document.system.inventory.choiceB.filter(x => x);
if (filteredChoiceB.length < 2)
await this.document.update({
'system.inventory.choiceB': [...filteredChoiceB.map(x => x.uuid), item.uuid]
'system.characterGuide.suggestedArmor': item.uuid
});
}
} else if (target.classList.contains('choice-a-section')) {
if (item.type === 'loot' || item.type === 'consumable') {
const filteredChoiceA = this.document.system.inventory.choiceA;
if (filteredChoiceA.length < 2)
await this.document.update({
'system.inventory.choiceA': [...filteredChoiceA.map(x => x.uuid), item.uuid]
});
}
} else if (item.type === 'loot') {
if (target.classList.contains('take-section')) {
const filteredTake = this.document.system.inventory.take.filter(x => x);
if (filteredTake.length < 3)
await this.document.update({
'system.inventory.take': [...filteredTake.map(x => x.uuid), item.uuid]
});
} else if (target.classList.contains('choice-b-section')) {
const filteredChoiceB = this.document.system.inventory.choiceB.filter(x => x);
if (filteredChoiceB.length < 2)
await this.document.update({
'system.inventory.choiceB': [...filteredChoiceB.map(x => x.uuid), item.uuid]
});
}
}
}
}