Compare commits

...

4 Commits

Author SHA1 Message Date
macniel a5c702059c fix id 2025-10-04 22:36:48 +02:00
macniel 9895809ccb adds ini mod to initiative 2025-10-04 22:24:51 +02:00
macniel e80ed623ec adds derived values for hold weapons 2025-10-04 22:15:25 +02:00
macniel 801de801ba initial draft of combat 2025-10-04 16:58:26 +02:00
16 changed files with 294 additions and 78 deletions

View File

@ -11,11 +11,14 @@ import {GroupDataModel} from "./module/data/group.mjs";
import {GroupSheet} from "./module/sheets/groupSheet.mjs";
import {EquipmentDataModel} from "./module/data/equipment.mjs";
import {AusruestungSheet} from "./module/sheets/equipmentSheet.mjs";
import { CreatureDataModel } from "./module/data/creature.mjs";
import { CreatureSheet } from "./module/sheets/creatureSheet.mjs";
async function preloadHandlebarsTemplates() {
return loadTemplates([
// ui partials.
'systems/DSA_4-1/templates/ui/partial-rollable-button.hbs',
'systems/DSA_4-1/templates/ui/partial-rollable-weaponskill-button.hbs',
'systems/DSA_4-1/templates/ui/partial-attribute-button.hbs',
'systems/DSA_4-1/templates/ui/partial-talent-editable.hbs',
'systems/DSA_4-1/templates/ui/partial-die.hbs',
@ -38,7 +41,8 @@ Hooks.once("init", () => {
// Configure System Data Models.
CONFIG.Actor.dataModels = {
character: PlayerCharacterDataModel,
group: GroupDataModel
group: GroupDataModel,
creature: CreatureDataModel,
};
CONFIG.Item.dataModels = {
@ -49,7 +53,7 @@ Hooks.once("init", () => {
}
CONFIG.Combat.initiative = {
formula: `(@attribute.ini.wuerfel)d6 + @attribute.ini.aktuell`,
formula: `(@ini.wuerfel)d6 + @ini.aktuell`,
decimals: 0
}
@ -60,6 +64,11 @@ Hooks.once("init", () => {
makeDefault: true,
label: 'DSA41.CharacterLabels.Item'
})
Actors.registerSheet('dsa41.creature', CreatureSheet, {
types: ["creature"],
makeDefault: true,
label : 'DSA41.CreatureLabel.Item'
})
Actors.registerSheet('dsa41.group', GroupSheet, {
types: ["group"],
makeDefault: true,
@ -92,7 +101,7 @@ Hooks.once("init", () => {
})
Hooks.on('dropActorSheetData', (actor, sheet, data) => {
if (data.id) {
if (data.uuid) {
if (actor.type === "character") {
return CharacterSheet.onDroppedData(actor, sheet, data);
} else {

View File

@ -26,6 +26,30 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
titel: new StringField(),
stand: new StringField(),
}),
ini: new SchemaField({
aktuell: new NumberField({ required: true, integer: true, initial: 0 }),
mod: new NumberField({ required: true, integer: true, initial: 0 }),
wuerfel: new NumberField({ required: true, integer: true, initial: 1}),
}),
lep: new SchemaField({
aktuell: new NumberField({ required: true, integer: true, initial: 0 }),
mod: new NumberField({ required: true, integer: true }),
}),
mr: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
aup: new SchemaField({
aktuell: new NumberField({ required: true, integer: true, initial: 0 }),
mod: new NumberField({ required: true, integer: true }),
}),
asp: new SchemaField({
aktuell: new NumberField({ required: true, integer: true, initial: 0 }),
mod: new NumberField({ required: true, integer: true }),
}),
kap: new SchemaField({
aktuell: new NumberField({ required: true, integer: true, initial: 0 }),
mod: new NumberField({ required: true, integer: true }),
}),
attribute: new SchemaField({
mu: new SchemaField({
start: new NumberField({ required: true, integer: true }),
@ -67,21 +91,6 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
mr: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
lep: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
aup: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
asp: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
kap: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
at: new SchemaField({
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
@ -94,11 +103,6 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
ini: new SchemaField({
aktuell: new NumberField({ required: true, integer: true, initial: 0 }),
mod: new NumberField({ required: true, integer: true, initial: 0 }),
wuerfel: new NumberField({ required: true, integer: true, initial: 1}),
}),
so: new SchemaField({
start: new NumberField({ required: true, integer: true }),
aktuell: new NumberField({ required: true, integer: true }),
@ -157,9 +161,6 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
}
_initializeSource(data, options) {
if (data.heldenausruestung.length === 0) {
let sets = [];
@ -178,7 +179,6 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
}
console.log(data)
return super._initializeSource(data, options);
}

View File

@ -9,6 +9,8 @@ export class SkillDataModel extends BaseItem {
name: new StringField({ required: true }),
gruppe: new StringField({ required: true }),
taw: new NumberField({ integer: true, initial: 0 }),
at: new NumberField({ required: false, integer: true, initial: 0 }),
pa: new NumberField({ required: false, 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 }),

View File

@ -11,6 +11,48 @@ export class Character extends Actor {
}
input.click()
}
/**
* @override
* Augment the actor source data with additional dynamic data. Typically,
* you'll want to handle most of your calculated/derived data in this step.
* Data calculated in this step should generally not exist in template.json
* (such as ability modifiers rather than ability scores) and should be
* available both inside and outside of character sheets (such as if an actor
* is queried and has a roll executed directly from it).
*/
prepareDerivedData() {
if (this.type === "character") {
const actorData = this;
const systemData = actorData.system;
const mu = systemData.attribute.mu.aktuell;
const kl = systemData.attribute.kl.aktuell;
const _in = systemData.attribute.in.aktuell;
const ch = systemData.attribute.ch.aktuell;
const ff = systemData.attribute.ff.aktuell;
const ge = systemData.attribute.ge.aktuell;
const ko = systemData.attribute.kk.aktuell;
const kk = systemData.attribute.kk.aktuell;
systemData.lep.max = Math.round((ko + ko + kk) / 2) + systemData.lep.mod;
systemData.aup.max = Math.round((mu + ko + ge) / 2) + systemData.aup.mod;
systemData.asp.max = Math.round((mu + _in + ch) / 2) + systemData.asp.mod;
systemData.at = Math.round((mu + ge + kk) / 5);
systemData.pa = Math.round((_in + ge + kk) / 5);
systemData.fk = Math.round((_in + ff + kk) / 5);
systemData.ini.aktuell = Math.round((mu + mu + _in + ge) / 5) + systemData.ini.mod;
systemData.mr.aktuell = Math.round((mu + kl + ko) / 5) + systemData.mr.mod;
}
}
/**
* Augment the basic Item data model with additional dynamic data.
*/
@ -22,7 +64,7 @@ export class Character extends Actor {
getRollData() {
const data = super.getRollData();
if (this.type !== 'character') return;
if (this.type !== 'character' && this.type !== 'creature') return;
// Copy the ability scores to the top level, so that rolls can use
// formulas like `@str.mod + 4`.

View File

@ -24,9 +24,10 @@ export class CharacterSheet extends ActorSheet {
}
/** @override */
getData() {
async getData() {
const context = super.getData();
// Use a safe clone of the actor data for further operations.
const actorData = context.data;
@ -38,8 +39,7 @@ export class CharacterSheet extends ActorSheet {
this.#addAdvantagesToContext(context)
this.#addAttributesToContext(context)
this.#addEquipmentsToContext(context)
this.#addCombatStatistics(context)
await this.#addCombatStatistics(context)
return context;
}
@ -75,6 +75,8 @@ export class CharacterSheet extends ActorSheet {
eigenschaft2: werte[1].name,
eigenschaft3: werte[2].name,
probe: `(${eigenschaften.join("/")})`,
at: item.system.at,
pa: item.system.pa,
id: item._id,
};
context.skills[talentGruppe].push(obj);
@ -171,13 +173,82 @@ export class CharacterSheet extends ActorSheet {
return false
}
#addCombatStatistics(context) {
#findEquipmentOnSlot(slot, setNumber) {
return this.object.items.get(this.object.system.heldenausruestung[setNumber][slot])
}
#findTalentsOfEquipment(equipment) {
}
async #addCombatStatistics(context) {
const actorData = context.data;
context.inidice = actorData.system.attribute.ini.wuerfel;
context.inivalue = actorData.system.attribute.ini.aktuell;
context.inimod = actorData.system.attribute.ini.mod;
context.inidice = actorData.system.ini.wuerfel;
context.inivalue = actorData.system.ini.aktuell;
context.inimod = actorData.system.ini.mod;
context.aupper = Math.min((context.actor.system.aup.aktuell / context.actor.system.aup.max) * 100, 100);
context.lepper = Math.min((context.actor.system.lep.aktuell / context.actor.system.lep.max) * 100, 100);
context.lepcurrent = context.actor.system.lep.aktuell ?? 0
context.aupcurrent = context.actor.system.aup.aktuell ?? 0
const fernkampf = this.#findEquipmentOnSlot("fernkampf", 0)
const links = this.#findEquipmentOnSlot("links", 0)
const rechts = this.#findEquipmentOnSlot("rechts", 0)
context.attacks = [];
if (fernkampf) {
const fkitems = fernkampf.system.rangedSkills.map( async (skillInQuestion) => await this.object.items.getName(skillInQuestion) )
fkitems.forEach( async skill => {
const obj = await skill
console.log(this.object.system.fk, obj.system.at);
context.attacks.push({
name: obj.name,
using: fernkampf.name,
atroll: `1d20 + ${this.object.system.fk + obj.system.at}`,
at: `1w20 + ${this.object.system.fk + obj.system.at}`,
iniroll: `(${context.inidice})d6 + ${context.inivalue + fernkampf.system.iniModifier??0}`,
ini: `${context.inidice}w6 + ${context.inivalue + fernkampf.system.iniModifier??0}`,
})
})
}
if (links) {
const meitems = links.system.meleeSkills.map( async (skillInQuestion) => await this.object.items.getName(skillInQuestion) )
meitems.forEach( async skill => {
const obj = await skill
context.attacks.push({
name: obj.name,
using: links.name,
atroll: `1d20 + ${this.object.system.at + obj.system.at + links.system.attackModifier}`,
at: `1w20 + ${this.object.system.at + obj.system.at + links.system.attackModifier}`,
paroll: `1d20 + ${this.object.system.pa + obj.system.pa + links.system.parryModifier}`,
pa: `1w20 + ${this.object.system.pa + obj.system.pa + links.system.parryModifier}`,
iniroll: `(${context.inidice})d6 + ${context.inivalue + links.system.iniModifier??0}`,
ini: `${context.inidice}w6 + ${context.inivalue + links.system.iniModifier??0}`,
})
})
}
if (rechts) {
const meitems = rechts.system.meleeSkills.map( async (skillInQuestion) => await this.object.items.getName(skillInQuestion) )
meitems.forEach( async skill => {
const obj = await skill
console.log(this.object.system.at)
context.attacks.push({
name: obj.name,
using: rechts.name,
atroll: `1d20 + ${this.object.system.at + obj.system.at + rechts.system.attackModifier}`,
at: `1w20 + ${this.object.system.at + obj.system.at + rechts.system.attackModifier}`,
paroll: `1d20 + ${this.object.system.pa + obj.system.pa + rechts.system.parryModifier}`,
pa: `1w20 + ${this.object.system.pa + obj.system.pa + rechts.system.parryModifier}`,
iniroll: `(${context.inidice})d6 + ${context.inivalue + rechts.system.iniModifier??0}`,
ini: `${context.inidice}w6 + ${context.inivalue + rechts.system.iniModifier??0}`,
})
})
}
// add weapons to sidebar
}
#addEquipmentsToContext(context) {
@ -289,7 +360,11 @@ export class CharacterSheet extends ActorSheet {
}
prepareEigenschaftRoll(actorData, name) {
if (name) {
return actorData.system.attribute[name.toLowerCase()].aktuell
} else {
return 0
}
}
async _onTalentRoll(event) {

View File

@ -206,43 +206,24 @@ function mapRawJson(actor, rawJson) {
json.attribute.ge = getAttributeJson(attributes, "Gewandtheit")
json.attribute.ko = getAttributeJson(attributes, "Konstitution")
json.attribute.kk = getAttributeJson(attributes, "Körperkraft")
json.attribute.mr = {
json.mr = {
mod: filterAttribute(attributes,"Magieresistenz").mod
}
json.attribute.lep = {
json.lep = {
mod: filterAttribute(attributes,"Lebensenergie").mod
}
json.attribute.aup = {
json.aup = {
mod: filterAttribute(attributes,"Ausdauer").mod
}
json.attribute.asp = {
json.asp = {
mod: filterAttribute(attributes,"Astralenergie").mod
}
json.attribute.kap = {
json.kap = {
mod: filterAttribute(attributes,"Karmaenergie").mod
}
let attribute = filterAttribute(attributes,"Karmaenergie")
json.attribute.at = {
mod: attribute.mod,
aktuell: attribute.value
}
attribute = filterAttribute(attributes,"at")
json.attribute.pa = {
mod: attribute.mod,
aktuell: attribute.value
}
attribute = filterAttribute(attributes,"pa")
json.attribute.at = {
mod: attribute.mod,
aktuell: attribute.value
}
attribute = filterAttribute(attributes,"fk")
json.attribute.fk = {
mod: attribute.mod,
aktuell: attribute.value
}
attribute = filterAttribute(attributes,"ini")
json.attribute.ini = {
json.ini = {
mod: attribute.mod,
aktuell: attribute.value
}

View File

@ -0,0 +1,10 @@
{
"_id": "Msv0BWdwlNlF2ETU",
"_key": "!items!Msv0BWdwlNlF2ETU",
"type": "Skill",
"name": "Bogen",
"system": {
"gruppe": "Kampf",
"talent": ""
}
}

View File

@ -0,0 +1,10 @@
{
"_id": "Msv0BWdwlNlF2EtU",
"_key": "!items!Msv0BWdwlNlF2EtU",
"type": "Skill",
"name": "Dolche",
"system": {
"gruppe": "Kampf",
"talent": ""
}
}

View File

@ -0,0 +1,10 @@
{
"_id": "Msv0bWdwlNlF2EtU",
"_key": "!items!Msv0bWdwlNlF2EtU",
"type": "Skill",
"name": "Schwerter",
"system": {
"gruppe": "Kampf",
"talent": ""
}
}

View File

@ -0,0 +1,10 @@
{
"_id": "Msv0bWdwlNlF2etU",
"_key": "!items!Msv0bWdwlNlF2etU",
"type": "Skill",
"name": "Säbel",
"system": {
"gruppe": "Kampf",
"talent": ""
}
}

View File

@ -13,7 +13,7 @@ $rollable_colours: (
"Wissen": colour.$talent-knowledge-color,
"Sprachen": colour.$talent-language-color,
"Handwerk": colour.$talent-crafting-color,
"kampftalent": colour.$kampftalent-color,
"kampf": colour.$kampftalent-color,
"liturgie": colour.$liturgie-color,
"zauber": colour.$zauber-color,
);
@ -197,7 +197,7 @@ $rollable_colours: (
@include coloring("liturgie");
@include coloring("zauber");
@include coloring("talent");
@include coloring("kampftalent");
@include coloring("kampf");
@include coloring("Körperlich");
@include coloring("Natur");
@ -205,3 +205,4 @@ $rollable_colours: (
@include coloring("Wissen");
@include coloring("Sprachen");
@include coloring("Handwerk");

View File

@ -95,6 +95,7 @@
],
"documentTypes": {
"Actor": {
"creature": {},
"group": {
},
@ -216,8 +217,7 @@
"distance": 10,
"units": "Schritt"
},
"primaryTokenAttribute": "resources.sp",
"secondaryTokenAttribute": "resources.aus",
"primaryTokenAttribute": "lep.aktuell",
"url": "https://git.macniel.online/macniel/foundry-dsa41-game",
"manifest": "https://git.macniel.online/macniel/foundry-dsa41-game/releases/download/{{VERSION}}/system.json",
"download": "https://git.macniel.online/macniel/foundry-dsa41-game/releases/download/{{VERSION}}/release.zip"

View File

@ -18,20 +18,30 @@
<h2 class="sidebar-element header">Kampf Daten</h2>
<div class="sidebar-element resource-bar">
<label>LeP:</label><span class="resource-fill" style="width: 50%"></span></div>
<label>LeP: {{this.lep}}</label><span class="resource-fill" style="width: {{this.lepper}}%"></span></div>
<div class="sidebar-element resource-bar">
<label>AuP:</label><span class="resource-fill" style="width: 80%"></span></div>
<label>AuP: {{this.aup}}</label><span class="resource-fill" style="width: {{this.aupper}}%"></span></div>
<div class="sidebar-element rollable" data-roll="1d20+23" data-label="Attacke"><label>AT</label>
<div class="formula">1W20+23</div>
{{#each attacks}}
<div>
<h3>{{this.using}} ({{this.name}})</h3>
{{#if this.at}}
<div class="sidebar-element rollable" data-roll="{{this.atroll}}" data-label="Attacke"><label>AT</label>
<div class="formula">{{this.at}}</div>
</div>
<div class="sidebar-element"><label>PA</label>: -</div>
<div class="sidebar-element rollable" data-label="Ausweichen" data-roll="1d6+2"><label>Ausweichen</label>
<div class="formula">1W6+2</div>
{{/if}}
{{#if this.pa}}
<div class="sidebar-element rollable" data-roll="{{this.paroll}}" data-label="Parade"><label>PA</label>
<div class="formula">{{this.pa}}</div>
</div>
<div class="sidebar-element rollable" data-label="Initiative" data-roll="1d6+6"><label>Initiative</label>
<div class="formula">1W6+6</div>
{{/if}}
{{#if this.ini}}
<div class="sidebar-element rollable" data-label="Initiative" data-roll="{{this.iniroll}}"><label>Initiative</label>
<div class="formula">{{this.ini}}</div>
</div>
{{/if}}
</div>
{{/each}}
<h2 class="sidebar-element header">Favouriten</h2>
@ -82,9 +92,31 @@
<span class="inline">w6</span>
<input type="number" name="system.attribute.ini.aktuell" value="{{this.inivalue}}"/>
</div>
<div class="lebensenergie">
<label>Lebensenergie:</label>
<input type="number" name="system.lep.aktuell" value="{{actor.system.lep.aktuell}}"/>
<span class="inline">von</span>
<input type="number" name="system.lep.max" value="{{actor.system.lep.max}}"/>
</div>
<div class="ausdauer">
<label>Ausdauerpunkte:</label>
<input type="number" name="system.aup.aktuell" value="{{actor.system.aup.aktuell}}"/>
<span class="inline">von</span>
<input type="number" name="system.aup.max" value="{{actor.system.aup.max}}"/>
</div>
</div>
<div class="tab skills" data-group="primary" data-tab="skills">
<div>
<h2>Kampftalente</h2>
<ul>
{{#each skills.Kampf}}
<li>
{{> "systems/DSA_4-1/templates/ui/partial-rollable-weaponskill-button.hbs" this}}
</li>
{{/each}}
</ul>
</div>
<div>
<h2>Körperliche Talente</h2>
<ul>

View File

@ -44,11 +44,23 @@
<input type="text" name="system.behinderung" value="{{system.behinderung}}" />
</label>
</div>
<div>
<label>Sprachenkomplexizität
<input type="text" name="system.komplexität" value="{{system.komplexität}}" />
</label>
</div>
<div>
<label>Attacke
<input type="text" name="system.at" value="{{system.at}}" />
</label>
</div>
<div>
<label>Parade
<input type="text" name="system.pa" value="{{system.pa}}" />
</label>
</div>
</div>
<div class="tab description" data-group="primary" data-tab="description">

View File

@ -1,6 +1,6 @@
<div class="block rollable {{this.type}} {{this.gruppe}}" data-id="{{this.id}}" data-taw="{{this.taw}}" data-name="{{this.name}}" data-eigenschaft1="{{this.eigenschaft1}}" data-eigenschaft2="{{this.eigenschaft2}}" data-eigenschaft3="{{this.eigenschaft3}}" data-taw="{{this.taw}}" data-rollEigenschaft1="{{this.rollEigenschaft1}}" data-rollEigenschaft2="{{this.rollEigenschaft2}}" data-rollEigenschaft3="{{this.rollEigenschaft3}}">
<div class="die">
<div class="die">
{{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }}
<span class="value">{{#if this.taw}}
{{this.taw}}

View File

@ -0,0 +1,22 @@
<div class="block rollable {{this.type}} {{this.gruppe}}" data-id="{{this.id}}" data-taw="{{this.taw}}" data-name="{{this.name}}" data-eigenschaft1="{{this.eigenschaft1}}" data-eigenschaft2="{{this.eigenschaft2}}" data-eigenschaft3="{{this.eigenschaft3}}" data-taw="{{this.taw}}" data-rollEigenschaft1="{{this.rollEigenschaft1}}" data-rollEigenschaft2="{{this.rollEigenschaft2}}" data-rollEigenschaft3="{{this.rollEigenschaft3}}">
<div class="die">
{{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }}
<span class="value">{{#if this.taw}}
{{this.taw}}
{{else}}
{{#if this.liturgiekenntnis}}
<img src="{{this.liturgiekenntnis}}"/>
{{/if}}
{{/if}}
</span>
</div>
<div class="container">
<span class="name" data-id="{{this.id}}">{{this.name}}</span>
<div class="werte">
<div class="eigenschaft"><span class="name">AT</span><span class="value">{{this.at}}</span></div>
<div class="eigenschaft"><span class="name">PA</span><span class="value">{{this.pa}}</span></div>
</div>
</div>
</div>