foundry-dsa41-game/src/main.mjs

461 lines
16 KiB
JavaScript

import {PlayerCharacterDataModel} from "./module/data/character.mjs";
import {SkillSheet} from "./module/sheets/skillSheet.mjs";
import {SpellSheet} from "./module/sheets/spellSheet.mjs";
import {SkillDataModel} from "./module/data/skill.mjs";
import {SpellDataModel} from "./module/data/spell.mjs";
import {VornachteileDataModel} from "./module/data/vornachteile.mjs";
import {Character} from "./module/documents/character.mjs";
import CharacterSheet from "./module/sheets/characterSheet.mjs";
import {AdvantageSheet} from "./module/sheets/advantageSheet.mjs";
import {GroupDataModel} from "./module/data/group.mjs";
import {GroupSheet} from "./module/sheets/groupSheet.mjs";
import {EquipmentDataModel} from "./module/data/equipment.mjs";
import {EquipmentSheet} 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";
import {Trefferzone, Wunde, Zonenruestung, Zonenwunde} from "./module/data/Trefferzone.js";
import {ProfessionDataModel} from "./module/data/profession.mjs";
import {SpeciesDataModel} from "./module/data/species.mjs";
import {CultureDataModel} from "./module/data/culture.mjs";
import {CultureSheet} from "./module/sheets/CultureSheet.mjs";
import {SpeciesSheet} from "./module/sheets/SpeciesSheet.mjs";
import {ProfessionSheet} from "./module/sheets/ProfessionSheet.mjs";
import {XmlImportDialog} from "./module/dialog/xmlImportDialog.mjs";
import {MerchantDataModel} from "./module/data/merchant.mjs";
import {MerchantSheet} from "./module/sheets/merchantSheet.mjs";
import {RestingDialog} from "./module/dialog/restingDialog.mjs";
import {BattleDialog} from "./module/dialog/battleDialog.mjs";
async function preloadHandlebarsTemplates() {
return foundry.applications.handlebars.loadTemplates([
// ui partials.
'systems/DSA_4-1/templates/ui/partial-rollable-button.hbs',
'systems/DSA_4-1/templates/ui/partial-rollable-weaponskill-button.hbs',
'systems/DSA_4-1/templates/ui/partial-rollable-language-button.hbs',
'systems/DSA_4-1/templates/ui/partial-attribute-button.hbs',
'systems/DSA_4-1/templates/ui/partial-talent-editable.hbs',
'systems/DSA_4-1/templates/ui/partial-die.hbs',
'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-array-editor.hbs',
'systems/DSA_4-1/templates/dialog/liturgy-dialog.hbs'
]);
}
Hooks.once("init", () => {
game.DSA41 = {
rollItemMacro,
Zonenruestung,
Zonenwunde,
Trefferzone,
Wunde,
RestingDialog,
BattleDialog
}
// Configure custom Document implementations.
CONFIG.Actor.documentClass = Character;
// Configure System Data Models.
CONFIG.Actor.dataModels = {
character: PlayerCharacterDataModel,
group: GroupDataModel,
creature: CreatureDataModel,
Merchant: MerchantDataModel,
};
CONFIG.Item.dataModels = {
Skill: SkillDataModel,
Spell: SpellDataModel,
Advantage: VornachteileDataModel,
Equipment: EquipmentDataModel,
Liturgy: LiturgyDataModel,
Blessing: BlessingDataModel,
SpecialAbility: SpecialAbilityDataModel,
ActiveEffect: ActiveEffectDataModel,
Profession: ProfessionDataModel,
Spezies: SpeciesDataModel,
Kultur: CultureDataModel,
}
CONFIG.Combat.initiative = {
formula: `(@ini.wuerfel)d6 + @ini.aktuell`,
decimals: 0
}
const setMovementSpeeds = () => {
const movementActions = CONFIG.Token.movement.actions
for (const key of ["swim", "climb", "crawl", "walk", "drive", "ride", "fly"]) {
delete movementActions[key]?.getCostFunction
}
movementActions.climb.canSelect = (token) => {
const actor = token.actor | null;
return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Klettern")?.system.taw > 0) || actor.type === "Creature"
}
movementActions.crawl.canSelect = (token) => {
const actor = token.actor | null;
return actor.type === "Character" || actor.type === "Creature"
}
movementActions.walk.canSelect = (token) => {
const actor = token.actor | null;
return actor.type === "Character" || actor.type === "Creature"
}
movementActions.swim = {
label: "TOKEN.MOVEMENT.ACTIONS.swim.label",
icon: "fa-solid fa-swim",
order: 0,
canSelect: (token) => {
const actor = token.actor | null;
return actor.type === "Character" || actor.type === "Creature"
},
deriveTerrainDifficulty: () => 1,
}
movementActions.drive = {
label: "TOKEN.MOVEMENT.ACTIONS.drive.label",
icon: "fa-solid fa-car-side",
order: 0,
canSelect: (token) => {
const actor = token.actor | null;
return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Fahrzeuge lenken")?.system.taw > 0) || actor.type === "Creature"
},
deriveTerrainDifficulty: () => 1,
}
movementActions.ride = {
label: "TOKEN.MOVEMENT.ACTIONS.ride.label",
icon: "fa-solid fa-horse",
order: 0,
canSelect: (token) => {
const actor = token.actor | null;
return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Reiten")?.system.taw > 0) || actor.type === "Creature"
},
deriveTerrainDifficulty: () => 1,
}
movementActions.fly = {
label: "TOKEN.MOVEMENT.ACTIONS.fly.label",
icon: "fa-solid fa-wings",
order: 0,
canSelect: (token) => {
const actor = token.actor | null;
return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Fliegen")?.system.taw > 0) || actor.type === "Creature"
},
deriveTerrainDifficulty: () => 1,
}
}
setMovementSpeeds()
console.log("DSA 4.1 is ready for development!")
foundry.documents.collections.Actors.registerSheet('dsa41.character', CharacterSheet, {
types: ["character"],
makeDefault: true,
label: 'DSA41.CharacterLabels.Item'
})
foundry.documents.collections.Actors.registerSheet('dsa41.creature', CreatureSheet, {
types: ["creature"],
makeDefault: true,
label: 'DSA41.CreatureLabel.Item'
})
foundry.documents.collections.Actors.registerSheet('dsa41.group', GroupSheet, {
types: ["group"],
makeDefault: true,
label: 'DSA41.GroupLabel.Item'
})
foundry.documents.collections.Items.registerSheet('dsa41.skill', SkillSheet, {
types: ["Skill"],
makeDefault: true,
label: 'DSA41.SkillLabels.Item',
});
foundry.documents.collections.Items.registerSheet('dsa41.spell', SpellSheet, {
types: ["Spell"],
makeDefault: true,
label: 'DSA41.SpellLabels.Item',
});
foundry.documents.collections.Items.registerSheet('dsa41.advantage', AdvantageSheet, {
types: ["Advantage"],
makeDefault: true,
label: 'DSA41.VornachteilLabels.Item'
})
foundry.documents.collections.Items.registerSheet('dsa41.equipment', EquipmentSheet, {
types: ["Equipment"],
makeDefault: false,
label: 'DSA41.AusruestungLabels.Item'
})
foundry.documents.collections.Items.registerSheet('dsa41.liturgy', LiturgySheet, {
types: ["Liturgy"],
makeDefault: true,
label: 'DSA41.LiturgyLabels.Item'
})
foundry.documents.collections.Items.registerSheet('dsa41.specialAbility', SpecialAbilitySheet, {
types: ["SpecialAbility"],
makeDefault: true,
label: 'DSA41.SpecialAbilityLabels.Item'
})
foundry.documents.collections.Items.registerSheet('dsa41.activeEffect', ActiveEffectSheet, {
types: ['ActiveEffect'],
makeDefault: true,
label: 'DSA41.ActiveEffectLabels.ActiveEffect'
})
foundry.documents.collections.Items.registerSheet('dsa41.culture', CultureSheet, {
types: ['Culture'],
makeDefault: true,
label: 'DSA41.CultureLabels.Culture'
})
foundry.documents.collections.Items.registerSheet('dsa41.spezien', SpeciesSheet, {
types: ['Species'],
makeDefault: true,
label: 'DSA41.SpeciesLabels.Species'
})
foundry.documents.collections.Items.registerSheet('dsa41.profession', ProfessionSheet, {
types: ['Profession'],
makeDefault: true,
label: 'DSA41.ProfessionLabels.Profession'
})
foundry.documents.collections.Actors.registerSheet('dsa41.merchant', MerchantSheet, {
types: ['Merchant'],
makeDefault: true,
label: 'DSA41.MerchantLabels.MerchantLabel'
})
game.settings.register('DSA_4-1', 'optional_colorfuldice', {
name: "Optional: Farbige Würfel nach Paramanthus",
hint: "Färbt die Würfel je nach Attribut ein",
scope: "client",
config: true,
type: Boolean,
default: false,
onChange: value => {
},
requiresReload: false
})
game.settings.register('DSA_4-1', 'optional_trefferzonen', {
name: "Optional: Trefferzonen",
hint: "Ersetzt das Wundensystem aus dem BRW durch das Trefferzonensystem aus WdH",
scope: "world",
config: true,
type: Boolean,
default: false,
onChange: value => {
},
requiresReload: true
})
game.settings.register('DSA_4-1', 'optional_ruestungzonen', {
name: "Optional: Zonenrüstung",
hint: "Ersetzt das Rüstungssystem aus dem BRW durch das Zonenrüstungssystem aus WdH",
scope: "world",
config: true,
type: Boolean,
default: false,
onChange: value => {
},
requiresReload: true
})
game.settings.register('DSA_4-1', 'optional_ausdauer', {
name: "Optional: Ausdauerregeln",
hint: "Aktiviert Regeln für das Spiel mit Ausdauer",
scope: "world",
config: true,
type: Boolean,
default: false,
onChange: value => {
},
requiresReload: true
})
game.settings.register('DSA_4-1', 'optional_distanzklassen', {
name: "Optional: Distanzklassen",
hint: "Aktiviert Regeln für das Spiel mit Distanzklassen",
scope: "world",
config: true,
type: Boolean,
default: false,
onChange: value => {
},
requiresReload: true
})
game.settings.register('DSA_4-1', 'optional_aufstufen_von_liturgien', {
name: "Optional: Aufstufen von Liturgien",
hint: "Aktiviert die Regeln zum Aufstufen von Liturgien",
scope: "world",
config: true,
type: Boolean,
default: false,
disabled: true,
requiresReload: true
})
Handlebars.registerHelper("weight", (data) => {
const baseValue = data * 1000 // to get to gramms (1/1000 Stone)
const stone = Math.floor(baseValue / 1000)
const remainder = baseValue - (stone * 1000)
const ounces = remainder / 25
let stoneRepresentation = ''
let ouncesRepresentation = ''
if (stone > 0) {
stoneRepresentation = `<span class="stone">${stone}</span>`
}
if (ounces > 0) {
ouncesRepresentation = `<span class="ounces">${ounces}</span>`
}
return new Handlebars.SafeString(`<span class="weight">${stoneRepresentation}${ouncesRepresentation}</span>`)
})
Handlebars.registerHelper("fieldTooltip", (...args) => {
const [fieldName, actorId] = args
const actor = game.actors.find(p => p._id === actorId)
let tooltip = ""
if (actor) {
Object.entries(actor.getModificationsOn(fieldName)).forEach(([key, value]) => {
tooltip += `${key}: ${value}<br/>`
})
} else {
tooltip = `${fieldName} not found`
}
return new Handlebars.SafeString(tooltip)
})
Handlebars.registerHelper("currency", (data) => {
// schema for Mittelreich: 1 Ducat = 10 Silver = 100 Kreutzer = 1000 Heller
// internally the price is always given in Silver
// so we need to inflate the value of price by 100 to be able to divide beginning from Heller
const baseValue = data * 100
// then we can regex over it
const currencyRegexp = /(.*)(.)(.)(.)/g
const withDucats = currencyRegexp.exec(baseValue)
let _ = undefined
let ducats = 0
let silver = 0
let kreutzer = 0
let heller = 0
if (withDucats) {
[_, ducats, silver, kreutzer, heller] = withDucats
} else {
const currencyRegexp = /(.)(.)(.)/g
const withSilver = currencyRegexp.exec(baseValue)
if (withSilver) {
[_, silver, kreutzer, heller] = withSilver
} else {
const currencyRegexp = /(.)(.)/g
const withKreutzer = currencyRegexp.exec(baseValue)
if (withKreutzer) {
[_, kreutzer, heller] = withKreutzer
} else {
heller = baseValue
}
}
}
let str = `<span class='coins' data-tooltip="${ducats > 0 ? ducats + ' Dukaten ' : ''}${silver > 0 ? silver + ' Silbertaler ' : ''}${kreutzer > 0 ? kreutzer + ' Kreuzer ' : ''}${heller > 0 ? heller + ' Heller' : ''}">`
if (ducats > 0) {
str += ducats + "<i class='symbol ducat'></i>"
}
if (silver > 0) {
str += silver + "<i class='symbol silver'></i>"
}
if (kreutzer > 0) {
str += kreutzer + "<i class='symbol kreutzer'></i>"
}
if (heller > 0) {
str += heller + "<i class='symbol heller'></i>"
}
str = str + "</span>"
return new Handlebars.SafeString(str)
})
return preloadHandlebarsTemplates();
})
Hooks.once("ready", async function () {
// Wait to register hotbar drop hook on ready so that modules could register earlier if they want to
Hooks.on("hotbarDrop", (bar, data, slot) => {
return createTalentMacro(data, slot)
});
});
Hooks.on("getActorContextOptions", (application, menuItems) => {
menuItems.push({
name: "Import from XML",
icon: '<i class="fas fa-file"></i>',
callback: (li) => {
const actorId = li.getAttribute("data-entry-id")
const actor = game.actors.get(actorId)
//actor.import()
new XmlImportDialog(actor).render(true)
}
})
})
async function createTalentMacro(data, slot) {
if (data.type !== "Item") return;
const uuid = foundry.utils.parseUuid(data.uuid)
const itemId = uuid.id;
const actorId = uuid.primaryId;
const item = await game.actors.get(actorId).items.get(itemId);
// Create the macro command
const command = `game.DSA41.rollItemMacro("${data.uuid}");`;
const macro = await Macro.create({
name: item.name,
type: "script",
img: item.img,
command: command,
flags: {"dsa41.skillMacro": true}
});
game.user.assignHotbarMacro(macro, slot);
return false;
}
function rollItemMacro(_uuid) {
const speaker = ChatMessage.getSpeaker();
const uuid = foundry.utils.parseUuid(_uuid)
const itemId = uuid.id;
const actorId = uuid.primaryId;
let actor = game.actors.get(actorId);
const item = actor ? actor.items.get(itemId) : null;
if (!item) return ui.notifications.warn(`Your controlled Actor does not have an item with id ${itemId}`);
return item.system.roll();
}