Character Creation with Default Values is no longer possible (got to be but its not scope of MVP anyway).

Skills either from Compendia or Imported Entries can now be uniquely added to a Character.

This approach will help us later with adding other Elements like Advantages (these need a "uniqueness" Attribute), Spells (always unique), Miracles (also always unique), Equipment
pull/47/head
macniel 2025-10-02 16:52:56 +02:00
parent 32031eb548
commit 1afdd483e6
6 changed files with 159 additions and 245 deletions

View File

@ -72,7 +72,7 @@ Hooks.once("init", () => {
})
Hooks.on('dropActorSheetData', (actor, sheet, data) => {
CharacterSheet.onDroppedData(actor, sheet, data);
return CharacterSheet.onDroppedData(actor, sheet, data);
} )
Hooks.once("ready", async function() {
@ -112,4 +112,4 @@ function rollItemMacro(itemName) {
// Trigger the item roll
return item.roll();
}
}

View File

@ -1,41 +1,12 @@
import {Skill} from "../documents/skill.mjs";
import {SkillDataModel} from "./skill.mjs";
const {
SchemaField, NumberField, StringField, EmbeddedDocumentField, DocumentIdField, ArrayField, ForeignDocumentField
} = foundry.data.fields;
/**
* @extends {Map<string, Set<VornachteileDataModel>>}
*/
class SkillMap extends Map {
/** @inheritDoc */
get(key, { type }={}) {
const result = super.get(key);
if ( !result?.size || !type ) return result;
return result.filter(i => i.type === type);
}
/* -------------------------------------------- */
/** @inheritDoc */
set(key, value) {
if ( !this.has(key) ) super.set(key, new Set());
this.get(key).add(value);
return this;
}
}
export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
/**
* Mapping of item identifiers to the items.
* @type {SkillMap<string, Set<VornachteileDataModel>>}
*/
skills = this.skills
static defineSchema() {
return {
name: new StringField(),
@ -143,11 +114,7 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
auswahlen: new ArrayField(new StringField()),
})),
talente: new ArrayField(new SchemaField({
talent: new DocumentIdField(Item),
taw: new NumberField({integer: true, required: true}),
})
),
talente: new ArrayField(new DocumentIdField(Item)),
zauber: new ArrayField(new SchemaField({
talent: new DocumentIdField(),
zfw: new NumberField({integer: true, required: true}),
@ -171,106 +138,8 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
super._initialize(options);
}
/**
* Adds base skills according to the BRW to the given actor
* @param actor
* @returns {Promise<void>}
*/
async #createBaseSkills(actor) {
const compendiumOfSkills = await game.packs.get('DSA_4-1.talente-brw');
const talentsByName = [
"Athletik", "Klettern", "Körperbeherrschung", "Schleichen", "Schwimmen", "Selbstbeherrschung", "Sich Verstecken", "Singen", "Sinnenschärfe", "Tanzen", "Zechen",
"Menschenkenntnis", "Überreden",
"Fährtensuchen", "Orientierung", "Wildnisleben",
"Götter/Kulte", "Rechnen", "Sagen/Legenden",
"Heilkunde: Wunden", "Holzbearbeitung", "Kochen", "Lederverarbeitung", "Malen/Zeichnen", "Schneidern"
]
const talente = []
const mappedCompendiumItems = talentsByName.map( talentName => {
const talent = compendiumOfSkills.index.find( skill => skill.name === talentName)
return talent._id
})
for (const talentId of mappedCompendiumItems) {
const talent = await compendiumOfSkills.getDocument(talentId);
try {
const embeddedDocument = (await thisCharacter.createEmbeddedDocuments('Item', [talent]))[0]
if (embeddedDocument.type === "Skill") {
if (talent) {
talente.push({
taw: 0,
talent: embeddedDocument.id,
})
}
}
} catch (error) {
console.error(`${talentId} not found in items`)
}
}
await actor.update({system: { talente: talente}})
}
/**
* Sets the attributes of the given actor to their default values
* @param actor
* @returns {Promise<void>}
*/
async #setBaseAttributes(actor) {
const startEigenschaften = {
"mu": {
start: 10,
aktuell: 10,
mod: 0
},
"kl": {
start: 10,
aktuell: 10,
mod: 0
},
"in": {
start: 10,
aktuell: 10,
mod: 0
},
"ch": {
start: 10,
aktuell: 10,
mod: 0
},
"ff": {
start: 10,
aktuell: 10,
mod: 0
},
"ge": {
start: 10,
aktuell: 10,
mod: 0
},
"ko": {
start: 10,
aktuell: 10,
mod: 0
},
"kk": {
start: 10,
aktuell: 10,
mod: 0
}
}
await actor.update({system: {attribute: startEigenschaften}})
}
async _onCreate(data, options, userId) {
const thisCharacter = await game.actors.getName(data.name);
await this.#createBaseSkills(thisCharacter)
await this.#setBaseAttributes(thisCharacter)
super._onCreate(data, options, userId);
}

View File

@ -8,6 +8,7 @@ export class SkillDataModel extends BaseItem {
return {
name: new StringField({ required: true }),
gruppe: new StringField({ required: true }),
taw: new NumberField({ integer: true, initial: 0 }),
probe: new ArrayField(new StringField(), { exact: 3 }), // References one of the eight attributes by name
voraussetzung: new SchemaField({
talent: new StringField({ model: SkillDataModel }),
@ -15,7 +16,7 @@ export class SkillDataModel extends BaseItem {
}), // Required skills at a given level
talent: new HTMLField({ required: true }),
behinderung: new NumberField({ required: false}), // BE-X
komplexität: new NumberField({ required: false }), // In case of languages
komplexität: new NumberField({ required: false }), // In case of languages
}
}
/**
@ -86,5 +87,4 @@ export class SkillDataModel extends BaseItem {
patzer: patzerCounter === countToPatzer,
}
}
}
}

View File

@ -41,4 +41,103 @@ export class Character extends Actor {
}
}
async addSkillFromCompendiumByNameToActor(talentName, actor) {
const compendiumOfSkills = game.packs.get('DSA_4-1.talente-brw');
const talentId = compendiumOfSkills.index.find( skill => skill.name === talentName)
let talentObject = {}
const talent = await compendiumOfSkills.getDocument(talentId);
try {
const embeddedDocument = (await actor.createEmbeddedDocuments('Item', [talent]))[0]
if (embeddedDocument.type === "Skill") {
if (talent) {
talentObject = {
taw: 0,
talent: embeddedDocument.id,
}
}
}
} catch (error) {
console.error(`${talentName} not found in items`, error)
}
await actor.update({system: { talente: talentObject, ...actor.system.talente}})
}
/**
* Adds base skills according to the BRW to the given actor
* @param actor
* @returns {Promise<void>}
*/
async #createBaseSkills(actor) {
const talentsByName = [
"Athletik", "Klettern", "Körperbeherrschung", "Schleichen", "Schwimmen", "Selbstbeherrschung", "Sich Verstecken", "Singen", "Sinnenschärfe", "Tanzen", "Zechen",
"Menschenkenntnis", "Überreden",
"Fährtensuchen", "Orientierung", "Wildnisleben",
"Götter/Kulte", "Rechnen", "Sagen/Legenden",
"Heilkunde: Wunden", "Holzbearbeitung", "Kochen", "Lederverarbeitung", "Malen/Zeichnen", "Schneidern"
]
const talente = []
talentsByName.forEach(talentName => {
this.addSkillFromCompendiumByNameToActor(
talentName,
)
})
await actor.update({system: { talente: talente}})
}
/**
* Sets the attributes of the given actor to their default values
* @param actor
* @returns {Promise<void>}
*/
async #setBaseAttributes(actor) {
const startEigenschaften = {
"mu": {
start: 10,
aktuell: 10,
mod: 0
},
"kl": {
start: 10,
aktuell: 10,
mod: 0
},
"in": {
start: 10,
aktuell: 10,
mod: 0
},
"ch": {
start: 10,
aktuell: 10,
mod: 0
},
"ff": {
start: 10,
aktuell: 10,
mod: 0
},
"ge": {
start: 10,
aktuell: 10,
mod: 0
},
"ko": {
start: 10,
aktuell: 10,
mod: 0
},
"kk": {
start: 10,
aktuell: 10,
mod: 0
}
}
await actor.update({system: {attribute: startEigenschaften}})
}
}

View File

@ -39,25 +39,15 @@ export class CharacterSheet extends ActorSheet {
}
#addSkillsToContext(context) {
const actorSkills = {}
const actorData = context.data;
context.skills = {};
context.flatSkills = [];
Object.values(actorData.items).forEach( (item) => {
Object.values(actorData.items).forEach( (item, index) => {
if (item.type === "Skill") {
actorSkills[item._id] = item;
}
}
);
if ( context.system.talente?.length >= 0) {
context.system.talente.forEach( ( { taw, talent }, index) => {
if (actorSkills[talent]) {
const talentObjekt = actorSkills[talent];
if (talentObjekt.type === 'Skill') {
const talentGruppe = talentObjekt.system.gruppe;
const eigenschaften = Object.values(talentObjekt.system.probe);
const talentGruppe = item.system.gruppe;
const eigenschaften = Object.values(item.system.probe);
const werte = [
{name: eigenschaften[0], value: this.prepareEigenschaftRoll(actorData, eigenschaften[0])},
{name: eigenschaften[1], value: this.prepareEigenschaftRoll(actorData, eigenschaften[1])},
@ -69,9 +59,9 @@ export class CharacterSheet extends ActorSheet {
const obj = {
type: "talent",
gruppe: talentGruppe,
name: talentObjekt.name,
taw: "" + taw,
tawPath: `system.talente.${index}.taw`,
name: item.name,
taw: "" + item.system.taw,
tawPath: `system.items.${index}.taw`,
werte,
rollEigenschaft1: werte[0].value,
rollEigenschaft2: werte[1].value,
@ -84,9 +74,8 @@ export class CharacterSheet extends ActorSheet {
context.skills[talentGruppe].push(obj);
context.flatSkills.push(obj);
}
}
})
}
}
);
}
#addAdvantagesToContext(context) {
@ -298,88 +287,40 @@ export class CharacterSheet extends ActorSheet {
}
async #handleDroppedSkill(actor, data) {
let alreadyInSet = false;
let previousTaw = 0;
const id = foundry.utils.parseUuid(data.uuid).id;
const item = (await actor.createEmbeddedDocuments('Item', [await game.items.get(id)]))[0]
actor.system.talente.forEach(({taw, talent}) => {
if (talent) {
if (talent._id === item._id) {
alreadyInSet = talent;
previousTaw = taw;
}
#handleDroppedSkill(actor, skill) {
const array = Array.from(actor.items);
for ( let i = 0; i < array.length; i++ ) {
if (array[i].name === skill.name) {
return false;
}
})
const myContent = `TaW: <input id="taw" type="number" value="${previousTaw}" />`;
new Dialog({
title: `Talent ${item.name} ${alreadyInSet?'ersetzen':'hinzufügen'}`,
content: myContent,
buttons: {
button1: {
label: "hinzufügen",
callback: (html) => myCallback(html),
icon: `<i class="fas fa-check"></i>`
}
}
}).render(true);
async function myCallback(html) {
const taw = html.find("input#taw").val();
let index = actor.system.talente.findIndex( predicate => predicate.talent && predicate.talent._id === alreadyInSet._id )
let sorted = [];
if (alreadyInSet) {
actor.system.talente[index].taw = taw;
sorted = actor.system.talente;
} else {
const newItem = {
taw: taw,
talent: {_id: item._id, name: item.name}
}
console.log(newItem, await game.items.get(item._id))
sorted = [newItem, ...actor.system.talente].sort((a, b) => {
return a.talent.name.localeCompare(b.talent.name)
}
);
}
const serialised = sorted.map(({taw, talent}) => {
return {
taw: taw,
talent: talent._id
}
});
await actor.update({
system: {
talente: [
...serialised
]
}
});
ui.notifications.info(`Talent ${item.name} auf TaW ${taw} hinzugefügt`);
}
}
static getElementByName(collection, id) {
const array = Array.from(collection);
for (const element of array) {
if (element._id === id) {
return element;
}
}
}
static onDroppedData(actor, characterSheet, data) {
const item = game.items.get(foundry.utils.parseUuid(data.uuid).id)
switch (item.type) {
const uuid = foundry.utils.parseUuid(data.uuid);
const collection = uuid.collection.index ?? uuid.collection;
const document = CharacterSheet.getElementByName(collection, uuid.id);
const {
name,
type
} = document
console.log(name, type)
switch (type) {
case "Skill":
characterSheet.#handleDroppedSkill(actor, data);
return characterSheet.#handleDroppedSkill(actor, document); // on false cancel this whole operation
default:
console.log(item, item.type);
return false;
}
// maybe dont
actor.items.clear()
}
}

View File

@ -101,6 +101,22 @@ function calculateBirthdate(json) {
return `${day}. ${month} ${year} BF`
}
function mapSkills(rawJson) {
let talents = []
for (let talent in held.talentliste.talent) {
talent = held.talentliste.talent[talent]
let talentItem = game.items.getName(talent.name)
if (talentItem) {
let talentJson = {
talent: talentItem,
taw: talent.value,
}
talents.push(talentJson)
}
}
return talents
}
/**
* parses a json into a fitting character-json
* @param rawJson the json parsed from the Helden-Software XML
@ -248,19 +264,8 @@ function mapRawJson(rawJson) {
}
json.sonderfertigkeiten = specialAbilities
json.liturgien = liturgies
let talents = []
for (let talent in held.talentliste.talent) {
talent = held.talentliste.talent[talent]
let talentItem = game.items.getName(talent.name)
if (talentItem) {
let talentJson = {
talent: talentItem,
taw: talent.value,
}
talents.push(talentJson)
}
}
json.talente = talents
json.talente = mapSkills(rawJson)
let spells = []
/*for (let spell in held.zauberliste.zauber) {
spell = held.zauberliste.zauber[spell]
@ -337,4 +342,4 @@ Hooks.on("getActorContextOptions", (application, menuItems) => {
actor.import()
}
})
})
})