Compare commits

..

5 Commits

Author SHA1 Message Date
macniel 454faf8fe6 Merge pull request 'Adds Rollable Attributes' (#37) from feature/rollable-character-attributes into main
Reviewed-on: #37
2025-09-28 12:23:16 +02:00
macniel e4380d7822 Merge branch 'main' into feature/rollable-character-attributes
Pull Request Check / testing (pull_request) Successful in 17s Details
2025-09-28 12:23:01 +02:00
macniel 446a10b7c4 Fix typos
Pull Request Check / testing (pull_request) Successful in 16s Details
2025-09-28 12:20:57 +02:00
macniel 109682b415 feature/rollable-character-talents (#38)
Pull Request Check / testing (pull_request) Successful in 17s Details
Hinterlegt bei den Talenten nun die Würfel mechanik. Fügt bei dieser auch hinzu, dass die Schwelle zum Patzer (Tollpatsch,...) reduziert werden könne, sodass Patzerschwelle, Meisterliche schwelle in anzahl und Wert angepasst werden kann.

Basistalente werden nun beim anlegen des Actors hinzugefügt, diese müssen entsprechend bereits aus dem Kompendium importiert sein.

**Das muss aber besser und automatischer gehen können.**

Co-authored-by: macniel <yuna.baehr@proton.me>
Reviewed-on: #38
2025-09-28 12:19:21 +02:00
macniel 531bb4ae39 Adds Rollable Attributes 2025-09-27 22:59:33 +02:00
7 changed files with 222 additions and 12 deletions

View File

@ -43,7 +43,8 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
so: new NumberField({ required: true, integer: true }),
gilde: new StringField(),
}),
talente: new ArrayField ( new ForeignDocumentField(Item) ),
talente: new ArrayField ( new SchemaField(
{taw: new NumberField(), talent: new ForeignDocumentField(Item) })),
zauber: new ArrayField ( new ForeignDocumentField(Item) ),
}
}
@ -52,4 +53,49 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
super._initialize(options);
}
_onCreate(data, options, userId) {
// prepare base talents
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 => {
const talent = game.items.getName(talentName);
console.log(talent);
if (talent) {
talente.push({
taw: 0,
talent
})
} else {
console.error(`${talentName} not found in items`)
}
})
// push base talents
game.actors.getName(data.name).update({system: {talente}})
const startEigenschaften = {
"mu": 10,
"kl": 10,
"in": 10,
"ch": 10,
"ff": 10,
"ge": 10,
"ko": 10,
"kk": 10,
}
game.actors.getName(data.name).update({system: {attribute: startEigenschaften}})
super._onCreate(data, options, userId);
}
}

View File

@ -7,4 +7,21 @@ export class Character extends Actor {
this.prepareEmbeddedDocuments();
}
getRollData() {
const data = super.getRollData();
if (this.type !== 'character') return;
// Copy the ability scores to the top level, so that rolls can use
// formulas like `@str.mod + 4`.
if (data.attribute) {
for (let [k, v] of Object.entries(data.attribute)) {
data[k] = foundry.utils.deepClone(v);
}
}
console.log(data);
return data;
}
}

View File

@ -1,6 +1,5 @@
import { BaseItem } from "./base-item.mjs";
export class Skill extends BaseItem {
export class Skill extends Item {
/**
* Augment the basic Item data model with additional dynamic data.
*/

View File

@ -1,6 +1,4 @@
import { BaseItem } from "./base-item.mjs";
export class Spell extends BaseItem {
export class Spell extends Item {
/**
* Augment the basic Item data model with additional dynamic data.
*/

View File

@ -34,14 +34,63 @@ export class CharacterSheet extends ActorSheet {
// Add the actor's data to context.data for easier access, as well as flags.
context.system = actorData.system;
context.flags = actorData.flags;
context.attributes = [
{
eigenschaft: "mu",
name: "Mut",
wert: actorData.system.attribute.mu ?? 0,
},
{
eigenschaft: "kl",
name: "Klugheit",
wert: actorData.system.attribute.kl ?? 0,
},
{
eigenschaft: "in",
name: "Intuition",
wert: actorData.system.attribute.in ?? 0,
},
{
eigenschaft: "ch",
name: "Charisma",
wert: actorData.system.attribute.ch ?? 0,
},
{
eigenschaft: "ff",
name: "Fingerfertigkeit",
wert: actorData.system.attribute.ff ?? 0,
},
{
eigenschaft: "ge",
name: "Geschicklichkeit",
wert: actorData.system.attribute.ge ?? 0,
},
{
eigenschaft: "ko",
name: "Konstitution",
wert: actorData.system.attribute.ko ?? 0,
},
{
eigenschaft: "kk",
name: "Körperkraft",
wert: actorData.system.attribute.kk ?? 0,
},
];
context.skills = [];
if ( context.system.talente?.length >= 0) {
context.system.talente.forEach(talent => {
const tempTalent = talent();
console.log(tempTalent.system.probe);
console.log(talent);
const taw = talent.taw;
const talentObjekt = game.items.get(talent.talent);
const eigenschaften = Object.values(talentObjekt.system.probe);
context.skills.push({
talentName: tempTalent.name,
probe: `ROLLDATA(${Object.values(tempTalent.system.probe).join("/")})`
talentName: talentObjekt.name,
taw: taw,
rollEigenschaft1: this.prepareEigenschaftRoll(actorData, eigenschaften[0]),
rollEigenschaft2: this.prepareEigenschaftRoll(actorData, eigenschaften[1]),
rollEigenschaft3: this.prepareEigenschaftRoll(actorData, eigenschaften[2]),
probe: `(${eigenschaften.join("/")})`
});
})
@ -51,9 +100,97 @@ export class CharacterSheet extends ActorSheet {
return context;
}
prepareEigenschaftRoll(actorData, name) {
return actorData.system.attribute[name.toLowerCase()]
}
async _onTalentRoll(event) {
event.preventDefault();
const dataset = event.currentTarget.dataset;
console.log(dataset)
if (dataset.rolleigenschaft1) {
let roll1 = new Roll("3d20", this.actor.getRollData());
let evaluated1 = (await roll1.evaluate())
const dsaDieRollEvaluated = this._evaluateRoll(evaluated1.terms[0].results, {
taw: dataset.taw,
werte: [dataset.rolleigenschaft1, dataset.rolleigenschaft2, dataset.rolleigenschaft3],
})
if (dsaDieRollEvaluated.tap >= 0) { // erfolg
evaluated1.toMessage({
speaker: ChatMessage.getSpeaker({actor: this.actor}),
flavor: ` ${dsaDieRollEvaluated.meisterlich?'Meisterlich geschafft':'Geschafft'} mit ${dsaDieRollEvaluated.tap} Punkten übrig`,
rollMode: game.settings.get('core', 'rollMode'),
})
} else { // misserfolg
evaluated1.toMessage({
speaker: ChatMessage.getSpeaker({actor: this.actor}),
flavor: ` ${dsaDieRollEvaluated.meisterlich?'Gepatzt':''} mit ${Math.abs(dsaDieRollEvaluated.tap)} Punkten daneben`,
rollMode: game.settings.get('core', 'rollMode'),
})
}
}
}
_evaluateRoll(rolledDice, { taw, lowerThreshold = 1, upperThreshold = 20, countToMeisterlich = 3, countToPatzer = 3, werte = [] } ) {
let tap = taw;
let meisterlichCounter = 0;
let patzerCounter = 0;
let failCounter = 0;
rolledDice.forEach( (rolledDie, index) => {
if (tap < 0 && rolledDie.result > werte[index]) {
tap -= rolledDie.result - werte[index];
if (tap <0) { // konnte nicht vollständig ausgeglichen werden
failCounter++;
}
} else if (rolledDie.result > werte[index]) { // taw ist bereits aufgebraucht und wert kann nicht ausgeglichen werden
tap -= rolledDie.result - werte[index];
failCounter++;
}
if (rolledDie.result <= lowerThreshold) meisterlichCounter++;
if (rolledDie.result > upperThreshold) patzerCounter++;
})
return {
tap,
meisterlich: meisterlichCounter === countToMeisterlich,
patzer: patzerCounter === countToPatzer,
}
}
_onAttributeRoll(event) {
event.preventDefault();
const dataset = event.currentTarget.dataset;
if (dataset.roll) {
let label = dataset.label ? `[Attribut] ${dataset.label}` : '';
let roll = new Roll(dataset.roll, this.actor.getRollData());
roll.toMessage({
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
flavor: label,
rollMode: game.settings.get('core', 'rollMode'),
});
return roll;
}
}
activateListeners(html) {
super.activateListeners(html);
html.on('click', '.attribut.rollable', (evt) => {
console.log(evt);
this._onAttributeRoll(evt);
});
html.on('click', '.talent.rollable', (evt) => {
console.log(evt);
this._onTalentRoll(evt);
});
// Everything below here is only needed if the sheet is editable
if (!this.isEditable) return;

View File

@ -2,7 +2,7 @@
"_id": "o1nYjhmMP0Zzlcw6",
"_key": "!items!o1nYjhmMP0Zzlcw6",
"type": "Skill",
"name": "Sich verstecken",
"name": "Sich Verstecken",
"system": {
"gruppe": "Körperlich",
"probe": [

View File

@ -6,6 +6,18 @@
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100"/>
<div class="header-fields">
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name"/></h1>
<div class="attribute">
{{#each attributes}}
<button class="attribut rollable" data-label="{{this.name}}" data-roll="1d20cs<=@{{this.eigenschaft}}">
<span class="attribut-wert">
{{this.wert}}
</span>
<span class="attribut-name">
{{this.name}}
</span>
</button>
{{/each}}
</div>
</div>
</header>
@ -34,9 +46,10 @@
<div class="tab skills" data-group="primary" data-tab="skills">
<ul>
{{#each skills}}
<li><div>
<li><div class="talent rollable" data-taw="{{this.taw}}" data-rollEigenschaft1="{{this.rollEigenschaft1}}" data-rollEigenschaft2="{{this.rollEigenschaft2}}" data-rollEigenschaft3="{{this.rollEigenschaft3}}">
<b>{{this.talentName}}</b>
{{this.probe}}
TAW: {{this.taw}}
</div></li>
{{/each}}
</ul>