diff --git a/src/lang/de.json b/src/lang/de.json index a503b8d3..2561331e 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -1,22 +1,49 @@ { - "TYPES": { - "Actor": { - "Character": "Held", - "Creature": "Kreatur", - "Group": "Heldengruppe", - "Merchant": "Händler" - }, - "Item": { - "ActiveEffect": "Aktiver Effekt", - "Equipment": "Ausrüstungsgegenstand", - "Skill": "Talent", - "Advantage": "Vor-/Nachteil", - "SpecialAbility": "Sonderfertigkeit", - "Spell": "Zauber", - "Liturgy": "Liturgie", - "Species": "Spezies", - "Culture": "Kultur", - "Profession": "Profession" - } + "TYPES": { + "Actor": { + "Character": "Held", + "Creature": "Kreatur", + "Group": "Heldengruppe", + "Merchant": "Händler" + }, + "Item": { + "ActiveEffect": "Aktiver Effekt", + "Equipment": "Ausrüstungsgegenstand", + "Skill": "Talent", + "Advantage": "Vor-/Nachteil", + "SpecialAbility": "Sonderfertigkeit", + "Spell": "Zauber", + "Liturgy": "Liturgie", + "Species": "Spezies", + "Culture": "Kultur", + "Profession": "Profession" } + }, + "COOLDOWN": { + "progress": "{t} weiter durchführen", + "cancel": "{t} abbrechen", + "activate": "{t} auslösen" + }, + "WEAPON": { + "attack": "Mit {weapon} angreifen", + "parry": "Mit {weapon} parrieren", + "damage": "Mit {weapon} schaden machen", + "initiative": "Initiative würfeln" + }, + "COMBAT_DIALOG": { + "notReadyReason": { + "title": "Angriff kann aus folgenden Gründen nicht ausgeführt werden:", + "noTarget": "Kein Ziel ausgewählt", + "noWeapon": "Keine Waffe ausgewählt", + "noSkill": "Kein Waffentalent ausgewählt", + "noManeuver": "Kein Manöver ausgewählt", + "impossible": "Erschwernis zu hoch für Talentwert" + } + }, + "COMBAT_DIALOG_TP": { + "windowTitle": "Schaden Würfeln", + "regularFormula": "Schadensformel:", + "bonusDamage": "Zusätzlicher Schaden:", + "buttonText": "Würfeln" + } } diff --git a/src/module/dialog/combatAction.mjs b/src/module/dialog/combatAction.mjs index 87d9c7f4..c89f0165 100644 --- a/src/module/dialog/combatAction.mjs +++ b/src/module/dialog/combatAction.mjs @@ -50,12 +50,12 @@ export class CombatActionDialog extends HandlebarsApplicationMixin(ApplicationV2 */ _actor = null - constructor(actor) { + constructor(actor, data) { super(); this._actor = actor this._targetId = null - this._skillId = null - this._weaponId = null + this._skillId = data.skill ? data.skill : null + this._weaponId = data.weapon ? data.weapon : null this._defenseManeuverId = null this._actionManager = new ActionManager(this._actor) CombatActionDialog._instance = this @@ -288,9 +288,9 @@ export class CombatActionDialog extends HandlebarsApplicationMixin(ApplicationV2 const context = await super._prepareContext(options) context.actor = this._actor - context.distanceUnit = game.scenes.current.grid.units + context.distanceUnit = game.scenes.current?.grid.units - if (this._actor.getActiveTokens()[0]?.id) { + if (context.distanceUnit && this._actor.getActiveTokens()[0]?.id) { context.tokenDistances = this.#evaluateDistances() context.weapons = this.#evaluateWeapons() @@ -307,7 +307,28 @@ export class CombatActionDialog extends HandlebarsApplicationMixin(ApplicationV2 // TODO get W/M of weapon NOW - context.ready = this._targetId && this._weaponId && this._skillId && this._defenseManeuverId + if (this._targetNumber >= 0 && this._targetId && this._weaponId && this._skillId && maneuver) { + context.ready = true + } else { + context.notReadyReason = `${game.i18n.format("COMBAT_DIALOG.notReadyReason.title")}" + context.ready = false + } return context } else { ui.notifications.error(`Feature funktioniert nur wenn der Akteur ein Token auf der aktuellen Szene hat`); @@ -335,12 +356,10 @@ export class CombatActionDialog extends HandlebarsApplicationMixin(ApplicationV2 target.textContent = `(${result})` targetDescription.textContent = this._modDescription - if (result <= 0) { - context.ready = false + if (result <= 0 || !context.ready) { this.element.querySelector(".actions button").classList.remove("ready") this.element.querySelector(".actions button").setAttribute("disabled", true) } else { - context.ready = true this.element.querySelector(".actions button").classList.add("ready") this.element.querySelector(".actions button").removeAttribute("disabled") } diff --git a/src/module/dialog/defenseAction.mjs b/src/module/dialog/defenseAction.mjs index 6d4db78e..5e136d90 100644 --- a/src/module/dialog/defenseAction.mjs +++ b/src/module/dialog/defenseAction.mjs @@ -49,16 +49,16 @@ export class DefenseActionDialog extends HandlebarsApplicationMixin(ApplicationV */ _actor = null - constructor(actor, attackData) { + constructor(actor, data, attackData) { super(); - this._attackData = attackData ?? { + /*this._attackData = attackData ?? { modToDefense: 0, attacker: null, weapon: null, // is important to note as weapons like Chain Weapons or Flails can ignore Shields - } + }*/ this._actor = actor - this._skillId = null - this._weaponId = null + this._skillId = data.skill ? data.skill : null + this._weaponId = data.weapon ? data.weapon : null this._defenseManeuverId = null this._actionManager = new ActionManager(this._actor) //if (this._actor) { @@ -254,7 +254,26 @@ export class DefenseActionDialog extends HandlebarsApplicationMixin(ApplicationV // TODO get W/M of weapon NOW - context.ready = this._targetId && this._weaponId && this._skillId && this._defenseManeuverId + if (this._weaponId && this._skillId && this._defenseManeuverId) { + context.ready = true + } else { + context.notReadyReason = `${game.i18n.format("COMBAT_DIALOG.notReadyReason.title")}" + context.ready = false + } + return context } else { ui.notifications.error(`Feature funktioniert nur wenn der Akteur ein Token auf der aktuellen Szene hat`); @@ -282,12 +301,10 @@ export class DefenseActionDialog extends HandlebarsApplicationMixin(ApplicationV target.textContent = `(${result})` targetDescription.textContent = this._modDescription - if (result <= 0) { - context.ready = false + if (result <= 0 || !context.ready) { this.element.querySelector(".actions button").classList.remove("ready") this.element.querySelector(".actions button").setAttribute("disabled", true) } else { - context.ready = true this.element.querySelector(".actions button").classList.add("ready") this.element.querySelector(".actions button").removeAttribute("disabled") } diff --git a/src/module/setup/partials.mjs b/src/module/setup/partials.mjs index 9ff711c4..cf2de449 100644 --- a/src/module/setup/partials.mjs +++ b/src/module/setup/partials.mjs @@ -4,6 +4,7 @@ function loadPartials(hbs) { hbs.loadTemplates([ // ui partials. 'systems/DSA_4-1/templates/ui/partial-rollable-button.hbs', + 'systems/DSA_4-1/templates/ui/partial-cooldown.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', diff --git a/src/module/sheets/characterSheet.mjs b/src/module/sheets/characterSheet.mjs index 5508fab8..b59c2cfb 100644 --- a/src/module/sheets/characterSheet.mjs +++ b/src/module/sheets/characterSheet.mjs @@ -50,6 +50,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { activateCooldown: CharacterSheet.#activateCooldown, rest: CharacterSheet.#startResting, removeEffect: CharacterSheet.#removeEffect, + rollDamage: CharacterSheet.#rollDamage, } } @@ -202,12 +203,14 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { } static #openCombatAction(event, target) { + let {weapon, skill} = target.dataset + switch (target.dataset.mode) { case "attack": - new CombatActionDialog(this.document).render(true) + new CombatActionDialog(this.document, {weapon, skill}).render(true) break case "defense": - new DefenseActionDialog(this.document).render(true) + new DefenseActionDialog(this.document, {weapon, skill}).render(true) break } } @@ -261,6 +264,39 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { await this.document.update(formData.object) } + static async #rollDamage(event, target) { + let {weapon, isRanged} = target.dataset + isRanged = isRanged == "true" + weapon = this.document.items.get(weapon) + if (weapon) { + const damageFormula = isRanged ? weapon.system.rangedAttackDamage : weapon.system.meleeAttackDamage + const calculation = await foundry.applications.api.DialogV2.prompt({ + window: {title: game.i18n.format("COMBAT_DIALOG_TP.windowTitle")}, + content: `
`, + ok: { + label: game.i18n.format("COMBAT_DIALOG_TP.buttonText"), + callback: (event, button, dialog) => { + return { + formula: button.form.elements.formula.value, + bonusDamage: button.form.elements.bonusDamage.value + } + } + } + }); + + const sanitisedFormula = calculation.formula.replace(/wW/g, "d") + const suffix = calculation.bonusDamage >= 0 ? "+" + calculation.bonusDamage : calculation.bonusDamage + + let r = new Roll(sanitisedFormula + suffix, this.document.getRollData()); + const label = `Schadenswurf` + await r.toMessage({ + speaker: ChatMessage.getSpeaker({actor: this.document}), + flavor: label, + rollMode: game.settings.get('core', 'rollMode'), + }) + } + } + _getTabsConfig(group) { const tabs = foundry.utils.deepClone(super._getTabsConfig(group)) @@ -363,7 +399,10 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { fkitems.forEach(skill => { context.attacks.push({ name: skill.name, + id: fernkampf._id, + skillId: skill._id, using: fernkampf.name, + isRanged: true, at: `${this.document.system.fk.aktuell + skill.system.at}`, tp: `${fernkampf.system.rangedAttackDamage}`, ini: `${context.inidice}w6 + ${context.inivalue + fernkampf.system.iniModifier ?? 0}`, @@ -382,7 +421,10 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { const obj = skill context.attacks.push({ name: obj.name, + id: links._id, + skillId: skill._id, using: links.name, + isRanged: false, at: `${this.document.system.at.links.aktuell + obj.system.at + links.system.attackModifier}`, pa: `${this.document.system.pa.links.aktuell + obj.system.pa + links.system.parryModifier}`, tp: `${links.system.meleeAttackDamage}`, @@ -402,7 +444,10 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { const obj = skill context.attacks.push({ name: obj.name, + id: rechts._id, + skillId: skill._id, using: rechts.name, + isRanged: false, at: `${this.document.system.at.rechts.aktuell + obj.system.at + rechts.system.attackModifier}`, pa: `${this.document.system.pa.rechts.aktuell + obj.system.pa + rechts.system.parryModifier}`, tp: `${rechts.system.meleeAttackDamage}`, diff --git a/src/style/atoms/_typography.scss b/src/style/atoms/_typography.scss index 0827abfe..a56c325e 100644 --- a/src/style/atoms/_typography.scss +++ b/src/style/atoms/_typography.scss @@ -24,7 +24,6 @@ } input, - .rkp .pill, .cooldown > span, .attribute.rollable > .name, .attribute.rollable > .wert { diff --git a/src/style/molecules/_equipment.scss b/src/style/molecules/_equipment.scss index 8b541f92..86c606e2 100644 --- a/src/style/molecules/_equipment.scss +++ b/src/style/molecules/_equipment.scss @@ -9,14 +9,12 @@ padding: 2px 0 0 2px; margin: 4px 0 0 4px; gap: 8px; - @if darkmode { - background: rgba(0, 0, 0, 0.3); - } @else { - background: assets.$tab-pane-background; - } + background: assets.$tab-pane-background; .icon { - width: 32px; + object-fit: cover; + max-width: 32px; + max-height: 32px; padding: 0; } @@ -55,4 +53,12 @@ padding: 0 4px 4px 0; z-index: 2; } + + &.worn { + + .name { + font-weight: bold; + } + + } } \ No newline at end of file diff --git a/src/style/molecules/_sidebar-elements.scss b/src/style/molecules/_sidebar-elements.scss index 2ec297a8..7a696c23 100644 --- a/src/style/molecules/_sidebar-elements.scss +++ b/src/style/molecules/_sidebar-elements.scss @@ -133,10 +133,28 @@ .ini { grid-area: ini; + cursor: pointer; + + label { + cursor: pointer; + } + + &:hover { + text-shadow: 0 0 2px rgba(255, 0, 0, 1); + } } .tp { grid-area: tp; + cursor: pointer; + + label { + cursor: pointer; + } + + &:hover { + text-shadow: 0 0 2px rgba(255, 0, 0, 1); + } } } diff --git a/src/style/organisms/_character-sheet.scss b/src/style/organisms/_character-sheet.scss index 5b452e73..a653ed6f 100644 --- a/src/style/organisms/_character-sheet.scss +++ b/src/style/organisms/_character-sheet.scss @@ -36,6 +36,14 @@ font-size: large; } + .rkp { + + .pill { + cursor: pointer; + } + + } + } div.head-data { @@ -50,6 +58,7 @@ .profile-img { width: $sidebar-width - 16px; } + } nav.sheet-tabs.tabs { diff --git a/src/style/organisms/_combat-action-dialog.scss b/src/style/organisms/_combat-action-dialog.scss index 274d1032..e5359f2b 100644 --- a/src/style/organisms/_combat-action-dialog.scss +++ b/src/style/organisms/_combat-action-dialog.scss @@ -45,6 +45,8 @@ li { + cursor: pointer; + height: 32px; display: grid; line-height: 32px; @@ -63,6 +65,7 @@ } &.name-only { + padding-left: 40px; display: block; } } diff --git a/src/system.json b/src/system.json index 94324e87..be6b6c09 100644 --- a/src/system.json +++ b/src/system.json @@ -2,7 +2,7 @@ "id": "DSA_4-1", "title": "Das Schwarze Auge 4.1", "description": "Noch ein Spielsystem für Das Schwarze Auge 4.1", - "version": "0.5.1-rc1", + "version": "0.0.1", "compatibility": { "minimum": 12, "verified": 13 @@ -349,5 +349,5 @@ "primaryTokenAttribute": "lep.aktuell", "url": "https://git.macniel.online/macniel/foundry-dsa41-game", "manifest": "https://git.macniel.online/macniel/foundry-dsa41-game/raw/branch/main/src/system.json", - "download": "https://git.macniel.online/macniel/foundry-dsa41-game/releases/download/0.5.1-rc1/release.zip" + "download": "https://git.macniel.online/macniel/foundry-dsa41-game/releases/download/0.0.1/release.zip" } diff --git a/src/templates/actor/character/main-sheet.hbs b/src/templates/actor/character/main-sheet.hbs index e4420fc3..f4198f96 100644 --- a/src/templates/actor/character/main-sheet.hbs +++ b/src/templates/actor/character/main-sheet.hbs @@ -62,25 +62,34 @@

{{this.using}} ({{this.name}})

{{#if this.at}} -