adds GRW races to compendia

pull/61/head
macniel 2025-10-24 13:31:19 +02:00
parent ae57e2f90e
commit 408ef3ddc0
28 changed files with 453 additions and 48 deletions

View File

@ -45,7 +45,7 @@ const convert = function (from, to, ofType, overwrite = true) {
} }
const filewalker = (source) => { const filewalker = (source) => {
console.log("entering directory", source); console.debug("entering directory", source);
readdirSync(source).forEach(file => { readdirSync(source).forEach(file => {
if (statSync(join(source, file)).isDirectory()) { if (statSync(join(source, file)).isDirectory()) {
filewalker(join(source, file)); filewalker(join(source, file));

View File

@ -8,7 +8,7 @@ const DEST = "/home/macniel/.local/share/FoundryVTT/Data/systems/DSA_4-1"
if (existsSync(SRC)) { if (existsSync(SRC)) {
cpSync(SRC, resolve(DEST), {recursive: true}) cpSync(SRC, resolve(DEST), {recursive: true})
console.log(`copied ${SRC} to ${DEST}`) console.debug(`copied ${SRC} to ${DEST}`)
} else { } else {
console.error(`Source ${SRC} does not exists. Forgot to compile?`) console.error(`Source ${SRC} does not exists. Forgot to compile?`)
} }

View File

@ -234,7 +234,6 @@ Hooks.on("getActorContextOptions", (application, menuItems) => {
}) })
async function createTalentMacro(data, slot) { async function createTalentMacro(data, slot) {
console.log(data, slot)
if (data.type !== "Item") return; if (data.type !== "Item") return;
const uuid = foundry.utils.parseUuid(data.uuid) const uuid = foundry.utils.parseUuid(data.uuid)

View File

@ -36,7 +36,6 @@ export class ActiveEffectDataModel extends BaseItem {
_onCreate(data, options, userId) { _onCreate(data, options, userId) {
super._onCreate(data, options, userId); super._onCreate(data, options, userId);
console.log(data);
if (this.parent.getEmbeddedCollection("ActiveEffect").contents.length === 0) { if (this.parent.getEmbeddedCollection("ActiveEffect").contents.length === 0) {
this.parent.createEmbeddedDocuments("ActiveEffect", [{ this.parent.createEmbeddedDocuments("ActiveEffect", [{
@ -45,7 +44,7 @@ export class ActiveEffectDataModel extends BaseItem {
duration: {}, duration: {},
icon: this.img, icon: this.img,
}]); }]);
console.log("added default activeffect");
} }
} }

View File

@ -2,7 +2,6 @@ export default class BaseItem extends foundry.abstract.TypeDataModel {
/** @inheritDoc */ /** @inheritDoc */
async importFromCompendium(pack, id, updateData = {}, options = {}) { async importFromCompendium(pack, id, updateData = {}, options = {}) {
console.log(`called ${pack} ${id} `);
const created = await super.importFromCompendium(pack, id, updateData, options); const created = await super.importFromCompendium(pack, id, updateData, options);
const item = await pack.getDocument(id); const item = await pack.getDocument(id);

View File

@ -107,7 +107,6 @@ export class LiturgyData {
static lookupAlias(alias) { static lookupAlias(alias) {
return LiturgyData.#aliases.find((entry) => { return LiturgyData.#aliases.find((entry) => {
console.log(alias, entry.aliases.indexOf(alias) !== -1)
return entry.aliases.indexOf(alias) !== -1 return entry.aliases.indexOf(alias) !== -1
})?.originalName ?? alias; // cant determine thus simply return the original query name })?.originalName ?? alias; // cant determine thus simply return the original query name
} }

View File

@ -101,7 +101,7 @@ export class SpecialAbilityDataModel extends BaseItem {
targetField = flatActor?.[requirement.attribute.toLocaleLowerCase()] targetField = flatActor?.[requirement.attribute.toLocaleLowerCase()]
} }
if (requirement.talent) { if (requirement.talent) {
targetField = this.parent.actor.itemTypes["Skill"].find(p => p.name.toLocaleLowerCase() === requirement.talent.toLocaleLowerCase()).taw targetField = this.parent.actor.itemTypes["Skill"].find(p => p.name.toLocaleLowerCase() === requirement.talent.toLocaleLowerCase())?.taw ?? Number.NaN
} }
if (requirement.minValue) { if (requirement.minValue) {
passes = requirement.minValue <= targetField passes = requirement.minValue <= targetField

View File

@ -1,6 +1,6 @@
import BaseItem from "./base-item.mjs"; import BaseItem from "./base-item.mjs";
const {BooleanField, NumberField, StringField, HTMLField} = foundry.data.fields; const {BooleanField, ArrayField, SchemaField, NumberField, StringField, HTMLField} = foundry.data.fields;
export class SpeciesDataModel extends BaseItem { export class SpeciesDataModel extends BaseItem {
@ -8,6 +8,29 @@ export class SpeciesDataModel extends BaseItem {
return { return {
description: new HTMLField(), description: new HTMLField(),
baseSpeed: new NumberField({required: true, initial: 6, integer: true}), baseSpeed: new NumberField({required: true, initial: 6, integer: true}),
modAttributes: new SchemaField({
mu: new NumberField({required: false, initial: 0, integer: true}),
kl: new NumberField({required: false, initial: 0, integer: true}),
in: new NumberField({required: false, initial: 0, integer: true}),
ch: new NumberField({required: false, initial: 0, integer: true}),
ff: new NumberField({required: false, initial: 0, integer: true}),
ge: new NumberField({required: false, initial: 0, integer: true}),
ko: new NumberField({required: false, initial: 0, integer: true}),
kk: new NumberField({required: false, initial: 0, integer: true}),
}),
mod: new SchemaField({
le: new NumberField({required: true, initial: 10, integer: true}),
au: new NumberField({required: true, initial: 10, integer: true}),
mr: new NumberField({required: true, initial: -4, integer: true}),
}),
defaultAdvantages: new ArrayField(new StringField()),
recommendedAdvantages: new ArrayField(new StringField()),
discouragedAdvantages: new ArrayField(new StringField()),
availableCultures: new ArrayField(new StringField()),
talentModifications: new ArrayField(new SchemaField({
name: new StringField(),
value: new NumberField(),
})),
feminineDemonym: new StringField(), feminineDemonym: new StringField(),
masculineDemonym: new StringField(), masculineDemonym: new StringField(),
} }

View File

@ -15,7 +15,6 @@ export class ModifyLiturgy {
ModifyLiturgy.data = data; ModifyLiturgy.data = data;
ModifyLiturgy.data.maxmods = Math.round(data.lkp / 3); ModifyLiturgy.data.maxmods = Math.round(data.lkp / 3);
ModifyLiturgy.data.variation = null; ModifyLiturgy.data.variation = null;
console.log("ModifyLiturgy constructed", data)
} }
static renderMods(html) { static renderMods(html) {

View File

@ -46,7 +46,7 @@ export class XmlImportDialog extends HandlebarsApplicationMixin(ApplicationV2) {
static async #onSubmitForm(event, form, formData) { static async #onSubmitForm(event, form, formData) {
event.preventDefault() event.preventDefault()
console.log("lets go", formData.object) console.debug("Importing XML with following options", formData.object)
const options = {} const options = {}

View File

@ -181,9 +181,24 @@ export class Character extends Actor {
} }
}, 0) }, 0)
// evaluate SFs
this.itemTypes["SpecialAbility"].forEach(sf => {
if (sf.system.isActive()) {
sf.system.getActiveMod()?.forEach(mod => {
// we don't apply to .mod as that field is only applicable for temporary modifier from ActiveEffects, Spells, Liturgies, Combat Effects
if (systemData[mod.name] && systemData[mod.name].aktuell) { // apply to aktuell
systemData[mod.name].aktuell = systemData[mod.name].aktuell + mod.value
} else if (systemData[mod.name]) { // apply directly
systemData[mod.name] = systemData[mod.name] + mod.value
}
})
}
})
// TODO the same for Advantages/Disadvantages
} }
} }
/** /**

View File

@ -11,8 +11,6 @@ export class Creature extends Actor {
getRollData() { getRollData() {
const data = super.getRollData(); const data = super.getRollData();
console.log(this.type)
if (this.type !== 'character' && this.type !== 'creature') return; if (this.type !== 'character' && this.type !== 'creature') return;
// Copy the ability scores to the top level, so that rolls can use // Copy the ability scores to the top level, so that rolls can use

View File

@ -5,7 +5,6 @@ export class Group extends Actor {
*/ */
prepareData() { prepareData() {
console.log("prepare", this);
super.prepareData(); super.prepareData();
} }

View File

@ -6,14 +6,4 @@ export class SpecialAbility extends Item {
super.prepareData(); super.prepareData();
} }
getRequirements() {
let requirements = []
if (this.system.value && this.system.auswahl.find(p => p.name === this.system.value)) {
requirements = this.system.auswahl[this.system.value].requirement
} else {
requirements = this.system.requirement
return requirements
}
}
} }

View File

@ -1,4 +1,4 @@
export class Species extends Item { export class SpbaseSpeedecies extends Item {
/** /**
* Augment the basic Item data model with additional dynamic data. * Augment the basic Item data model with additional dynamic data.
*/ */

View File

@ -46,7 +46,7 @@ export class SpeciesSheet extends HandlebarsApplicationMixin(DocumentSheetV2) {
context.name = context.document.name context.name = context.document.name
context.img = context.document.img context.img = context.document.img
context.description = context.document.system.description context.description = context.document.system.description
context.baseSpeed = context.document.baseSpeed context.baseSpeed = context.document.system.baseSpeed
context.masculineDemonym = context.document.system.masculineDemonym context.masculineDemonym = context.document.system.masculineDemonym
context.feminineDemonym = context.document.system.feminineDemonym context.feminineDemonym = context.document.system.feminineDemonym

View File

@ -173,7 +173,6 @@ export default {
condition: (target) => { condition: (target) => {
const {itemId} = target.dataset const {itemId} = target.dataset
const item = thisObject.document.items.get(itemId) const item = thisObject.document.items.get(itemId)
console.log(item.system.category)
return !thisObject.document.isWorn(itemId) && item.system.category.indexOf("Munition") != -1 return !thisObject.document.isWorn(itemId) && item.system.category.indexOf("Munition") != -1
} }
}, },
@ -182,14 +181,8 @@ export default {
callback: (target) => { callback: (target) => {
const updateObject = thisObject.document.getEquipmentSetUpdateObject() const updateObject = thisObject.document.getEquipmentSetUpdateObject()
// find next unoccupied slot and enter the item // find next unoccupied slot and enter the item
console.log(updateObject)
const nextUnoccupiedSlot = Object.entries(updateObject).find(([key, value]) => { const nextUnoccupiedSlot = Object.entries(updateObject).find(([key, value]) => {
// but not when it is a weapon slot // but not when it is a weapon slot
console.log(key, value === null
&& key.indexOf(".links") === -1
&& key.indexOf(".rechts") === -1
&& key.indexOf(".fernkampf") === -1
&& key.indexOf(".munition") === -1)
return value === null return value === null
&& key.indexOf(".links") === -1 && key.indexOf(".links") === -1
&& key.indexOf(".rechts") === -1 && key.indexOf(".rechts") === -1

View File

@ -161,7 +161,6 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
if (cooldown && cooldown.current <= 0) { if (cooldown && cooldown.current <= 0) {
const am = new ActionManager(this.document) const am = new ActionManager(this.document)
const action = am.evaluate().find(action => action.name === cooldown.data.maneuver.id) const action = am.evaluate().find(action => action.name === cooldown.data.maneuver.id)
console.log(action)
if (action) { if (action) {
action.activate(cooldowns, {...cooldown.data, actor: this.document}) action.activate(cooldowns, {...cooldown.data, actor: this.document})

View File

@ -275,7 +275,6 @@ export class EquipmentSheet extends HandlebarsApplicationMixin(DocumentSheetV2)
} }
_canDragDrop(event) { _canDragDrop(event) {
console.log(event)
return true return true
} }
@ -287,11 +286,7 @@ export class EquipmentSheet extends HandlebarsApplicationMixin(DocumentSheetV2)
if (documentClass) { if (documentClass) {
const document = await documentClass.fromDropData(data) const document = await documentClass.fromDropData(data)
console.log(document, document.parent)
// Dropped Documents // Dropped Documents
document.update({"parent": this.document}) document.update({"parent": this.document})
} }
} }

View File

@ -94,7 +94,6 @@ export class GroupSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
} }
static async #dieRoll(evt) { static async #dieRoll(evt) {
console.log(evt)
} }
async #onUpdateCharacterSettings(data) { async #onUpdateCharacterSettings(data) {
@ -161,7 +160,6 @@ export class GroupSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
// TODO hook on changes in the given folder // TODO hook on changes in the given folder
const characters = await game.folders.get(groupData.system.groupId).contents const characters = await game.folders.get(groupData.system.groupId).contents
console.log(characters)
for (const character of characters) { for (const character of characters) {

View File

@ -353,7 +353,6 @@ export class XmlImport {
if (combatStatistic) { // melee with AT/PA values if (combatStatistic) { // melee with AT/PA values
let at = combatStatistic.at - atbasis ?? 0 let at = combatStatistic.at - atbasis ?? 0
let pa = combatStatistic.pa - pabasis ?? 0 let pa = combatStatistic.pa - pabasis ?? 0
console.log({system: {taw, at, pa}})
embeddedDocument.update({system: {taw, at, pa}}); embeddedDocument.update({system: {taw, at, pa}});
} else { // ranged with only AT values which is equal to taw } else { // ranged with only AT values which is equal to taw
embeddedDocument.update({system: {taw: taw, at: taw, pa: null}}); // at is already at raw taw and wasn't influenced by helden-software precalculations embeddedDocument.update({system: {taw: taw, at: taw, pa: null}}); // at is already at raw taw and wasn't influenced by helden-software precalculations
@ -570,7 +569,7 @@ export class XmlImport {
if (sfId) { if (sfId) {
if (variantName) { if (variantName) {
console.log(baseAbility + " to be imported as " + variantName) // TODO if baseAbility already imported, adjust its level instead of adding a new embeddedDocument console.debug(baseAbility + " to be imported as " + variantName) // TODO if baseAbility already imported, adjust its level instead of adding a new embeddedDocument
} }
compendiumOfSF.getDocument(sfId._id).then(sf => { compendiumOfSF.getDocument(sfId._id).then(sf => {
actor.createEmbeddedDocuments('Item', [sf]).then(_ => { actor.createEmbeddedDocuments('Item', [sf]).then(_ => {

View File

@ -7,7 +7,7 @@
"minValue": "12" "minValue": "12"
}, },
{ {
"talent": "Sinnesschärfe", "talent": "Sinnenschärfe",
"minValue": "15" "minValue": "15"
}, },
{ {

View File

@ -0,0 +1,108 @@
{
"name": "helden.model.rasse.Auelf",
"masculineDemonym": "Elf",
"feminineDemonym": "Elfin",
"description": "Die Herkunft der Elfen (die sich selbst feya nennen, Einzahl fey) ist ein Mysterium, wahrscheinlich ist ihr Ursprung in der so genannten Lichtwelt außerhalb des bekannten Kosmos zu suchen. Als ihre aventurische Heimat gelten die verwunschenen Wälder der Salamandersteine. Heute gibt es folgende bedeutende Völker: Waldelfen (Salamandersteine), Auelfen (an Seen und Flussläufen in Nord- und Mittelaventurien, selten in den Städten der Menschen), Firnelfen (Grimmfrostöde, Eiszinnen, Küsten von Brecheis- und Bernsteinbucht, Nebelzinnen und Yetiland) und Steppenelfen (nördliche Steppen, sehr stark in der Grünen Ebene).",
"baseSpeed": 8,
"modAttributes": {
"kk": -1,
"ge": 2,
"kl": -1,
"in": 1
},
"mods": {
"le": 6,
"au": 12,
"ae": 12,
"mr": -2
},
"defaultAdvantages": [
{
"name": "Altersresistenz"
},
{
"name": "Dämmerungssicht"
},
{
"name": "Gut Aussehend"
},
{
"name": "Herausragender Sinn"
},
{
"name": "Resistenz gegen Krankheiten"
},
{
"name": "Vollzauberer"
},
{
"name": "Wohlklang"
},
{
"name": "Zweistimmiger Gesang"
},
{
"name": "Sensibler Geruchssinn",
"value": 6
},
{
"name": "Unfähigkeit [Talent]",
"value": "Zechen"
}
],
"recommendedAdvantages": [
"Balance",
"Feenfreund",
"Flink",
"Herausragende Balance",
"Herausragendes Aussehen",
"Nachtsicht",
"Richtungssinn",
"Schlangenmensch",
"Nahrungsrestriktion",
"Raumangst"
],
"discouragedAdvantages": [
"Hitzeresistenz",
"Kampfrausch",
"Schwer zu verzaubern",
"Zwergennase",
"Blutrausch",
"Eingeschränkter Sinn",
"Farbenblind",
"Fettleibig",
"Krankheitsanfällig",
"Lichtscheu",
"Nachtblind",
"Unangenehme Stimme"
],
"availableCultures": [
"Auelf"
],
"talentModifications": [
{
"name": "Körperbeherrschung",
"value": 3
},
{
"name": "Schleichen",
"value": 2
},
{
"name": "Singen",
"value": 2
},
{
"name": "Sinnenschärfe",
"value": 5
},
{
"name": "Tanzen",
"value": 1
},
{
"name": "Zechen",
"value": -2
}
]
}

View File

@ -0,0 +1,83 @@
{
"name": "helden.model.rasse.Halbelf",
"masculineDemonym": "Halbelf",
"feminineDemonym": "Halbelfin",
"description": "Der Halbelf (von den Elfen Menschelf genannt) ist ein elfischer Mischling mit deutlichem Anteil menschlichen Blutes. Entgegen vielfachem Aberglauben ist es gleich, ob Vater oder Mutter von elfischem Blut sind: Aus einer entsprechenden Verbindung der beiden Rassen entsteht wegen nder Seltenheit einer solchen Liaison jedoch nicht häufig ein Halbelf.<br/>Je nachdem, in welcher Familie er groß geworden ist, wird sich dieser dem einen oder anderen Erbe mehr verpflichtet fühlen. Allerdings wird er sich nie ganz wie ein Mensch fühlen können, denn es gibt immer vorurteilsbehaftete Mitmenschen, die ihn sehr genau spüren lassen, dass er eigentlich nicht dazugehört. Und noch weniger wird er unter Elfen heimisch sein, denn auch sie werden nie vergessen, dass er anders ist. Zudem fehlt vielen Halbelfen die Fähigkeit des zweistimmigen Gesangs, so dass ihnen die Teilnahme anden Elfenliedern des salasandra ebenso verwehrt ist wie die Magie der elfischen Zauberlieder. Auch können sie Asdharia, die alte Sprache der Elfen, zwar vielleicht verstehen, aber niemals sprechen zumindest nicht so, dass ihre elfischen Verwandten es als schön empfinden könnten.",
"baseSpeed": 8,
"modAttributes": {
"kk": -1,
"ge": 1
},
"mods": {
"le": 8,
"au": 10,
"ae": 6,
"mr": -4
},
"defaultAdvantages": [
{
"name": "Gut Aussehend"
},
{
"name": "Viertelzauberer"
}
],
"recommendedAdvantages": [
"Balance",
"Dämmerungssicht",
"Feenfreund",
"Flink",
"Herausragender Sinn",
"Herausragendes Aussehen",
"Magiegespür",
"Schlangenmensch",
"Wohlklang",
"Zweistimmiger Gesang",
"Nahrungsrestriktion",
"Sensibler Geruchssinn"
],
"discouragedAdvantages": [
"Kampfrausch",
"Zwergennase",
"Blutrausch",
"Fettleibig",
"Krankheitsanfällig",
"Lichtscheu",
"Nachtblind",
"Schwer zu verzaubern",
"Unangenehme Stimme"
],
"availableCultures": [
"Auelf",
"Andergast/Nostria",
"Bornland",
"Mittelländische Städte",
"Horasreich"
],
"talentModifications": [
{
"name": "Körperbeherrschung",
"value": 2
},
{
"name": "Schleichen",
"value": 1
},
{
"name": "Singen",
"value": 1
},
{
"name": "Sinnenschärfe",
"value": 2
},
{
"name": "Tanzen",
"value": 1
},
{
"name": "Zechen",
"value": -1
}
]
}

View File

@ -2,6 +2,26 @@
"name": "helden.model.rasse.Mittellaender", "name": "helden.model.rasse.Mittellaender",
"masculineDemonym": "Mittelländer", "masculineDemonym": "Mittelländer",
"feminineDemonym": "Mittelländerin", "feminineDemonym": "Mittelländerin",
"description": "", "description": "Die hellhäutigen Mittelländer dominieren heutzutage Aventurien, stellen sie doch mehr als die Hälfte der Bewohner des Kontinents. Daraus ergibt sich eine kulturelle Vielfalt, die keine andere Rasse zu bieten hat; zudem kann fast jede erdenkliche Profession gewählt werden. Daher hat der Spieler bei der Gestaltung seines Mittelländers größtmögliche Freiheit.",
"baseSpeed": 6 "baseSpeed": 8,
"modAttributes": {},
"mods": {
"le": 10,
"au": 10,
"mr": -4
},
"defaultAdvantages": [],
"recommendedAdvantages": [],
"discouragedAdvantages": [
"Herausragende Balance",
"Nahrungsrestriktion"
],
"availableCultures": [
"Mittelländische Städte",
"Andergast/Nostria",
"Bornland",
"Horasreich",
"Südaventurien"
],
"talentModifications": []
} }

View File

@ -0,0 +1,61 @@
{
"name": "helden.model.rasse.Thorwaler",
"masculineDemonym": "Thorwaler",
"feminineDemonym": "Thorwalerin",
"description": "Große, stämmige, muskelbepackte Männer und Frauen mit wilden, blonden oder roten Mähnen das sind die Thorwaler, wie jedes Kind in Aventurien sie kennt. Sie sind geradlinig und haben die feine Diplomatie nicht eben erfunden, was sie im Rollenspiel zu idealen Einsteigercharakteren macht.",
"baseSpeed": 8,
"modAttributes": {
"mu": 1,
"ko": 1,
"kk": 1
},
"mods": {
"le": 11,
"au": 10,
"mr": -5
},
"defaultAdvantages": [
{
"name": "Jähzorn",
"value": 6
}
],
"recommendedAdvantages": [
"Ausdauernd",
"Eisern",
"Hohe Lebenskraft",
"Kampfrausch",
"Richtungssinn",
"Zäher Hund",
"Blutrausch"
],
"discouragedAdvantages": [
"Herausragende Balance",
"Glasknochen",
"Nahrungsrestriktion"
],
"availableCultures": [
"Thorwal",
"Mittelländische Städte",
"Andergast/Nostria",
"Südaventurien"
],
"talentModifications": [
{
"name": "Atheltik",
"value": 1
},
{
"name": "Sinnenschärfe",
"value": 1
},
{
"name": "Zechen",
"value": 1
},
{
"name": "Wettervorhersage",
"value": 1
}
]
}

View File

@ -0,0 +1,25 @@
{
"name": "helden.model.rasse.Tulamide",
"masculineDemonym": "Tulamide",
"feminineDemonym": "Tulamidin",
"description": "Eine vielschichtige Rasse mit mehreren verschiedenen, komplexen Kulturen sind die Tulamiden Südost-Aventuriens: Als eines der ältesten Menschenvölker hatten sie schon eine Hochkultur, ehe die ersten güldenländischen Siedler eintrafen. Ihnen wird nachgesagt, sie verbänden große Lebensfreude mit einem ebenso großen Interesse an Reichtum, aber auch an der Welt des Spirituellen und der Mysterien.",
"baseSpeed": 8,
"modAttributes": {},
"mods": {
"le": 10,
"au": 10,
"mr": -4
},
"defaultAdvantages": [],
"recommendedAdvantages": [],
"discouragedAdvantages": [
"Herausragende Balance",
"Nahrungsrestriktion"
],
"availableCultures": [
"Tulamidische Stadtstaaten",
"Novadi",
"Südaventurien"
],
"talentModifications": []
}

View File

@ -0,0 +1,104 @@
{
"name": "helden.model.rasse.Zwerg",
"masculineDemonym": "Zwerg",
"feminineDemonym": "Zwergin",
"description": "Die Zwerge, die sich selbst Angroschim nennen (Einzahl Angroscho, weibliche Form Angroschna und Mehrzahl Angroschax), wurden angeblich von Ingerimm geschaffen, um die Schätze der Welt gegen die habgierigen Drachen zu verteidigen. Sie haben großartige Handwerker und gefürchtete Krieger hervorgebracht ganz zu schweigen von ihrem vorzüglichen Bier.<br/>Zwerge gelten als geradlinig und stur und können sich angeblich stets daran erinnern, wem sie vor zwei Jahrzehnten einmal drei Heller geliehen haben.",
"baseSpeed": 8,
"modAttributes": {
"ff": 1,
"ge": -1,
"ko": 2,
"kk": 2
},
"mods": {
"le": 12,
"au": 18,
"mr": -4
},
"defaultAdvantages": [
{
"name": "Dämmerungssicht",
"value": 6
},
{
"name": "Resistenz gegen mineralische Gifte"
},
{
"name": "Resistenz gegen Krankheiten"
},
{
"name": "Schwer zu verzaubern"
},
{
"name": "Goldgier"
}
],
"recommendedAdvantages": [
"Ausdauernd",
"Eisern",
"gutes Gedächtnis",
"Hitzeresistenz",
"Hohe Lebenskraft",
"Hohe Magieresistenz",
"Kampfrausch",
"Richtungssinn",
"Zäher Hund",
"Zwergennase",
"Jähzorn",
"Lichtscheu",
"Platzangst",
"Unansehnlich"
],
"discouragedAdvantages": [
"Feenfreund",
"Flink",
"Herausragende Balance",
"Herausragendes Aussehen",
"Koboldfreund",
"Magiefreund",
"Schlangenmensch",
"Wohlklang",
"Dunkelangst",
"Glasknochen",
"Krankheitsanfällig",
"Nachtblind",
"Raumangst"
],
"availableCultures": [
"Ambosszwerg"
],
"talentModifications": [
{
"name": "Ringen",
"value": 1
},
{
"name": "Akrobatik",
"value": -3
},
{
"name": "Reiten",
"value": -1
},
{
"name": "Schwimmen",
"value": -3
},
{
"name": "Selbstbeherrschung",
"value": 2
},
{
"name": "Zechen",
"value": 1
},
{
"name": "Orientierung",
"value": 1
},
{
"name": "Gesteinskunde",
"value": 1
}
]
}