Compare commits

..

No commits in common. "6133cc223706536efa67aae87ad8e45b13e10c3b" and "ced57dbbcbc5653efa3bd68344ede043870003bc" have entirely different histories.

240 changed files with 1003 additions and 3802 deletions

1
.gitignore vendored
View File

@ -1,3 +1,2 @@
dist
node_modules
src/packs/__source

View File

@ -5,8 +5,6 @@
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
<excludeFolder url="file://$MODULE_DIR$/src/packs/__source" />
<excludeFolder url="file://$MODULE_DIR$/src/packs/__source" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

View File

@ -1,70 +1,23 @@
import {dest, series, src} from 'gulp';
import process from 'node:process';
import replace from 'gulp-replace';
import {getRandomValues} from 'node:crypto';
import * as dartSass from 'sass';
import gulpSass from 'gulp-sass';
import {deleteAsync} from 'del';
import {readdirSync, readFileSync, writeFileSync, rmdirSync, existsSync, mkdirSync} from "fs";
import {readdirSync} from 'node:fs';
import {join} from 'node:path';
import {compilePack} from '@foundryvtt/foundryvtt-cli';
const sass = gulpSass(dartSass);
/**
* Generate a random alphanumeric string ID of a given requested length using `crypto.getRandomValues()`.
* @param {number} length The length of the random string to generate, which must be at most 16384.
* @returns {string} A string containing random letters (A-Z, a-z) and numbers (0-9).
*/
function randomID(length = 16) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const cutoff = 0x100000000 - (0x100000000 % chars.length);
const random = new Uint32Array(length);
do {
getRandomValues(random);
} while (random.some(x => x >= cutoff));
let id = "";
for (let i = 0; i < length; i++) id += chars[random[i] % chars.length];
return id;
}
const convert = function (from, to, ofType) {
const SOURCE = from;
const DEST = to;
const TYPE = ofType;
try {
rmdirSync(DEST, {force: true, recursive: true})
} catch (e) {
}
mkdirSync(DEST)
readdirSync(SOURCE).forEach(file => {
let originalSource = JSON.parse(readFileSync(join(SOURCE, file), {encoding: "utf8"}));
let id = randomID();
let targetSource = {
_id: id,
_key: "!items!" + id,
type: TYPE,
img: originalSource.image,
name: originalSource.name.trim(),
system: {...originalSource}
}
delete targetSource.system.image;
let target = JSON.stringify(targetSource, null, 2);
let newFileName = "./" + join(DEST, id + ".json");
writeFileSync(newFileName, target, {encoding: "utf8"});
});
}
function cleanDist() {
return deleteAsync(['dist/**']);
}
function buildStyles() {
return src('src/style/**/*.scss')
.pipe(sass().on('error', sass.logError))
@ -87,27 +40,6 @@ function updateManifestFile() {
.pipe(dest('dist/'))
}
async function prepareDB() {
try {
if (!existsSync("./src/packs/__source")) {
mkdirSync("./src/packs/__source");
}
convert("./src/packs/_source/talente", "./src/packs/__source/talente", "Skill");
convert("./src/packs/_source/zauber", "./src/packs/__source/zauber", "Spell");
convert("./src/packs/_source/vorteile", "./src/packs/__source/vorteile", "Advantage");
convert("./src/packs/_source/waffen", "./src/packs/__source/waffen", "Equipment");
convert("./src/packs/_source/munition", "./src/packs/__source/munition", "Equipment");
convert("./src/packs/_source/ruestzeug", "./src/packs/__source/ruestzeug", "Equipment");
convert("./src/packs/_source/liturgien-und-segnungen", "./src/packs/__source/liturgien", "Liturgy");
} catch (err) {
console.error(err);
}
}
function buildDB() {
// Determine which source folders to process
@ -123,7 +55,7 @@ function buildDB() {
const src = join(PACK_SRC, folder.name);
const dest = join(PACK_DEST, folder.name);
console.info(`Compiling pack ${folder.name}`);
await compilePack(src, dest, {recursive: true, nedb: false});
await compilePack(src, dest, {recursive: true, log: true, nedb: false});
}
resolve()
@ -136,7 +68,6 @@ export default series(
copySource,
copyAssets,
buildStyles,
prepareDB,
buildDB,
updateManifestFile
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

View File

@ -13,13 +13,6 @@ 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";
import {LiturgySheet} from "./module/sheets/liturgySheet.mjs";
import {LiturgyDataModel} from "./module/data/liturgy.mjs";
import {BlessingDataModel} from "./module/data/blessing.mjs";
import {SpecialAbilityDataModel} from "./module/data/specialAbility.mjs";
import {SpecialAbilitySheet} from "./module/sheets/specialAbilitySheet.mjs";
import {ActiveEffectSheet} from "./module/sheets/ActiveEffectSheet.mjs";
import {ActiveEffectDataModel} from "./module/data/activeeffect.mjs";
async function preloadHandlebarsTemplates() {
return loadTemplates([
@ -30,12 +23,10 @@ async function preloadHandlebarsTemplates() {
'systems/DSA_4-1/templates/ui/partial-talent-editable.hbs',
'systems/DSA_4-1/templates/ui/partial-die.hbs',
'systems/DSA_4-1/templates/ui/partial-advantage-button.hbs',
'systems/DSA_4-1/templates/ui/partial-sf-button.hbs',
'systems/DSA_4-1/templates/ui/partial-action-button.hbs',
'systems/DSA_4-1/templates/ui/partial-equipment-button.hbs',
'systems/DSA_4-1/templates/ui/partial-equipment-group-button.hbs',
'systems/DSA_4-1/templates/ui/partial-array-editor.hbs',
'systems/DSA_4-1/templates/dialog/modify-liturgy.hbs'
'systems/DSA_4-1/templates/ui/partial-array-editor.hbs'
]);
}
@ -60,10 +51,6 @@ Hooks.once("init", () => {
Spell: SpellDataModel,
Advantage: VornachteileDataModel,
Equipment: EquipmentDataModel,
Liturgy: LiturgyDataModel,
Blessing: BlessingDataModel,
SpecialAbility: SpecialAbilityDataModel,
ActiveEffect: ActiveEffectDataModel,
}
CONFIG.Combat.initiative = {
@ -107,25 +94,9 @@ Hooks.once("init", () => {
})
Items.registerSheet('dsa41.equipment', AusruestungSheet, {
types: ["Equipment"],
makeDefault: false,
makeDefault: true,
label: 'DSA41.AusruestungLabels.Item'
})
Items.registerSheet('dsa41.liturgy', LiturgySheet, {
types: ["SpecialAbility"],
makeDefault: true,
label: 'DSA41.SpecialAbilityLabels.Item'
})
Items.registerSheet('dsa41.specialAbility', SpecialAbilitySheet, {
types: ["Liturgy"],
makeDefault: true,
label: 'DSA41.LiturgyLabels.Item'
})
Items.registerSheet('dsa41.activeEffect', ActiveEffectSheet, {
types: ['ActiveEffect'],
makeDefault: true,
label: 'DSA41.ActiveEffectLabels.ActiveFfect'
})
return preloadHandlebarsTemplates();
})

View File

@ -1,49 +0,0 @@
import BaseItem from "./base-item.mjs";
const {ArrayField, NumberField, StringField, HTMLField} = foundry.data.fields;
export class ActiveEffectDataModel extends BaseItem {
static defineSchema() {
return {
name: new StringField({required: true}),
notes: new HTMLField(),
}
/*
name: String, // Name of Vornachteil will be used for rendering and referencing by other Items
description: HTMLString, // only used for rendering
variant: [String]?, // variant name of Vornachteil e.g. "Mut" in the case of "Herausragende Eigenschaft"
levels: [Number]?, // available levels e.g. 1, 2 in the case of "Flink"
mods: [
{
level: Number?, // in reference to level of the Vornachteil, is null when it does not have any levels or is the only modification
field: String, // Reference to Actor Data e.g. "FF" maps to "FF.mod"
value: Number, // value of the Modification e.g. "+2" maps to 2
requirement: {
field: String // Reference to Actor Data e.g. "BE" maps "be.aktuell"
operation: String // Supported: "<=", ">"
value: Number // Target Value the referenced field has to compare against
}? // optional when the mod does not have an active requirement
}
]
*/
}
_onCreate(data, options, userId) {
super._onCreate(data, options, userId);
if (this.parent.getEmbeddedCollection("ActiveEffect").contents.length === 0) {
this.parent.createEmbeddedDocuments("ActiveEffect", [{
name: data.name,
changes: [],
duration: {},
icon: this.img,
}]);
console.log("added default activeffect");
}
}
}

View File

@ -1,13 +0,0 @@
const {
SchemaField, NumberField, StringField, EmbeddedDocumentField, DocumentIdField, ArrayField, ForeignDocumentField
} = foundry.data.fields;
export class BlessingDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
return {
gottheit: new StringField(),
wert: new NumberField({min: 0, integer: true}),
}
}
}

View File

@ -1,12 +1,5 @@
const {
SchemaField,
NumberField,
StringField,
HTMLField,
EmbeddedDocumentField,
DocumentIdField,
ArrayField,
ForeignDocumentField
SchemaField, NumberField, StringField, EmbeddedDocumentField, DocumentIdField, ArrayField, ForeignDocumentField
} = foundry.data.fields;
export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
@ -20,17 +13,16 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
professions: new ArrayField(new StringField()),
geschlecht: new StringField(),
haarfarbe: new StringField(),
groesse: new StringField(),
groesse: new NumberField({required: true, integer: false}),
augenfarbe: new StringField(),
geburtstag: new StringField(),
alter: new StringField(),
gewicht: new StringField(),
aussehen: new HTMLField(),
familie: new HTMLField(),
alter: new NumberField({required: true, integer: true}),
gewicht: new NumberField({required: true, integer: true}),
aussehen: new ArrayField(new StringField()),
familie: new ArrayField(new StringField()),
titel: new StringField(),
stand: new StringField(),
}),
setEquipped: new NumberField({required: true, initial: 0, max: 3, integer: true}),
ini: new SchemaField({
aktuell: new NumberField({required: true, integer: true, initial: 0}),
mod: new NumberField({required: true, integer: true, initial: 0}),
@ -115,7 +107,23 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
}),
gilde: new StringField(),
}),
vornachteile: new ArrayField(new SchemaField({
vornachteil: new DocumentIdField(Item),
wert: new NumberField({required: false, integer: true}),
})),
sonderfertigkeiten: new ArrayField(new SchemaField({
name: new StringField(),
auswahlen: new ArrayField(new StringField()),
})),
talente: new ArrayField(new DocumentIdField(Item)),
zauber: new ArrayField(new SchemaField({
talent: new DocumentIdField(),
zfw: new NumberField({integer: true, required: true}),
})),
liturgien: new ArrayField(new SchemaField({
name: new StringField(),
})),
kampfwerte: new ArrayField(new SchemaField({
name: new StringField(),
at: new NumberField({required: true, integer: true}),
@ -152,22 +160,27 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
_initializeSource(data, options) {
if (data.heldenausruestung.length === 0) {
let sets = [];
for (let i = 0; i < 3; i++) {
const preppedSet = {}
PlayerCharacterDataModel.getSlots().forEach(slot => {
preppedSet[slot] = null;
})
sets.push(preppedSet);
}
data.heldenausruestung = sets
}
return super._initializeSource(data, options);
}
async _onCreate(data, options, userId) {
console.log(data, 'onCreate');
}
static getSlots() {

View File

@ -1,12 +1,5 @@
const {
SchemaField,
ObjectField,
NumberField,
StringField,
EmbeddedDocumentField,
DocumentIdField,
ArrayField,
ForeignDocumentField
SchemaField, NumberField, StringField, EmbeddedDocumentField, DocumentIdField, ArrayField, ForeignDocumentField
} = foundry.data.fields;
export class GroupDataModel extends foundry.abstract.TypeDataModel {
@ -20,8 +13,7 @@ export class GroupDataModel extends foundry.abstract.TypeDataModel {
}),
characters: new ArrayField(
new DocumentIdField(Actor)
),
settings: new ObjectField(),
)
}
}

View File

@ -1,49 +0,0 @@
import BaseItem from "./base-item.mjs";
const {BooleanField, NumberField, SchemaField, ArrayField, StringField, HTMLField} = foundry.data.fields;
export class LiturgyDataModel extends BaseItem {
static defineSchema() {
return {
herkunft: new ArrayField(new SchemaField({
name: new StringField(),
grad: new NumberField(),
})),
grad: new NumberField({min: 1, max: 5}),
reichweite: new StringField(),
ziel: new StringField(),
wirkungsdauer: new StringField(),
auswirkung: new SchemaField({
I: new StringField(),
II: new StringField(),
III: new StringField(),
IV: new StringField(),
V: new StringField(),
VI: new StringField(),
VII: new StringField(),
VIII: new StringField(),
})
}
}
prepareData() {
}
/**
* Prepare a data object which is passed to any Roll formulas which are created related to this Item
* @private
*/
getRollData() {
}
/**
* Handle clickable rolls.
* @param {Event} event The originating click event
* @private
*/
async roll() {
}
}

View File

@ -1,115 +0,0 @@
export class LiturgyData {
static ranks = ["I", "II", "III", "IV", "V", "VI", "VII", "VIII"]
static #ranks = [
{index: 0, name: "O", lkp: 3, mod: 2, costKaP: 2, costKaPPermant: 0, duration: "{*} KR", strength: "{*}/2"},
{index: 1, name: "I", lkp: 3, mod: 0, costKaP: 5, costKaPPermant: 0, duration: "{*} KR", strength: "{*}/2"},
{
index: 2,
name: "II",
lkp: 6,
mod: -2,
costKaP: 10,
costKaPPermant: 0,
duration: "{*}*10 KR",
strength: "{*}/2+5"
},
{index: 3, name: "III", lkp: 9, mod: -4, costKaP: 15, costKaPPermant: 0, duration: "{*} SR", strength: "{*}+5"},
{
index: 4,
name: "IV",
lkp: 12,
mod: -6,
costKaP: 20,
costKaPPermant: 0,
duration: "{*} Stunden",
strength: "{*}+10"
},
{
index: 5,
name: "V",
lkp: 15,
mod: -8,
costKaP: 25,
costKaPPermant: 1,
duration: "{*} Tage",
strength: "{*}+15"
},
{
index: 6,
name: "VI",
lkp: 18,
mod: -10,
costKaP: 30,
costKaPPermant: 3,
duration: "{*} Wochen",
strength: "{*}+20"
},
{
index: 7,
name: "VII",
lkp: 21,
mod: -12,
costKaP: 35,
costKaPPermant: 5,
duration: "{*} Monate",
strength: "{*}+25"
},
{
index: 8,
name: "VIII",
lkp: 24,
mod: -14,
costKaP: 40,
costKaPPermant: 7,
duration: "{*} Jahre oder permanent",
casttime: "",
strength: "{*}+30"
},
];
static alverans = [
"Praios",
"Rondra",
"Efferd",
"Travia",
"Boron",
"Hesinde",
"Firun",
"Tsa",
"Phex",
"Peraine",
"Ingerimm",
"Rahja"
]
static #aliases = [
{
"originalName": "Handwerkssegen",
"aliases": ["Cereborns Handreichung", "Hauch der Leidenschaft"]
},
{
"originalName": "Heiliger Befehl",
"aliases": ["Wort der Wahrheit"],
},
{
"originalName": "Eidsegen",
"aliases": ["Lehnseid"],
}
]
static getRankOfLiturgy(liturgy, deity) {
const lookupData = liturgy.herkunft.find(p => p.name === deity)
const rank = lookupData?.grad;
return LiturgyData.#ranks[rank];
}
static lookupAlias(alias) {
return LiturgyData.#aliases.find((entry) => {
console.log(alias, entry.aliases.indexOf(alias) !== -1)
return entry.aliases.indexOf(alias) !== -1
})?.originalName ?? alias; // cant determine thus simply return the original query name
}
}

View File

@ -1,12 +0,0 @@
import BaseItem from "./base-item.mjs";
const {BooleanField, NumberField, SchemaField, ArrayField, StringField, HTMLField} = foundry.data.fields;
export class SpecialAbilityDataModel extends BaseItem {
static defineSchema() {
return {
name: new StringField()
}
}
}

View File

@ -7,11 +7,9 @@ export class SpellDataModel extends BaseItem {
static defineSchema() {
return {
seite: new NumberField(),
zfw: new NumberField(),
name: new StringField({required: true}),
probe: new ArrayField(new StringField(), {required: true, exact: 3}),
probeMod: new StringField(),
hauszauber: new BooleanField(),
technik: new StringField(),
zauberdauer: new StringField(),
wirkung: new StringField(),

View File

@ -1,89 +0,0 @@
import {LiturgyData} from "../data/miracle/liturgydata.mjs";
export class ModifyLiturgy {
static data = {}
static naming = {
"range": "Reichweite",
"strength": "Wirkung",
"target": "Ziele",
"castduration": "Wirkzeit",
"duration": "Wirkdauer"
}
constructor(data) {
ModifyLiturgy.data = data;
ModifyLiturgy.data.maxmods = Math.round(data.lkp / 3);
ModifyLiturgy.data.variation = null;
console.log("ModifyLiturgy constructed", data)
}
static renderMods(html) {
let result = '';
ModifyLiturgy.data.mods.forEach(((mod, index) => {
result += `<tr><td>${LiturgyData.ranks[mod.rank]}</td><td>${ModifyLiturgy.naming[mod.mod]}</td><td><button class="remove-mod" data-index="${index}"><i class="fa-solid fa-xmark"></i></button></td></tr>`
}))
return result;
}
handleRender(html) {
html.off('click', 'input[name="data.variation"]')
html.on('click', 'input[name="data.variation"]', (evt) => {
if (evt.currentTarget.checked) {
ModifyLiturgy.data.variation = evt.currentTarget.dataset['rank'];
ModifyLiturgy.data.mods = [];
}
this.render(html)
})
html.off('click', 'button[class="remove-mod"]')
html.on('click', 'button[class="remove-mod"]', (evt) => {
const {index} = evt.currentTarget.dataset;
ModifyLiturgy.data.mods.splice(index, 1);
this.render(html)
})
html.off('change', 'select[name="mod"]')
html.on('change', 'select[name="mod"]', (evt) => {
const value = evt.currentTarget.value;
if (value === '') return;
const currentRank = ModifyLiturgy.data.mods.length + Number(ModifyLiturgy.data.rank);
ModifyLiturgy.data.mods.push({
rank: currentRank,
mod: value,
});
evt.currentTarget.value = "";
this.render(html)
})
// render state
$('#mods', html).html(ModifyLiturgy.renderMods(html))
// state handling
if (ModifyLiturgy.data.mods.length === ModifyLiturgy.data.maxmods) {
$(".editor, .editor *", html).attr('disabled', 'disabled');
$(".editor select", html).hide();
$('span#info', html).text('LkW lässt keine weitere Modifikationen zu')
$("#mod_rank", html).text(LiturgyData.ranks[ModifyLiturgy.data.mods.length + Number(ModifyLiturgy.data.rank)]);
} else if (ModifyLiturgy.data.variation == null) {
$(".editor select *", html).attr('disabled', 'disabled');
$(".editor select", html).hide();
$('span#info', html).text('Keine Variante ausgewählt')
$("#mod_rank", html).text('');
} else {
$(".editor, .editor *", html).removeAttr('disabled');
$(".editor select", html).show();
$('span#info', html).text('')
$("#mod_rank", html).text('');
}
}
}

View File

@ -1,9 +0,0 @@
export class Blessing extends Item {
/**
* Augment the basic Item data model with additional dynamic data.
*/
prepareData() {
super.prepareData();
}
}

View File

@ -1,5 +1,4 @@
import {importCharacter} from "../xml-import/xml-import.mjs";
import {LiturgyData} from "../data/miracle/liturgydata.mjs";
export class Character extends Actor {
@ -28,80 +27,27 @@ export class Character extends Actor {
const actorData = this;
const systemData = actorData.system;
const mu = systemData.attribute.mu.aktuell + systemData.attribute.mu.mod;
const kl = systemData.attribute.kl.aktuell + systemData.attribute.kl.mod;
const _in = systemData.attribute.in.aktuell + systemData.attribute.in.mod;
const ch = systemData.attribute.ch.aktuell + systemData.attribute.ch.mod;
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 + systemData.attribute.ff.mod;
const ge = systemData.attribute.ge.aktuell + systemData.attribute.ge.mod;
const ko = systemData.attribute.kk.aktuell + systemData.attribute.ko.mod;
const kk = systemData.attribute.kk.aktuell + systemData.attribute.kk.mod;
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;
systemData.rs = 0;
systemData.be = 0;
// map current set to RS and BE
const ausruestung = systemData.heldenausruestung[systemData.setEquipped];
if (ausruestung) {
if (ausruestung.brust) {
systemData.rs += systemData.parent.items.get(ausruestung.brust).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.brust).system.armorHandicap ?? 0
}
if (ausruestung.bauch) {
systemData.rs += systemData.parent.items.get(ausruestung.bauch).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.bauch).system.armorHandicap ?? 0
}
if (ausruestung.ruecken) {
systemData.rs += systemData.parent.items.get(ausruestung.ruecken).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.ruecken).system.armorHandicap ?? 0
}
if (ausruestung.armlinks) {
systemData.rs += systemData.parent.items.get(ausruestung.armlinks).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.armlinks).system.armorHandicap ?? 0
}
if (ausruestung.armrechts) {
systemData.rs += systemData.parent.items.get(ausruestung.armrechts).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.armrechts).system.armorHandicap ?? 0
}
if (ausruestung.beinlinks) {
systemData.rs += systemData.parent.items.get(ausruestung.beinlinks).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.beinlinks).system.armorHandicap ?? 0
}
if (ausruestung.beinrechts) {
systemData.rs += systemData.parent.items.get(ausruestung.beinrechts).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.beinrechts).system.armorHandicap ?? 0
}
}
systemData.kap.max = 0;
// evaluate deities for KaP
const deities = systemData.parent.items.filter(p => p.type === "Blessing")
deities?.forEach((deity) => {
if (LiturgyData.alverans.includes(deity.system.gottheit)) {
systemData.kap.max = 24;
} else if (systemData.kap.max === 0) {
systemData.kap.max += 12;
}
}, 0)
}

View File

@ -1,9 +0,0 @@
export class Liturgy extends Item {
/**
* Augment the basic Item data model with additional dynamic data.
*/
prepareData() {
super.prepareData();
}
}

View File

@ -1,9 +0,0 @@
export class SpecialAbility extends Item {
/**
* Augment the basic Item data model with additional dynamic data.
*/
prepareData() {
super.prepareData();
}
}

View File

@ -1,44 +0,0 @@
export class ActiveEffectSheet extends ItemSheet {
/**@override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ['dsa41', 'sheet', 'activeeffect'],
width: 520,
height: 480
});
}
/** @override */
get template() {
return `systems/DSA_4-1/templates/item/item-activeeffect-sheet.hbs`;
}
/** @override */
getData() {
// Retrieve the data structure from the base sheet. You can inspect or log
// the context variable to see the structure, but some key properties for
// sheets are the actor object, the data object, whether or not it's
// editable, the items array, and the effects array.
const context = super.getData();
const effects = context.document.getEmbeddedCollection("ActiveEffect").contents;
if (effects.length > 0) {
context.effectId = effects[0]._id;
}
return context;
}
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.isEditable) return;
html.on('click', '.editEffects', (evt) => {
const {id} = evt.currentTarget.dataset;
const effect = this.object.effects.get(id);
effect.sheet.render(true);
})
}
}

View File

@ -202,11 +202,11 @@ export class ActionManager {
]
#hatSonderfertigkeitBeginnendMit(name) {
return this.actor.system.sonderfertigkeiten?.find(p => p.name.startsWith(name)) != null
return this.actor.system.sonderfertigkeiten.find(p => p.name.startsWith(name)) != null
}
#hatSonderfertigkeit(name) {
return this.actor.system.sonderfertigkeiten?.find(p => p.name === name) != null
return this.actor.system.sonderfertigkeiten.find(p => p.name === name) != null
}
evaluate() {

View File

@ -2,8 +2,6 @@ import {rollAttributeForActor} from "../utils/rolls/rollEngine.mjs";
import {PlayerCharacterDataModel} from "../data/character.mjs";
import {ActionManager} from "./actions/action-manager.mjs";
import {LiturgyData} from "../data/miracle/liturgydata.mjs";
import {ModifyLiturgy} from "../dialog/modify-liturgy.mjs";
export class CharacterSheet extends ActorSheet {
/**@override */
@ -27,32 +25,25 @@ export class CharacterSheet extends ActorSheet {
return `systems/DSA_4-1/templates/actor/actor-character-sheet.hbs`;
}
static onDroppedData(actor, characterSheet, data) {
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":
return characterSheet.#handleDroppedSkill(actor, document); // on false cancel this whole operation
case "Advantage":
return characterSheet.#handleDroppedAdvantage(actor, document);
case "ActiveEffect":
return characterSheet.#handleDroppedActiveEffect(actor, document);
case "Equipment":
return characterSheet.#handleDroppedEquipment(actor, document);
case "Liturgy":
return characterSheet.#handleDroppedLiturgy(actor, document);
case "SpecialAbility":
return characterSheet.#handleDroppedSpecialAbility(actor, document);
default:
return false;
}
/** @override */
async getData() {
const context = super.getData();
// Use a safe clone of the actor data for further operations.
const actorData = context.data;
// Add the actor's data to context.data for easier access, as well as flags.
context.system = actorData.system;
context.flags = actorData.flags;
this.#addSkillsToContext(context)
this.#addAdvantagesToContext(context)
this.#addAttributesToContext(context)
this.#addEquipmentsToContext(context)
await this.#addCombatStatistics(context)
this.#addActionsToContext(context)
return context;
}
static getElementByName(collection, id) {
@ -107,140 +98,56 @@ export class CharacterSheet extends ActorSheet {
);
}
#cleanUpMerkmal(merkmale) {
return merkmale.split(",").map((merkmal) => merkmal.trim())
}
/** @override */
async getData() {
const context = super.getData();
// Use a safe clone of the actor data for further operations.
#addAttributesToContext(context) {
const actorData = context.data;
// Add the actor's data to context.data for easier access, as well as flags.
context.system = actorData.system;
context.flags = actorData.flags;
context.derived = context.document.system;
context.originalName = actorData.name;
context.name = context.derived.name ?? actorData.name;
context.effects = actorData.effects ?? [];
this.#addSkillsToContext(context)
this.#addAdvantagesToContext(context)
this.#addSpecialAbilitiesToContext(context)
await this.#addAttributesToContext(context)
this.#addEquipmentsToContext(context)
await this.#addCombatStatistics(context)
this.#addActionsToContext(context)
this.#addSpellsToContext(context)
this.#addLiturgiesToContext(context)
return context;
}
#addSpellsToContext(context) {
const actorData = context.data;
context.spells = [];
Object.values(actorData.items).forEach((item, index) => {
if (item.type === "Spell") {
const eigenschaften = item.system.probe;
const werte = [
{name: eigenschaften[0], value: this.prepareEigenschaftRoll(actorData, eigenschaften[0])},
{name: eigenschaften[1], value: this.prepareEigenschaftRoll(actorData, eigenschaften[1])},
{name: eigenschaften[2], value: this.prepareEigenschaftRoll(actorData, eigenschaften[2])}
]
context.spells.push({
id: item._id,
name: item.name,
zfw: item.system.zfw,
hauszauber: item.system.hauszauber,
merkmal: this.#cleanUpMerkmal(item.system.merkmal),
rollEigenschaft1: werte[0].value,
rollEigenschaft2: werte[1].value,
rollEigenschaft3: werte[2].value,
eigenschaft1: werte[0].name,
eigenschaft2: werte[1].name,
eigenschaft3: werte[2].name,
})
}
})
context.hasSpells = context.spells.length > 0;
}
async #getModsOfAttribute(keyPath) {
let returnValue = [];
Array.from(this.object.appliedEffects).forEach(
(e) =>
e.changes.filter(c => c.key === keyPath).forEach(change => {
returnValue.push({
name: e.name,
value: change.value > 0 ? "+" + change.value : "" + change.value,
icon: e.icon,
})
}))
return returnValue;
}
async #addAttributesToContext(context) {
context.mods = {
"mu": await this.#getModsOfAttribute('system.attribute.mu.mod'),
"kl": await this.#getModsOfAttribute('system.attribute.kl.mod'),
"in": await this.#getModsOfAttribute('system.attribute.in.mod'),
"ch": await this.#getModsOfAttribute('system.attribute.ch.mod'),
"ff": await this.#getModsOfAttribute('system.attribute.ff.mod'),
"ge": await this.#getModsOfAttribute('system.attribute.ge.mod'),
"ko": await this.#getModsOfAttribute('system.attribute.ko.mod'),
"kk": await this.#getModsOfAttribute('system.attribute.kk.mod')
}
context.attributes = [
{
eigenschaft: "mu",
name: "MU",
tooltip: "Mut",
wert: (context.derived.attribute.mu.aktuell + context.derived.attribute.mu.mod) ?? 0,
wert: actorData.system.attribute.mu.aktuell ?? 0,
},
{
eigenschaft: "kl",
name: "KL",
tooltip: "Klugheit",
wert: (context.derived.attribute.kl.aktuell + context.derived.attribute.kl.mod) ?? 0,
wert: actorData.system.attribute.kl.aktuell ?? 0,
},
{
eigenschaft: "in",
name: "IN",
tooltip: "Intuition",
wert: (context.derived.attribute.in.aktuell + context.derived.attribute.in.mod) ?? 0,
wert: actorData.system.attribute.in.aktuell ?? 0,
},
{
eigenschaft: "ch",
name: "CH",
tooltip: "Charisma",
wert: (context.derived.attribute.ch.aktuell + context.derived.attribute.ch.mod) ?? 0,
wert: actorData.system.attribute.ch.aktuell ?? 0,
},
{
eigenschaft: "ff",
name: "FF",
tooltip: "Fingerfertigkeit",
wert: (context.derived.attribute.ff.aktuell + context.derived.attribute.ff.mod) ?? 0,
wert: actorData.system.attribute.ff.aktuell ?? 0,
},
{
eigenschaft: "ge",
name: "GE",
tooltip: "Geschicklichkeit",
wert: (context.derived.attribute.ge.aktuell + context.derived.attribute.ge.mod) ?? 0,
wert: actorData.system.attribute.ge.aktuell ?? 0,
},
{
eigenschaft: "ko",
name: "KO",
tooltip: "Konstitution",
wert: (context.derived.attribute.ko.aktuell + context.derived.attribute.ko.mod) ?? 0,
wert: actorData.system.attribute.ko.aktuell ?? 0,
},
{
eigenschaft: "kk",
name: "KK",
tooltip: "Körperkraft",
wert: (context.derived.attribute.kk.aktuell + context.derived.attribute.kk.mod) ?? 0,
wert: actorData.system.attribute.kk.aktuell ?? 0,
},
];
@ -264,22 +171,12 @@ export class CharacterSheet extends ActorSheet {
);
}
#addSpecialAbilitiesToContext(context) {
context.specialAbilities = [];
const actorData = context.data;
Object.values(actorData.items).forEach((item) => {
if (item.type === "SpecialAbility") {
context.specialAbilities.push({
id: item._id,
name: item.name,
});
}
}
);
#findEquipmentOnSlot(slot, setNumber) {
return this.object.items.get(this.object.system.heldenausruestung[setNumber][slot])
}
#findEquipmentOnSlot(slot, setNumber) {
return this.object.items.get(this.object.system.heldenausruestung[setNumber]?.[slot])
#findTalentsOfEquipment(equipment) {
}
#addActionsToContext(context) {
@ -287,18 +184,16 @@ export class CharacterSheet extends ActorSheet {
context.actions = am.evaluate()
}
#isWorn(itemId) {
#isWorn(itemId, setId) {
const slots = PlayerCharacterDataModel.getSlots()
const set = this.object.system.heldenausruestung[this.object.system.setEquipped]
if (set) {
const set = this.object.system.heldenausruestung[setId]
for (const slot of slots) {
const equipmentSlotId = set[slot]
if (equipmentSlotId === itemId) {
return slot
}
}
}
return false
}
@ -312,15 +207,13 @@ export class CharacterSheet extends ActorSheet {
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.keper = Math.min((context.actor.system.kap.aktuell / context.actor.system.kap.max) * 100, 100);
context.aspper = Math.min((context.actor.system.asp.aktuell / context.actor.system.asp.max) * 100, 100);
context.lepcurrent = context.actor.system.lep.aktuell ?? 0
context.aupcurrent = context.actor.system.aup.aktuell ?? 0
const fernkampf = this.#findEquipmentOnSlot("fernkampf", context.actor.system.setEquipped)
const links = this.#findEquipmentOnSlot("links", context.actor.system.setEquipped)
const rechts = this.#findEquipmentOnSlot("rechts", context.actor.system.setEquipped)
const fernkampf = this.#findEquipmentOnSlot("fernkampf", 0)
const links = this.#findEquipmentOnSlot("links", 0)
const rechts = this.#findEquipmentOnSlot("rechts", 0)
context.attacks = [];
if (fernkampf) {
@ -376,7 +269,7 @@ export class CharacterSheet extends ActorSheet {
}
prepareEigenschaftRoll(actorData, name) {
if (name && name !== "*") {
if (name) {
return actorData.system.attribute[name.toLowerCase()].aktuell
} else {
return 0
@ -389,24 +282,16 @@ export class CharacterSheet extends ActorSheet {
context.carryingweight = 0;
Object.values(actorData.items).forEach((item, index) => {
if (item.type === "Equipment") {
// worn items are halved weight
let effectiveWeight = item.system.weight ?? 0
if (this.#isWorn(item._id)) {
effectiveWeight = item.system.weight ? item.system.weight / 2 : 0
}
context.equipments.push({
index: index,
id: item._id,
quantity: item.system.quantity,
name: item.name,
icon: item.img ?? "",
weight: item.system.weight,
worn: this.#isWorn(item._id)
weight: item.system.weight ?? 0,
worn: this.#isWorn(item._id, 0)
})
context.carryingweight += item.system.quantity * effectiveWeight;
context.carryingweight += item.system.quantity * item.system.weight;
}
})
context.maxcarryingcapacity = actorData.system.attribute.kk.aktuell
@ -592,23 +477,22 @@ export class CharacterSheet extends ActorSheet {
}
}
#mapAllSets() {
#getEquipmentset(setId) {
const equipmentSet = this.object.system.heldenausruestung[setId]
const updateObject = {}
Array.from(this.object.system.heldenausruestung).forEach((equipmentSet, index) => {
updateObject[`system.heldenausruestung.${index}.links`] = equipmentSet.links;
updateObject[`system.heldenausruestung.${index}.rechts`] = equipmentSet.rechts;
updateObject[`system.heldenausruestung.${index}.brust`] = equipmentSet.brust;
updateObject[`system.heldenausruestung.${index}.bauch`] = equipmentSet.bauch;
updateObject[`system.heldenausruestung.${index}.ruecken`] = equipmentSet.ruecken;
updateObject[`system.heldenausruestung.${index}.kopf`] = equipmentSet.kopf;
updateObject[`system.heldenausruestung.${index}.fernkampf`] = equipmentSet.fernkampf;
updateObject[`system.heldenausruestung.${index}.munition`] = equipmentSet.munition;
updateObject[`system.heldenausruestung.${index}.armlinks`] = equipmentSet.armlinks;
updateObject[`system.heldenausruestung.${index}.armrechts`] = equipmentSet.armrechts;
updateObject[`system.heldenausruestung.${index}.beinlinks`] = equipmentSet.beinlinks;
updateObject[`system.heldenausruestung.${index}.beinrechts`] = equipmentSet.beinrechts;
})
// TODO: there's got to be a better angle!
updateObject[`system.heldenausruestung.${setId}.links`] = equipmentSet.links;
updateObject[`system.heldenausruestung.${setId}.rechts`] = equipmentSet.rechts;
updateObject[`system.heldenausruestung.${setId}.brust`] = equipmentSet.brust;
updateObject[`system.heldenausruestung.${setId}.bauch`] = equipmentSet.bauch;
updateObject[`system.heldenausruestung.${setId}.ruecken`] = equipmentSet.ruecken;
updateObject[`system.heldenausruestung.${setId}.kopf`] = equipmentSet.kopf;
updateObject[`system.heldenausruestung.${setId}.fernkampf`] = equipmentSet.fernkampf;
updateObject[`system.heldenausruestung.${setId}.munition`] = equipmentSet.munition;
updateObject[`system.heldenausruestung.${setId}.armlinks`] = equipmentSet.armlinks;
updateObject[`system.heldenausruestung.${setId}.armrechts`] = equipmentSet.armrechts;
updateObject[`system.heldenausruestung.${setId}.beinlinks`] = equipmentSet.beinlinks;
updateObject[`system.heldenausruestung.${setId}.beinrechts`] = equipmentSet.beinrechts;
return updateObject;
}
@ -644,137 +528,13 @@ export class CharacterSheet extends ActorSheet {
}
#addLiturgiesToContext(context) {
const actorData = context.data;
context.liturgies = [];
context.blessings = [];
Object.values(actorData.items).forEach((item, index) => {
if (item.type === "Blessing") {
context.blessings.push({
deity: item.system.gottheit,
value: item.system.wert
})
}
})
Object.values(actorData.items).forEach((item, index) => {
if (item.type === "Liturgy") {
context.blessings.forEach(({deity, value}) => {
let insertObject = context.liturgies.find(p => p.deity === deity);
if (!insertObject) {
insertObject = {
deity: deity,
lkp: value,
O: [],
I: [],
II: [],
III: [],
IV: [],
V: [],
VI: [],
VII: [],
VIII: [],
"NA": [],
countO: 1,
countI: 1,
countII: 1,
countIII: 1,
countIV: 1,
countV: 1,
countVI: 1,
countVII: 1,
countVIII: 1,
countNA: 0,
total: 3,
}
context.liturgies.push(insertObject);
}
// sort by rank
const rankData = LiturgyData.getRankOfLiturgy(item.system, deity)
if (rankData) {
let {index, name, lkp, mod, costKaP} = rankData;
insertObject["count" + name] = insertObject["count" + name] + 1;
insertObject[name].push({
id: item._id,
name: item.name,
lkpReq: lkp,
lkpMod: mod,
costKaP,
rank: index, // get effective liturgy rank based on deity
liturgiekenntnis: deity,
})
insertObject.total = insertObject.total + 1;
}
})
}
})
// clean up counter
Object.values(context.liturgies).forEach((litObject) => {
if (litObject.O.length === 0) litObject.countO = false;
if (litObject.I.length === 0) litObject.countI = false;
if (litObject.II.length === 0) litObject.countII = false;
if (litObject.III.length === 0) litObject.countIII = false;
if (litObject.IV.length === 0) litObject.countIV = false;
if (litObject.V.length === 0) litObject.countV = false;
if (litObject.VI.length === 0) litObject.countVI = false;
if (litObject.VII.length === 0) litObject.countVII = false;
if (litObject.VIII.length === 0) litObject.countVIII = false;
if (litObject.NA.length === 0) litObject.countNA = false;
})
context.hasLiturgies = context.blessings.length > 0;
}
#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;
}
}
}
async #handleDroppedActiveEffect(actor, activeEffect) {
const array = Array.from(actor.items);
for (let i = 0; i < array.length; i++) {
if (array[i].name === activeEffect.name) {
// replace active effect
actor.deleteEmbeddedDocuments('Item', [array[i].id]).then(() => {
console.log("await")
})
return true;
}
}
}
#handleDroppedAdvantage(actor, advantage) {
const array = Array.from(actor.items);
for (let i = 0; i < array.length; i++) {
if (array[i].name === advantage.name) { // TODO: adjust for uniqueness
return false;
}
}
}
activateListeners(html) {
super.activateListeners(html);
const tabs = new Tabs({
navSelector: ".paperdoll-tabs.tabs",
contentSelector: ".sheet-body.paperdoll-sets",
initial: "set" + (this.object.system.setEquipped + 1)
initial: "set1"
});
tabs.bind(html[0]);
@ -782,12 +542,6 @@ export class CharacterSheet extends ActorSheet {
this._onAttributeRoll(evt);
});
html.on('click', '[data-operation="switchSet"]', (evt) => {
const {id} = evt.currentTarget.dataset;
console.log(id);
this.object.update({"system.setEquipped": id})
})
html.on('click', '.talent.rollable', (evt) => {
this._onTalentRoll(evt);
});
@ -796,32 +550,21 @@ export class CharacterSheet extends ActorSheet {
this._onRoll(evt);
});
// TODO: merge into click.clickable handler
html.on('click', '.talent .name', (evt) => {
this.openEmbeddedDocument(evt.target.dataset.id);
evt.stopPropagation();
})
// TODO: merge into click.clickable handler
html.on('click', '.advantage .name', (evt) => {
this.openEmbeddedDocument(evt.target.dataset.id);
evt.stopPropagation();
})
// TODO: merge into click.clickable handler
html.on('click', '.equipment', (evt) => {
this.openEmbeddedDocument(evt.target.parentElement.dataset.id);
evt.stopPropagation();
})
html.on('click', '.clickable', async (evt) => {
const {id, operation} = evt.currentTarget.dataset;
if (operation === "openActorSheet") {
this.openEmbeddedDocument(id);
evt.stopPropagation();
}
})
html.on('dragstart', '.equipment', (evt) => {
evt.originalEvent.dataTransfer.setData("application/json", JSON.stringify({
documentId: evt.currentTarget.dataset.id
@ -836,8 +579,7 @@ export class CharacterSheet extends ActorSheet {
if (actor === this.object._id && documentId) { // managing equipped items
//const slot = this.#isWorn(documentId, setId)
//const updateObject = await this.#getEquipmentset(Number(setId))
const updateObject = this.#mapAllSets()
const updateObject = this.#getEquipmentset(setId)
updateObject[`system.heldenausruestung.${setId}.${target}`] = documentId;
console.log(updateObject);
@ -898,7 +640,7 @@ export class CharacterSheet extends ActorSheet {
callback: (event) => {
const {setId, target, actor} = event[0].dataset
const updateObject = this.#mapAllSets()
const updateObject = this.#getEquipmentset(setId)
updateObject[`system.heldenausruestung.${setId}.${target}`] = null;
this.object.update(updateObject);
@ -907,86 +649,57 @@ export class CharacterSheet extends ActorSheet {
}
]);
html.on('click', '.liturgy.rollable', async (evt) => {
evt.stopPropagation();
const {id, rank, lkp, deity} = evt.currentTarget.dataset;
const document = await this.object.items.get(id)
const data = {};
data.rank = rank;
data.lkp = lkp;
data.deity = deity;
data.variations = [];
const ranks = LiturgyData.ranks
ranks.forEach(rank => {
if (document.system.auswirkung[rank]) {
data.variations.push({
rank,
effect: document.system.auswirkung[rank]
})
}
})
data.mods = [];
const htmlContent = await renderTemplate('systems/DSA_4-1/templates/dialog/modify-liturgy.hbs', data);
const dialogData = {
title: document.name,
content: htmlContent,
data: {},
buttons: {
submit: {
label: "Wirken",
icon: '<i class="fas fa-die"></i>',
callback: (html) => {
},
},
},
}
dialogData.render = new ModifyLiturgy(data).handleRender
const dialog = new Dialog(dialogData, {
classes: ['dsa41', 'dialog', 'liturgy'],
height: 480
})
dialog.render(true);
#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;
})
}
}
}
#handleDroppedAdvantage(actor, advantage) {
const array = Array.from(actor.items);
for (let i = 0; i < array.length; i++) {
if (array[i].name === advantage.name) { // TODO: adjust for uniqueness
return false;
}
}
}
#handleDroppedEquipment(actor, equipment) {
const array = Array.from(actor.items);
for (let i = 0; i < array.length; i++) {
if (array[i].name === equipment.name) { // TODO: adjust item quantity if item is the same
console.log(equipment);
return false;
}
}
}
#handleDroppedLiturgy(actor, liturgy) {
const array = Array.from(actor.items);
for (let i = 0; i < array.length; i++) {
if (array[i].name === liturgy.name) { // TODO: allow multiple miracles with the same name
return false;
}
}
}
static onDroppedData(actor, characterSheet, data) {
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":
return characterSheet.#handleDroppedSkill(actor, document); // on false cancel this whole operation
case "Advantage":
return characterSheet.#handleDroppedAdvantage(actor, document);
case "Equipment":
return characterSheet.#handleDroppedEquipment(actor, document);
#handleDroppedSpecialAbility(actor, specialAbility) {
const array = Array.from(actor.items);
for (let i = 0; i < array.length; i++) {
if (array[i].name === specialAbility.name) { // TODO: allow multiple miracles with the same name
default:
return false;
}
}
}
}
}

View File

@ -72,10 +72,10 @@ export class CreatureSheet extends foundry.appv1.sheets.ActorSheet {
})
html.on('click', '.editor .add-attack', async (evt) => {
const name = html[0].querySelector('#attack_name').value
const at = html[0].querySelector('#attack_at').value
const pa = html[0].querySelector('#attack_pa').value
const tp = html[0].querySelector('#attack_tp').value
const name = evt.target.parentElement.querySelector('#attack_name').value
const at = evt.target.parentElement.querySelector('#attack_at').value
const pa = evt.target.parentElement.querySelector('#attack_pa').value
const tp = evt.target.parentElement.querySelector('#attack_tp').value
const newAttack = {
name,

View File

@ -15,54 +15,6 @@ export class GroupSheet extends ActorSheet {
});
}
static async onDroppedData(group, sheet, data) {
if (data.type === "Actor") {
const uuid = await foundry.utils.parseUuid(data.uuid);
const character = await (game.actors.get(uuid.id))
// check if character already is part of the group
if (group.system.characters.includes(character._id)) {
ui.notifications.warn(`${character.name} befindet sich bereits in der Heldengruppe ${group.name}`)
return false;
}
// update group
const settings = {...group.system.settings}
character.items.filter((i) => i.type === "Advantage").forEach((advantage) => {
settings[this.#stringToKeyFieldName(advantage.name)] = false
})
character.items.filter((i) => i.type === "Skill").forEach((skill) => {
settings[this.#stringToKeyFieldName(skill.name)] = false
}),
await group.update({
system: {
characters: [
...group.system.characters,
character._id
],
settings: settings
}
})
ui.notifications.info(`${character.name} ist der Heldengruppe ${group.name} beigetreten`)
}
if (data.type === "Equipment") {
const uuid = await foundry.utils.parseUuid(data.uuid);
const equipment = await (game.actors.get(uuid.id))
ui.notifications.info(`${equipment.name} befindet sich nun im Inventar der Heldengruppe ${group.name}`)
return true;
}
}
#stringToKeyFieldName(s) {
return s.replace(/[ \[\]:]/g, "_").toLowerCase()
}
/** @override */
get template() {
return `systems/DSA_4-1/templates/actor/group-sheet.hbs`;
}
/** @override */
async getData() {
const context = super.getData();
@ -73,11 +25,8 @@ export class GroupSheet extends ActorSheet {
// Add the actor's data to context.data for easier access, as well as flags.
context.system = groupData.system;
context.flags = groupData.flags;
context.characters = []
context.fields = [];
const hiddenFields = Object.entries(groupData.system.settings).filter(([key, value]) => value === true).map(([key, value]) => key)
context.characters = [];
for (const characterId of groupData.system.characters) {
const character = await game.actors.get(characterId)
@ -96,14 +45,14 @@ export class GroupSheet extends ActorSheet {
{name: "KO", value: character.system.attribute.ko.aktuell},
{name: "KK", value: character.system.attribute.kk.aktuell},
],
advantages: character.items.filter((i) => i.type === "Advantage").filter((i) => hiddenFields.includes(this.#stringToKeyFieldName(i.name))).map((advantage) => {
advantages: character.items.filter((i) => i.type === "Advantage").map((advantage) => {
return {
name: advantage.name,
id: advantage._id,
value: advantage.system.value,
}
}),
skills: character.items.filter((i) => i.type === "Skill").filter((i) => hiddenFields.includes(this.#stringToKeyFieldName(i.name))).map((skill) => {
skills: character.items.filter((i) => i.type === "Skill").map((skill) => {
return {
name: skill.name,
taw: skill.system.taw,
@ -117,6 +66,7 @@ export class GroupSheet extends ActorSheet {
)
}
context.equipments = [];
const actorData = context.data;
Object.values(actorData.items).forEach((item, index) => {
@ -134,6 +84,41 @@ export class GroupSheet extends ActorSheet {
return await context;
}
/** @override */
get template() {
return `systems/DSA_4-1/templates/actor/group-sheet.hbs`;
}
static async onDroppedData(group, sheet, data) {
if (data.type === "Actor") {
const uuid = await foundry.utils.parseUuid(data.uuid);
const character = await (game.actors.get(uuid.id))
// check if character already is part of the group
if (group.system.characters.includes(character._id)) {
ui.notifications.warn(`${character.name} befindet sich bereits in der Heldengruppe ${group.name}`)
return false;
}
// update group
await group.update({
system: {
characters: [
...group.system.characters,
character._id
]
}
})
ui.notifications.info(`${character.name} ist der Heldengruppe ${group.name} beigetreten`)
}
if (data.type === "Equipment") {
const uuid = await foundry.utils.parseUuid(data.uuid);
const equipment = await (game.actors.get(uuid.id))
ui.notifications.info(`${equipment.name} befindet sich nun im Inventar der Heldengruppe ${group.name}`)
return true;
}
}
openEmbeddedDocument(documentId) {
this.object.items.get(documentId).sheet.render(true)
}

View File

@ -1,50 +0,0 @@
export class LiturgySheet extends ItemSheet {
/**@override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ['dsa41', 'sheet', 'item', 'liturgy'],
width: 520,
height: 480,
tabs: [
{
navSelector: '.sheet-tabs',
contentSelector: '.sheet-body',
initial: 'description',
},
],
});
}
/** @override */
get template() {
return `systems/DSA_4-1/templates/item/item-liturgy-sheet.hbs`;
}
/** @override */
getData() {
// Retrieve the data structure from the base sheet. You can inspect or log
// the context variable to see the structure, but some key properties for
// sheets are the actor object, the data object, whether or not it's
// editable, the items array, and the effects array.
const context = super.getData();
// Use a safe clone of the actor data for further operations.
const liturgyData = context.data;
// Add the actor's data to context.data for easier access, as well as flags.
context.system = liturgyData.system;
context.flags = liturgyData.flags;
context.json = JSON.stringify(liturgyData);
return context;
}
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (this.isEditable) {
}
}
}

View File

@ -1,51 +0,0 @@
export class SpecialAbilitySheet extends ItemSheet {
/**@override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ['dsa41', 'sheet', 'item', 'specialability'],
width: 520,
height: 480,
tabs: [
{
navSelector: '.sheet-tabs',
contentSelector: '.sheet-body',
initial: 'description',
},
],
});
}
/** @override */
get template() {
return `systems/DSA_4-1/templates/item/item-special-ability-sheet.hbs`;
}
/** @override */
getData() {
// Retrieve the data structure from the base sheet. You can inspect or log
// the context variable to see the structure, but some key properties for
// sheets are the actor object, the data object, whether or not it's
// editable, the items array, and the effects array.
const context = super.getData();
// Use a safe clone of the actor data for further operations.
const advantageData = context.data;
// Add the actor's data to context.data for easier access, as well as flags.
context.system = advantageData.system;
context.flags = advantageData.flags;
context.json = JSON.stringify(advantageData.system, null, 4);
return context;
}
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.isEditable) {
}
}
}

View File

@ -34,13 +34,7 @@ export class VornachteilSheet extends ItemSheet {
// Add the actor's data to context.data for easier access, as well as flags.
context.system = advantageData.system;
context.flags = advantageData.flags;
context.choices = {}
context.system.auswahl.forEach(a => {
context.choices[a] = a
})
context.hasChoices = context.system.auswahl.length > 0;
context.hasModality = context.system.value == null
context.json = JSON.stringify(advantageData.system, null, 4);
return context;
}
@ -49,9 +43,7 @@ export class VornachteilSheet extends ItemSheet {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.isEditable) {
}
if (!this.isEditable) return;
}
}

View File

@ -1,7 +1,3 @@
import {LiturgyData} from "../data/miracle/liturgydata.mjs";
import {BlessingDataModel} from "../data/blessing.mjs";
import {Blessing} from "../documents/blessing.mjs";
let months = [
"Praios",
"Rondra",
@ -75,7 +71,7 @@ function getJsonFromXML(dom) {
}
async function addSkillFromCompendiumByNameToActor(talentName, taw, actor) {
const compendiumOfSkills = game.packs.get('DSA_4-1.talente');
const compendiumOfSkills = game.packs.get('DSA_4-1.talente-brw');
const talentId = compendiumOfSkills.index.find(skill => skill.name === talentName)
if (talentId) {
@ -106,40 +102,6 @@ async function addAdvantageFromCompendiumByNameToActor(advantageName, advantageV
}
}
async function addSpellsFromCompendiumByNameToActor(spellName, zfw, representation, hauszauber, actor) {
const compendiumOfSpells = game.packs.get('DSA_4-1.spells');
const SCREAMING_NAME = spellName.toUpperCase()
const spellId = compendiumOfSpells.index.find(spell => spell.name === SCREAMING_NAME)
if (spellId) {
const spell = await compendiumOfSpells.getDocument(spellId._id);
try {
const embeddedDocument = (await actor.createEmbeddedDocuments('Item', [spell]))[0]
embeddedDocument.update({system: {zfw: zfw, hauszauber: hauszauber, repräsentation: representation}});
} catch (error) {
console.error(`${spell} not found in items`, error)
}
}
}
async function addLiturgiesFromCompendiumByNameToActor(liturgyName, actor) {
const compendiumOfLiturgies = game.packs.get('DSA_4-1.liturgien');
const liturgyId = compendiumOfLiturgies.index.find(liturgy => {
return liturgy.name === LiturgyData.lookupAlias(liturgyName.split(" (")[0])
})
if (liturgyId) {
const liturgy = await compendiumOfLiturgies.getDocument(liturgyId._id);
try {
await actor.createEmbeddedDocuments('Item', [liturgy])
} catch (error) {
console.error(`${liturgy} not found in items`, error)
}
}
}
/**
* gets the text content of a file
* @param file the file with the desired content
@ -174,27 +136,9 @@ function calculateBirthdate(json) {
function mapSkills(actor, held) {
for (let talent in held.talentliste.talent) {
talent = held.talentliste.talent[talent]
// hook liturgy
if (talent.name.startsWith("Liturgiekenntnis")) {
actor.createEmbeddedDocuments('Item', [
new Blessing({
name: talent.name,
type: "Blessing",
system: {
gottheit: new RegExp("\\((.+)\\)").exec(talent.name)[1],
wert: talent.value
}
})
])
} else {
// proceed
addSkillFromCompendiumByNameToActor(talent.name, talent.value, actor)
}
}
}
function mapAdvantages(actor, held) {
for (let advantage in held.vt.vorteil) {
@ -203,20 +147,6 @@ function mapAdvantages(actor, held) {
}
}
function mapSpells(actor, held) {
for (let spell in held.zauberliste.zauber) {
spell = held.zauberliste.zauber[spell]
addSpellsFromCompendiumByNameToActor(spell.name, spell.value, spell.repraesentation, spell.hauszauber === "true", actor)
}
}
function mapMiracles(actor, liturgies) {
for (let liturgy in liturgies) {
liturgy = liturgies[liturgy]
addLiturgiesFromCompendiumByNameToActor(liturgy.name, actor)
}
}
/**
* parses a json into a fitting character-json
* @param rawJson the json parsed from the Helden-Software XML
@ -251,14 +181,16 @@ function mapRawJson(actor, rawJson) {
held.basis.rasse.aussehen.aussehentext0,
held.basis.rasse.aussehen.aussehentext1,
held.basis.rasse.aussehen.aussehentext2,
held.basis.rasse.aussehen.aussehentext3].join('\n')
held.basis.rasse.aussehen.aussehentext3,
]
json.meta.familie = [
held.basis.rasse.aussehen.familietext0,
held.basis.rasse.aussehen.familietext1,
held.basis.rasse.aussehen.familietext2,
held.basis.rasse.aussehen.familietext3,
held.basis.rasse.aussehen.familietext4,
held.basis.rasse.aussehen.familietext5].join('\n')
held.basis.rasse.aussehen.familietext5,
]
json.meta.titel = held.basis.rasse.aussehen.titel
json.meta.stand = held.basis.rasse.aussehen.stand
let attributes = held.eigenschaften.eigenschaft
@ -334,9 +266,19 @@ function mapRawJson(actor, rawJson) {
json.liturgien = liturgies
mapSkills(actor, held)
mapSpells(actor, held)
mapMiracles(actor, liturgies)
let spells = []
/*for (let spell in held.zauberliste.zauber) {
spell = held.zauberliste.zauber[spell]
spells.push({
name: spell.name,
rep: spell.repraesentation,
hauszauber: spell.hauszauber === "true",
zfw: spell.value,
anmerkungen: spell.zauberkommentar,
komplexitaet: spell.k,
})
}*/
json.zauber = spells
let combatValues = []
for (let combatValue in held.kampf.kampfwerte) {
combatValue = held.kampf.kampfwerte[combatValue]

View File

@ -1,5 +1,9 @@
{
"_id": "oMOnOMtQmZJBRney",
"_key": "!items!oMOnOMtQmZJBRney",
"type": "Skill",
"name": "Betören",
"system": {
"gruppe": "Gesellschaft",
"probe": [
"IN",
@ -15,3 +19,4 @@
"behinderung": "-2",
"talent": "Die Ausstrahlung auf das andere Geschlecht der eigenen Spezies wird durch dieses Talent bewertet. Eine gelungene Talentprobe besagt, dass das Opfer der Annäherungsversuche dem Helden gewogen ist. Was die betörte Persen harten Nüssen beiderlei Geschlechts kann der Meister die Betören-Probe mit Zuschlägen versehen. Will ein Held versuchen, ein Mitglied einer anderen Art (speziell zwischen Elfen und Zwergen) zu betören, dann sollte der Meister deutliche Probenzuschläge verlangen; weniger heftige Zuschläge sind für Betörungsversuche gegenüber dem eigenen Geschlecht erforderlich."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "wSQAMG9yqfjdjrP9",
"_key": "!items!wSQAMG9yqfjdjrP9",
"type": "Skill",
"name": "Etikette",
"system": {
"gruppe": "Gesellschaft",
"probe": [
"KL",
@ -11,3 +15,4 @@
"behinderung": "-2",
"talent": "So mancher Held kann unversehens in feine Gesellschaft geraten sei es, weil er als Belohnung für eine besondere Tat zu einem Hofball oder einer Audienz eingeladen wird, sei es, weil er am Herzogenhof einem Schurken nachspioniert. Wie aber lautet die korrekte Anredeform für die Tochter des Markgrafen, welcher Anwesende wird zuerst gegrüßt, welches Wappen trägt das Haus Sturmfels-Mersingen? Aber auch: Welche Weine gelten momentan als schick? Welche Mode trägt man am Horashof? Ist es geschmacklos, süße Pastetchen nach Hammelaufläufen zu reichen? Da der Spieler all diese Dinge unmöglich im Kopf behalten kann, sollte sein Held sich möglichst auf dem Feld der Etikette auskennen. Etikette-Proben können vom Meister verdeckt gewürfelt werden."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "Msv0BWdwlNlF2ETu",
"_key": "!items!Msv0BWdwlNlF2ETu",
"type": "Skill",
"name": "Gassenwissen",
"system": {
"gruppe": "Gesellschaft",
"probe": [
"KL",
@ -11,3 +15,4 @@
"behinderung": "-4",
"talent": "Dieses Talent ermöglicht eine rasche Orientierung in einer fremden Stadt. Erfolgreiche Pro­ben veranlassen den Meis­ter zu Antworten auf Fragen wie die folgenden: Wo treffe ich voraussichtlich auf Gardisten? Wo finde ich einen preiswerten Unterschlupf? Wo kann ich hier am leichtesten meinem Steckenpferd (der Beutelschneiderei) nachgehen? In welchem Tempel hat man am ehesten ein Herz für eine arme Seele, die sich für eine kräftige Mahlzeit zu jedem beliebigen Gott bekennt? Auch städtebauliche Fragen können mit diesem Talent beantwortet werden: Wie erkenne ich einen Eingang zu einem für Menschen passend großen Kanalisationsschacht? Ist in dieser Stadt mit Sackgassen oder abgeschlossenen Hinterhöfen zu rechnen? Ebenfalls hilfreich ist in diesem Zusammenhang die Sonderfertigkeit Ortskenntnis, aber auch die Vorteile Innerer Kompass bzw. Richtungssinn. Proben auf Gassenwissen können vom Meister verdeckt gewürfelt werden."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "HWN1FkoCA2nz4Upu",
"_key": "!items!HWN1FkoCA2nz4Upu",
"type": "Skill",
"name": "Lehren",
"system": {
"gruppe": "Gesellschaft",
"probe": [
"KL",
@ -10,3 +14,4 @@
],
"talent": "Nicht jedem ist es gegeben, eigenes Wissen und eigene Fertigkeiten so an andere weiterzugeben, dass diese tatsächlich Lehren für sich selber daraus ziehen können. Da vermutlich nur wenige Helden sich ernsthaft für eine Lehrmeister-Tätigkeit interessieren, wird die Pflege dieses Talentes wohl hauptsächlich Meisterpersonen vorbehalten bleiben."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "FQutitgTp9Lcko9C",
"_key": "!items!FQutitgTp9Lcko9C",
"type": "Skill",
"name": "Menschenkenntnis",
"system": {
"gruppe": "Gesellschaft",
"probe": [
"KL",
@ -8,3 +12,4 @@
],
"talent": "Eine Talentprobe hilft dem Helden, die wahren Absichten einer Meisterperson zu durchschauen. Die Zuverlässigkeit eines Bergführers kann auf diese Art ebenso über prüft werden wie die Ehrlichkeit eines Rosshändlers. Menschenkenntnis-Proben werden prinzipiell vom Meister verdeckt ausgeführt. Je nach Gelingen teilt der Meister dem Spieler mit, welche Gefühle die Person bei dem Helden auslöst &ndash; dies kann bei einer misslungenen Probe auch durchaus eine Fehleinschätzung sein. Um zu verhindern, dass der Held mit Hilfe einer einfachen Menschenkenntnis-Probe den zentralen Bösewicht eines Abenteuers schon bei der ersten Begegnung entlarvt, kann der Meister die Talentprobe mit einem geheimen Zuschlag belegen, der sich üblicherweise aus der Erfahrung der Meisterperson ergibt. Eine andere Möglichkeit ist eine vergleichende Probe gegen den Überreden-Wert desjenigen, der durchschaut werden soll. <br/> Um die Absichten eines Wesens aus einer fremden Spezies einzuschätzen, ist auf jeden Fall eine erschwerte Probe erforderlich; bei Personen aus anderen Kulturen (also solchen, zu denen man über kein passendes Kulturwissen verfügt) sind Zuschläge zumindest empfehlenswert."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "NgnteXT2iTyRjNZp",
"_key": "!items!NgnteXT2iTyRjNZp",
"type": "Skill",
"name": "Sich Verkleiden",
"system": {
"gruppe": "Gesellschaft",
"probe": [
"MU",
@ -11,3 +15,4 @@
"behinderung": "*2",
"talent": "Der Spielleiter legt die Zuschläge auf die Probe nach Art der gewünschten Verkleidung fest, auch die hervorstechenden Körpermerkmale des Helden sind zu berücksichtigen. (Ein hünenhafter Thorwaler hat gewisse Schwierigkeiten, als zierliches Mohamädel aufzutreten.) Eine gelungene Sich Verkleiden-Probe bewirkt, dass einem Helden seine Verkleidung von der Umgebung zunächst einmal abgenommen wird, die Kleidung also ebenso überzeugend ist wie die Bewegungen und das Verhalten. Doch auch ein gut verkleideter Held muss sein Gegenüber überreden, wenn er mit ihm ins Gespräch kommt. Hohe TaP* aus der Sich Verkleiden-Probe mögen die Überreden-Probe erleichtern, wer­­den sie aber kaum überflüssig machen. Ebenfalls in diesem Talent enthalten ist eine grundlegende Kenntnis der Mode und des Schminkens."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "WOVcxACW8cFeBqTC",
"_key": "!items!WOVcxACW8cFeBqTC",
"type": "Skill",
"name": "Überreden",
"system": {
"gruppe": "Gesellschaft",
"probe": [
"MU",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Mit diesem Talent ist ein Held in der Lage, sein Gegenüber mit einem Wortgewitter so zu verwirren, dass sich dieses zumindest kurzfristig vom Helden zu bestimmten Handlungen bewegen lässt. Anwendungsgebiete dieses Talents sind:<ul><li><b>Feilschen:</b> Neben anderen gehört auch das Feilschen zu den Fertigkeiten, die nach Möglichkeit nicht durch Proben, sondern durch reale Spieleraktionen dargestellt werden können. In diesem Fall lässt sich der Meister je nach Talentwert des Helden leichter, schwieriger oder gar nicht herunterhandeln. Im Interesse einer zügigen Spielabwicklung kann aber auch eine Feilschen-Probe gewürfelt werden. Der Spieler kann sich selbst Zuschläge auf die Probe auferlegen je höher der Zuschlag, desto höher der Prozentsatz, um den er den Verkäufer herunterhandeln kann: +0 = 5 %, +1 = 10 %, +2 = 15 % usw. bis zu einem Maximum von +9 = 50 %. Hier ist durchaus auch eine vergleichende Probe möglich. Auch das Anpreisen, also die Fertigkeit, eine beliebige Ware so lautstark und wortgewandt zu präsentieren, dass sich zumindest potentielle Käufer einfinden, die die Ware begutachten, fällt unter diesen Bereich.</li><li><b>Betteln:</b> Dies umfasst sowohl die Kenntnis der richtigen Verkleidung als auch der entsprechenden herzerweichenden Sprüche, um mitleidigen Seelen einige Heller aus der Tasche zu ziehen. Betteln ist in den Städten nicht nur unumgänglicher Zwang für die Ärmsten der Armen, nein, es hat sich über die Jahre zu einer Art Lehrberuf entwickelt, wie ja auch die Bettler in den großen Städten Aventuriens oftmals regelrechte Gilden gegründet haben.</li><li><b>Lügen:</b> Eigentlich sollte man denken, dass jeder Held dieses Talent besitzt, doch es gibt einige Typen, die so gründlich zur Ehrlichkeit erzogen wurden, dass sie auch dann Probleme mit dem Lügen haben, wenn es um ihr Leben geht. Eine Lüge kann nur dann erfolgreich sein, wenn das Opfer im Augenblick keine Möglichkeit hat, den Wahrheitsgehalt einer Aussage zu überprüfen. Eine gelungene Probe kann das Opfer dazu bringen, auf die Überprüfung einer unwahren Aussage zu verzichten. Lügen-Proben können je nach Situation vom Meister verdeckt gewürfelt werden. Eine Lüge lässt sich unter Umständen durch eine erfolgreiche Menschenkenntnis-Probe durchschauen. Die Fertigkeit, vor Gardisten, Richtern und gehörnten Ehegatten eigene Verfehlungen als klein und unwichtig erscheinen zu lassen und schnell zu anderen Themen überzugehen, nennt man üblicherweise nicht Lügen, sondern Herausreden, sie wird aber trotzdem genauso behandelt.</li></ul>"
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "zybPhpBCTeJZNX2C",
"_key": "!items!zybPhpBCTeJZNX2C",
"type": "Skill",
"name": "Überzeugen",
"system": {
"gruppe": "Gesellschaft",
"probe": [
"KL",
@ -15,3 +19,4 @@
"behinderung": "situationsbedingt",
"talent": "Dieses Talent bewirkt bei richtiger Anwendung eine mindestens einige Tage andauernde Bewusstseinsänderung, ganz im Gegensatz zum eher kurzfristigen Überreden. Der Meister legt die Zuschläge auf die entsprechende Probe fest, die von der Klugheit und der Lebenseinstellung des Opfers abhängen. Wer eine Karriere als Geweihter oder Demagoge anstrebt, wird um dieses Talent nicht herumkommen."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "XxPXNovZd9AX2sHM",
"_key": "!items!XxPXNovZd9AX2sHM",
"type": "Skill",
"name": "Abrichten",
"system": {
"gruppe": "Handwerk",
"probe": [
"MU",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Wann immer ein Held seinem Tier ein Kunststück (einem Pferd den stummen Alarm, einem Hund das Apportieren oder Männchen machen) beibringen will, ist eine Probe auf Abrichten fällig. Zuschläge auf die Probe entstehen durch Fehlversuche (+2 für jede gescheiterte Probe), durch die Schwierigkeit des Kunststücks und die grundsätzliche Lernfähigkeit und potentielle Loyalität des Tieres. Übungen, die einem Tier wegen körperlicher oder geistiger Beschränkungen nicht möglich sind, kann ihm auch ein meisterlicher Abrichter nicht beibringen."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "w3wHyimJXv6EjnMw",
"_key": "!items!w3wHyimJXv6EjnMw",
"type": "Skill",
"name": "Ackerbau",
"system": {
"gruppe": "Handwerk",
"probe": [
"IN",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Dies ist die grundlegende Kenntnis von Bodenverhältnissen, Aussaat und Ernte, Feldbestellungs- und Lagerungsmethoden. Mit dem Talent kann man Nutzpflanzen erkennen und unterscheiden und auf diese Art und Weise z.B. eine auf einer einsamen Insel gestrandete Heldengruppe vor dem Verhungern bewahren. Zudem erkennt ein Ackerbau-Kundiger leicht Wert und Haltbarkeit von Nahrungsmitteln. Auf der aktiven Seite heißt dies auch, dass ein entsprechend ausgebildeter Held mit Pflug, Hacke und Dreschflegel umzugehen weiß."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "oHnVR4rpCZes1MBk",
"_key": "!items!oHnVR4rpCZes1MBk",
"type": "Skill",
"name": "Alchimie",
"system": {
"gruppe": "Handwerk",
"probe": [
"MU",
@ -19,3 +23,4 @@
"behinderung": "situationsbedingt",
"talent": "Dieses Talent regelt die Herstellung normaler Chemikalien und wundertätiger Mittel. Der Spieler teilt dem Meister mit, welches alchimistische Gemisch sein Held herstellen will, und der Spielleiter legt den Zuschlag (oder Abzug) auf die erforderliche Probe fest. Bevor es zur Probe kommt, muss der Held natürlich erst einmal in den Besitz der benötigten Zutaten und auch der Rezeptur kommen. Eine gescheiterte Probe auf diesem gefährlichen Gebiet kann mancherlei bewirken: ein harmloses, aber bestialisch stinkendes, grünes Wölkchen zum Beispiel, oder aber einen Urknall, der ein halbes Stadtviertel in Schutt und Asche legt. Der Meister sollte so fair sein, seinen Helden in etwa anzudeuten, was eine gescheiterte Probe für sie bedeuten könnte. Talentproben in Alchimie können auch zur Analyse unbekannter Mixturen dienen aber auch auf diesem Gebiet kann ein Irrtum verhängnisvolle Folgen haben (und hier sollte der Meister auch ruhig verdeckt würfeln)."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "LIxUN4SY7uqj716y",
"_key": "!items!LIxUN4SY7uqj716y",
"type": "Skill",
"name": "Bergbau",
"system": {
"gruppe": "Handwerk",
"probe": [
"IN",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Diese Talent umfasst das sachgerechte Anlegen von Tunneln und Schächten und deren Luftversorgung und Wasserentsorgung. Außerdem ermöglichen Kenntnisse im Bergbau die Beurteilung von unterirdischen Anlagen: Welche Wesen haben sie geschaffen? Wo wurden bauliche Veränderungen vorgenommen? Solche und ähnliche Fragen müssen vom Meister nach einer gelungener Bergbau-Probe des Helden beantwortet werden. Dass sich die meisten aventurischen Zwerge fast von Natur aus auf solche Kenntnisse verstehen, verwundert nicht."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "FfCHnaMJj7yVr04i",
"_key": "!items!FfCHnaMJj7yVr04i",
"type": "Skill",
"name": "Bogenbau",
"system": {
"gruppe": "Handwerk",
"probe": [
"KL",
@ -15,3 +19,4 @@
"behinderung": "situationsbedingt",
"talent": "Bogenbauer und Armbruster beschäftigen sich mit der Herstellung von Schusswaffen und sind die einzigen, die auf Spannung basierende, Projektile verschießende Fernwaffen (und die dazugehörigen Projektile) bauen können. Natürlich können sie auch die Qualität solcher Waffen einschätzen. Im Band Wege des Schwerts stellen wir Ihnen ein detailliertes System zum Bau von Schusswaffen vor; im Rahmen dieser Basisregeln gilt folgende Vorgehensweise: Um einen Bogen oder eine Armbrust herzustellen, benötigt man die richtigen Materialien für Bogen und Sehne (zusätzlich bei der Armbrust für Schaft und Abzug) und muss eine bestimmte Anzahl von Talentproben ablegen. Jede Probe dauert einen halben Tag; bei Misslingen einer Probe kann man versuchen zu retten, was noch zu retten ist, und eine Probe ablegen, die um den doppelten Betrag erschwert ist. Misslingt auch diese, ist die gesamte Waffe missraten und das Material verdorben.<br/>Um Pfeile oder Bolzen herzustellen, ist neben geeignetem Holz und vorhandenen Spitzen noch eine Bogenbau-Probe nötig, um die Spitze und vorallem die Befiederung korrekt anzubringen. Der Zuschlag auf der Probe ist der Tabelle zu entnehmen. Jede Probe zum Herstellen eines Geschosses dauert eine halbe Stunde.<table><tr><th>Typ</th><th>Herstellen</th><th>Geschoss</th></tr><tr><td>Kurzbogen</td><td>4 Proben</td><td>Probe</td></tr><tr><td>Langbogen</td><td>6 Proben</td><td>Probe +1</td></tr><tr><td>Kriegsbogen</td><td>7 Proben</td><td>2 Proben + 3</td></tr><tr><td>Leichte Armbrust</td><td>4 Proben</td><td>2 Proben -1</td></tr><tr><td>Schwere Armbrust</td><td>12 Proben</td><td>2 Proben +1</td></tr><table>"
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "usmU8Ig2qragLaNd",
"_key": "!items!usmU8Ig2qragLaNd",
"type": "Skill",
"name": "Boote Fahren",
"system": {
"gruppe": "Handwerk",
"probe": [
"GE",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Alle Talente, die zur Fortbewegung eines kleinen Wasserfahrzeuges dienen - Rudern, Segeln, Paddeln, Staken - sind hier zusammengefasst. Der Meister sollte es den Helden gestatten, ohne Probe gemächlich über einen stillen Teich zu rudern - irgendwie wird der Held schon ans andere Ufer gelangen. Segeln, Wildwasserfahrten und ähnliche Aktionen sind nur mit gelungener Probe möglich. Für die Handhabung größerer Segel- oder Ruderschiffe sind das Talent SEEFAHRT und eine gröere Anzahl von Matrosen nötig."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "LbFLcgogaKsaMyAZ",
"_key": "!items!LbFLcgogaKsaMyAZ",
"type": "Skill",
"name": "Fahrzeug Lenken",
"system": {
"gruppe": "Handwerk",
"probe": [
"IN",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Die Art des Fahrzeugs und vor allem die Anzahl der Zugtiere bestimmen die Schwierigkeiten einer Probe. Um z.B. eine vierspänige Rennkutsche auch bei geringem Tempo einigermaßen auf der Straße zu halten, muss eine Probe +4 gelingen, ein von einem geduldigen Maultier gezogener Lastkarren fährt sich da schon bedeutend gemütlicher. Verschärfte Proben werden fällig, wenn gewagte Manöver ausgeführt werden sollen: im scharfen Galopp ein Stadttor passieren oder ein von sechs durchgehend Darpatrindern gezogenes Brauereigespann einbremsen. Ebenfalls erschwert ist das Steuern ungewohnter Fahrzeuge wie Hundeschlitten für solche Helden, die bislang nur Erfahrung mit Pferdekutschen haben."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "rCpbo6Hwvu3Poxp9",
"_key": "!items!rCpbo6Hwvu3Poxp9",
"type": "Skill",
"name": "Falschspiel",
"system": {
"gruppe": "Handwerk",
"probe": [
"MU",
@ -15,3 +19,4 @@
"behinderung": "situationsbedingt",
"talent": "Je nach Erfolg der Probe kann der Held entweder sein eingesetztes Geld vervielfachen oder (bei einem ausgespielten Glücksspiel) Würfelergebnisse zu seinen Gunsten ändern, neue Karten ziehen und dergleichen. Das Scheitern einer Probe kann den Helden in arge Bedrängnis bringen (notorische Falschspieler genießen überall in Aventurien eine extrem kurze Lebenserwartung). In Kombination mit einer Sinnenschärfe-Probe kann ein hohes Falschspiel-Talent auch dazu dienen, eventuelle Mogeleien zu enttarnen. (Aber auch hier ist absolute Vorsicht angeraten: Aus oben genannten Gründen ist eine falsche Bezichtigung meist Grund genug für eine Duellforderung.) Falschspiel-Proben können natürlich auch vom Meister verdeckt gewürfelt werden."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "aTCyxWYkNfnI7XGi",
"_key": "!items!aTCyxWYkNfnI7XGi",
"type": "Skill",
"name": "Feinmechanik",
"system": {
"gruppe": "Handwerk",
"probe": [
"KL",
@ -15,3 +19,4 @@
"behinderung": "situationsbedingt",
"talent": "Grundlage dieser Tätigkeit ist die Fein- oder Goldschmiedekunst, mit der man Schmuck und andere filigrane Arbeiten aus Gold, Silber, Mondsilber und anderen Edelmetallen herstellen kann. DAss sich Feinmechaniker daher auch mit Wert und Qualität von Schmuckwaren auskennen, verstehen sich von selbst. Der nächste Schritt sind Gravuren in verschiedenen Metallen (für Schilder und Medaillen bis hin zum künstlerischen Kupferstich), der Guss von kleinen und kleinsten Formen (für Siegel- oder Prägestöcke und auch für deren Fälschung) und schließlich der Zusammenbau all dieser Teile zu Uhrwerken, kleinsten Schlössern, winzigen Armbrustmechanismen oder Spieluhren. Feinmechaniker arbeiten meist eng mit Steinscheidern und Juwelieren zusammen, wenn sie diese Kunst nicht gar selbst beherrschen."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "e2rEB8x4gSiZ90f6",
"_key": "!items!e2rEB8x4gSiZ90f6",
"type": "Skill",
"name": "Fleischer",
"system": {
"gruppe": "Handwerk",
"probe": [
"KL",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Ein Schlachter, Fleischer oder Metzger weiß, wie man Nutztiere sinnvoll tötet, möglichst viele verwertbare Materialien erhält und daraus haltbare Schinken, Wurstwaren und Bratenstücke herstellt. Er kann beurteilen, wie lang Fleisch noch haltbar ist und welchen Preis es auf dem Markt erzielt. Zudem kennt er sich ähnlich wie der Anatom mit verwundbaren Punkten aus und richtet deswegen im Kampf gegen Wesen, die seinem üblichen Schlachtvieh auch nur grob ähneln, 1 TP mehr an als ein unkundiger Kämpfer, wenn er über einen Fleischer-TaW von 10 oder mehr verfügt (Die Zusatzpunkte von Anatomie und Fleische werden nicht aufaddiert: Auch ein Anatomie-beschlagener Fleischer erhält keine 2 zusätzlichen TP im waffenlosen Kampf.)"
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "0oad2z7EgAiwKrLM",
"_key": "!items!0oad2z7EgAiwKrLM",
"type": "Skill",
"name": "Gerber/Kürschner",
"system": {
"gruppe": "Handwerk",
"probe": [
"KL",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Dieses Talent dient dazu, die Haut oder auch das Fell von Tieren haltbar und dabei ansehnlich zu machen und zu Lederwaren oder Pelzen weiterzuvearbeiten, was oft eine ziemlich unappetitliche Angelegenheit ist, die einen starken Magen und eine Art intuitiver Alchimie erfordert. Gerber und Kürschner sind in der Lage, Pelz- und Lederwaren nach Wert und Qualität zu beurteilen. Eine bisweilen ausgeübte Spezialisierung ist die Präparation von Trophäen aus Tieren aller Art."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "zpXltR1dnEv9h9nY",
"_key": "!items!zpXltR1dnEv9h9nY",
"type": "Skill",
"name": "Grobschmied",
"system": {
"gruppe": "Handwerk",
"probe": [
"FF",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "<b>Schwarzschmiede</b> (die trotz des Namens auch mit Messing und Bronze umzugehen wissen) beschäftigen sich vor allem mit der Herstellung von landwirtschaftlichem Gerät, dem Beschlagen von Reittieren oder der Produktion von Kleineisenteilen wie Nägel, Bolzen, Ketten und Scharniere. Sie sind zudem in der Lage, Waffen zu reparieren.<br/>Ein <b>Waffenschmied</b> ist zwar auch in der Lage, Harken, Türbeschläge oder Truhenbänder herzustellen, seine Spezialität sind jedoch Metallwaffen aller Art, die er sowohl herstellen und reparieren als auch beurteilen kann.<br/>Der <b>Plättner</b> und Harnischmacher hat das Grobschmiedehandwerk bis zum Äußersten verfeinert und stellt nur noch metallene Rüstungen aller Art her; er ist der einzige, der Harnische, Lamellare und auch Kettenhemden herstellen und reparieren kann.<br/><b>Spengler</b> oder Blechwerker schließlich befassen sich mit der Weiterverarbeitung von Metallblechen (vor allem zu Rohren) und deren Einsatz, wozu auch Kenntnisse im Nieten und Löten gehören; sie sind ebenfalls in der Lage, Rüstungen zu reparieren."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "ZMCP3q6ZMcehR30p",
"_key": "!items!ZMCP3q6ZMcehR30p",
"type": "Skill",
"name": "Heilkunde: Gift",
"system": {
"gruppe": "Handwerk",
"probe": [
"MU",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Eine gelungene erste Probe (Erkennungsprobe) auf das Heilen von Vergiftungen bedeutet, dass das Gift richtig identifiziert wurde. (Einige besonders tückische Gifte erfordern einen Zuschlag in Höhe ihrer halben Stufe.) Der Meister benennt die Art des Giftes und teilt mit, wie die Vergiftung zu behandeln ist möglicherweise muss der Heiler zunächst bestimmte Kräuter oder andere Zutaten beschaffen. Sind diese Voraussetzungen erfüllt, kann der Heiler eine zweite Probe (Behandlungsprobe) ablegen, die mit einem Zuschlag in Höhe der gesamten Stufe des Giftes zu belegen ist. Gelingt auch diese Probe, so ist die Wirkung des Gifts gestoppt und der Heiler kann dem Vergifteten Lebenspunkte zurückgeben, die er durch diese Vergiftung verloren hatte: 1 LeP pro übrig behaltenem Talentpunkt bei der zweiten Probe. Eine dritte Probe regelt die Nachbehandlung; auch hier können dem Patienten LeP je nach übrig behaltenen Talentpunkten zurückgegeben werden; der Zuschlag beträgt hierbei wieder die halbe Stufe des Giftes.<br/>Das Scheitern der Erkennungsprobe bedeutet, dass der Heiler das Gift offensichtlich nicht kennt und keine Heilmethode weiß; das Scheitern der Behandlungsprobe bedeutet, dass keine Heilung einsetzt (bei einem Patzer sogar, dass der Vergiftete 1d6 zusätzliche SP erleidet); das Scheitern der Nach behandlungsprobe bedeutet ebenfalls, dass der Patient zusätzlichen Schaden erleidet (1d6 SP).<br/>Zum Ablegen der Erkennungsprobe wer­den mindestens 5 Kampfrunden benöigt, zum Ablegen der Behandlungs- und der Nachbehandlungs-Probe jeweils mindestens 1 SR, die NachbehandlungsProbe kann zudem erst zwölf Stunden nach der Behandlungs-Probe (und nur einmal) abgelegt werden. Bei Misslingen der Proben kann der Heiler in diesem speziellen Fall keinen zweiten Versuch unternehmen."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "MpS719GhrrI2p9BB",
"_key": "!items!MpS719GhrrI2p9BB",
"type": "Skill",
"name": "Heilkunde: Krankheiten",
"system": {
"gruppe": "Handwerk",
"probe": [
"MU",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Alle Arten von Krankheiten - auch das gefährliche Wundfieber können mit Hilfe dieses Talentes behandelt werden. Der Heilkundige legt eine erste Talentprobe ab, die zur Diagnose und zum Bestimmen der benötigten Salben oder Arzneien dient: die Erkennungsprobe. (Eventuell ist diese Probe um die halbe Stufe der Krankheit erschwert, wenn es sich um eine schleichende Krankheit ohne eindeutige Symptome handelt.) Anschließend kann durch eine zweite Probe, die um die Stufe der Krankheit erschwert ist, die Heilung eingeleitet werden, wobei der Heiler dem Patienten so viele Lebenspunkte zurückgibt, wie er Talentpunkte übrig behalten hat (maximal natürlich so viele, wie durch die Krankheit verloren gegangen sind).<br/>Einen Tag später (und dann folgend jeden weiteren Tag) ist eine Nachbehandlungs-Probe möglich, die um die halbe Stufe der Krankheit erschwert ist; die übrig behaltenen TaP kommen dem Patienten als LeP zugute (auch hier natürlich nur, um den Schaden zu reduzieren, der aus einer Krankheit herrührt). Beim Scheitern der ersten Probe kommt es nicht zu einer Heilung; beim Scheitern einer der anschließenden Proben wird die Heilung gestoppt und der Patient erleidet 1d6 zusätzliche Schadenspunkte.<br/>Zum Ablegen der Erkennungs-Probe wird mindestens 1 Spielrunde benötigt, zum Ablegen der Behandlungs-Probe und der Nach-behandlungs-Proben mindestens 6 SR. Die Nachbehandlungs-Proben können erst einen Tag nach der Behandlungs-Probe abgelegt werden (und dann jeweils am nächsten Tag); bei Misslingen dieser Proben kann der Heiler keinen zweiten Versuch unternehmen."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "iVeiTvzkpV6a1HYX",
"_key": "!items!iVeiTvzkpV6a1HYX",
"type": "Skill",
"name": "Heilkunde: Wunden",
"system": {
"gruppe": "Handwerk",
"probe": [
"KL",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Brüche, Schnitte, Stiche und andere Verletzungen, die im Kampf oder durch Unfälle entstehen, sind durch dieses Talent zu kurieren.<br/> <b>Erste Hilfe:</b> Ein lebensbedrohlich Verletzter (0 oder weniger LeP) kann dadurch gerettet werden, dass einem behandelnden Gefährten eine Heilkunde Wunden-Probe gelingt, die um doppelt so viele Punkte erschwert ist, wie die LeP des Patienten unter 0 liegen. Diese Probe dauert doppelt so viele KR, wie die LeP des Patienten unter 0 liegen, abzüglich der übrig behaltenen TaP aus der Heilkunde-Probe. Misslingt die Probe, so erleidet der Patient 1d6 weitere SP; die Probe kann jedoch wiederholt werden, wenn der Verletzte noch lebt. Bei Gelingen der Probe ist die akute Todesgefahr gebannt, der Patient hat nun genau 1 LeP.<br/><b>Heilung fördern:</b> Der Heilkundige legt eine erste Probe ab, die der allgemeinen Versorgung der Wunde dient und das Ausbrechen von Wundfieber verhindert, und anschließend eine zweite Probe, die der Einleitung der Heilung dient. Der Kranke erhält halb so viele LeP zurück, wie der Punktüberschuss aus der zweiten Probe beträgt. Diese LeP gelten zusätzlich zu den LeP, die der Verletzte aus der normalen Heilung (der nächtlichen Regneration) gewinnt, und werden auch nur während dieser Regenerationsphase zurückgewonnen. Am nächsten Tag (und den folgenden) kann jeweils eine Nachbehandlungs-Probe durchgeführt werden, die die allgemeine Regenerationsfähigkeit des Patienten verbessert: Pro 3 übrig behaltener Punkte aus dieser Probe erhält der Patient einen Punkt zu seiner folgenden nächtlichen Regeneration hinzu (bei 5 übrig behaltenen Punkten also 1 zusätzlichen LeP).<br/>Beim Scheitern der ersten Probe kommt es nicht zu einer Heilung, der Patient erkrankt mit einer erhöhten Wahrscheinlichkeit an Wundfieber (KO-Wurf um 3 erschwert). Beim Scheitern der zweiten Heilprobe und der Nachbehandlungs-Proben wird die normale Heilung für 24 Stunden unterbrochen und der Verletzte erleidet 1W6 zusätzliche Schadenspunkte. Die erste Probe dauert 4 Spielrunden, die zweite Probe und die Nachbehandlungsproben dauern jeweils 6 SR. Auch hier gilt insgesamt, dass nur solche LeP-Verluste mit diesem Talent zu heilen sind, die durch Verletzungen verursacht wurden.<br/><b>Wunden</b>: Um die Heilung einer Wunde (sie­ he im Kapitel zum Kampf auf Seite 138) zu beschleunigen, ist zuallererst eine reguläre Probe zur Erstversorgung (s.o.) nötig, die um je 2 Punkte pro erlittener Wunde erschwert ist. (Wenn Sie überhaupt mit Wunden spielen, ist diese Probe generell die Erstversor gungs-Probe, Sie müssen also nicht einmal gegen Schaden und einmal gegen Wunden würfeln.) Die zweite Probe, die der Einleitung der Heilung dient, ist um 3 Punkte pro Wunde des Patienten erschwert.<br/>Misslingt diese Probe, tritt keine Heilung ein; bei einem Patzer erleidet der Patient 1d6 weitere Schadenspunkte. Gelingt sie jedoch, so ist die KO-Probe des Patienten, mit der die Heilung eingeleitet wird, um so viele Punkte erleichtert, wie bei der Heilkunde-Probe übrig behalten wurden."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "2X8vXmUi5DTQAMaC",
"_key": "!items!2X8vXmUi5DTQAMaC",
"type": "Skill",
"name": "Holzbearbeitung",
"system": {
"gruppe": "Handwerk",
"probe": [
"KL",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Dieses Talent hat viele Anwendungsmöglichkeiten im täglichen Heldenleben: Eine morsche Bootsplanke ist auszutauschen, eine improvisierte Waffe oder ein Werkzeug soll hergestellt, auf die Schnelle ein Kinderspielzeug geschnitzt werden. Eine gelungene Probe auf dieses Talent kann die erfolgreiche Herstellung oder Reparatur eines hölzernen Gegenstandes bedeuten. Für die Fertigstellung komplizierter Holzgeräte oder besonders kunstfertiger Schnitzereien sollte die Probe mit einem Zuschlag belegt werden; beim Erstellen hölzerner Skulpturen zählen natürlich die TaP* für den künstlerischen Eindruck."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "hR8i8jzmOR5v4HmR",
"_key": "!items!hR8i8jzmOR5v4HmR",
"type": "Skill",
"name": "Kartographie",
"system": {
"gruppe": "Handwerk",
"probe": [
"KL",
@ -15,3 +19,4 @@
"behinderung": "situationsbedingt",
"talent": "Ein Kartograph befasst sich mit dem Erstellen von Karten und Plänen, sowohl von Gebäuden als auch von Landschaften. Im Spiel bedeutet dieses Talent, dass dem Helden Orientierungs-Proben erleichtert werden, wenn er sich in einer Landschaft oder einem Höhlensystem befindet, das er bereits erkundet und kartiert hat. Er erhält vom Meister Hinweise zum korrekten Aufzeichnen von Höhlensystemen, Gebäuden etc. und kann auch leichter Reiseentfernungen abschätzen. Das Lesen von Landkarten ohne dieses Talent fällt schwer und kann leicht zu Fehlinterpretationen führen."
}
}

View File

@ -1,5 +1,9 @@
{
"_id": "IUW5qqSD1Imgtm5u",
"_key": "!items!IUW5qqSD1Imgtm5u",
"type": "Skill",
"name": "Kochen",
"system": {
"gruppe": "Handwerk",
"probe": [
"KL",
@ -11,3 +15,4 @@
"behinderung": "situationsbedingt",
"talent": "Ein Held, der seine Gäste (daheim oder am Lagerfeuer) durch ein gelungenes Essen beeindrucken will, muss eine erfolgreiche Probe evtl. mit Zuschlägen ablegen. Eine knapp gelungene Probe bedeutet, dass es dem Helden gelungen ist, etwas Essbares zu kochen um etwas Genießbares zu produzieren, sollte er mindestens 3 Talentpunkte übrig behalten; schmackhafte Kost beginnt bei 6 verbleibenden Punkten. Gerade in den Küchen des Hochadels ist diese Kunst nicht nur angesehen, sondern auch oft weiter unterteilt - Spezialisten für süßes Backwerk oder Bratensoßen sind keine Seltenheit."
}
}

Some files were not shown because too many files have changed in this diff Show More