implements ui for battle dice

pull/62/head
macniel 2025-10-29 13:21:48 +01:00
parent db708d991c
commit b50e4d3090
8 changed files with 734 additions and 18 deletions

View File

@ -31,6 +31,7 @@ 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([
@ -58,7 +59,8 @@ Hooks.once("init", () => {
Zonenwunde,
Trefferzone,
Wunde,
RestingDialog
RestingDialog,
BattleDialog
}
// Configure custom Document implementations.

View File

@ -0,0 +1,171 @@
import {ActionManager} from "../sheets/actions/action-manager.mjs";
const {ApplicationV2, HandlebarsApplicationMixin} = foundry.applications.api
/**
* @typedef TokenDistance
* @property {Number} x
* @property {Number} y
* @property {Number} d
* @property {Token} token
*/
export class BattleDialog extends HandlebarsApplicationMixin(ApplicationV2) {
static DEFAULT_OPTIONS = {
classes: ['dsa41', 'dialog', 'battle'],
tag: "form",
position: {
width: 640,
height: 518
},
window: {
resizable: false,
},
form: {
submitOnChange: true,
closeOnSubmit: false,
handler: BattleDialog.#onSubmitForm
},
actions: {
selectOffenseActor: BattleDialog.#setOffenseActor,
selectDefenseActor: BattleDialog.#setDefenseActor,
doBattle: BattleDialog.#doBattle,
}
}
static PARTS = {
form: {
template: 'systems/DSA_4-1/templates/dialog/battle-dialog.hbs',
}
}
/**
* @type {Actor}
* @private
*/
_offenseActor = null
_defenseActor = null
constructor() {
super()
}
static async #onSubmitForm(event, form, formData) {
event.preventDefault()
this._offenseTalent = formData.object['offense.talent']
this._defenseTalent = formData.object['defense.talent']
}
static #setOffenseActor(event, target) {
const {id} = target.dataset
this._offenseActor = game.actors.get(id)
this.render({parts: ["form"]})
}
static #setDefenseActor(event, target) {
const {id} = target.dataset
this._defenseActor = game.actors.get(id)
this.render({parts: ["form"]})
}
static #doBattle(event, target) {
// TODO get test values from talents either from the object or from the fields
if (formData.object['saveOffenseData']) {
}
if (formData.object['saveDefenseData']) {
}
// TODO perform Dice Rolls but in secret mode so its up to the GM if they want to display the result or not
this.close()
}
_configureRenderOptions(options) {
super._configureRenderOptions(options)
if (options.window) {
options.window.title = "Vergleichende Proben"
}
return options
}
async _prepareContext(options) {
const context = await super._prepareContext(options)
context.actors = game.actors.filter(actor => actor.type === "character" || actor.type === "creature")
context.offenseTalent = this._offenseTalent ?? ''
context.offenseTalents = {}
if (this._offenseActor) {
context.offenseActor = this._offenseActor._id
if (this._offenseActor.system.attribute) {
context.offenseAttributes = {}
Object.entries(this._offenseActor.system.attribute)?.forEach(([key, eigenschaft]) => {
context.offenseAttributes[key] = eigenschaft?.aktuell ?? 0
})
} else {
context.offenseAttributes = false
}
if (this._offenseActor.itemTypes["Skill"]?.length > 0) {
this._offenseActor.itemTypes["Skill"]?.forEach((skill) => {
if (skill.system.probe.length === 3) {
context.offenseTalents[`${skill.name}: ${skill.system.taw} (${skill.system.probe[0]}/${skill.system.probe[1]}/${skill.system.probe[2]})`] = skill.id
}
})
} else {
context.offenseTalents = false
}
}
context.defenseTalent = this._defenseTalent ?? ''
context.defenseTalents = {}
if (this._defenseActor) {
context.defenseActor = this._defenseActor._id
if (this._defenseActor.system.attribute) {
context.defenseAttributes = {}
Object.entries(this._defenseActor.system.attribute)?.forEach(([key, eigenschaft]) => {
context.defenseAttributes[key] = eigenschaft?.aktuell ?? 0
})
} else {
context.defenseAttributes = false
}
if (this._defenseActor.itemTypes["Skill"]?.length > 0) {
this._defenseActor.itemTypes["Skill"]?.forEach((skill) => {
if (skill.system.probe.length === 3) {
context.defenseTalents[`${skill.name}: ${skill.system.taw} (${skill.system.probe[0]}/${skill.system.probe[1]}/${skill.system.probe[2]})`] = skill.id
}
})
} else {
context.defenseTalents = false
}
}
return context
}
_onRender(context, options) {
}
}

View File

@ -0,0 +1,14 @@
.dsa41 {
fieldset {
border-left: 0;
border-bottom: 0;
border-right: 0;
legend {
padding: 0 16px;
text-align: center;
}
}
}

View File

@ -0,0 +1,195 @@
.dsa41.dialog.battle {
section[data-application-part="form"] {
display: grid;
height: 100%;
grid-template-columns: 1fr 1fr;
grid-template-rows: 32px 1fr 32px;
gap: 8px;
grid-template-areas: "presets presets" "offense defense" "summary summary";
.presets {
grid-area: presets;
label {
display: flex;
flex-direction: row;
height: 32px;
justify-content: center;
gap: 0 8px;
span {
height: 32px;
line-height: 32px;
vertical-align: middle;
flex: 0;
}
select {
flex: 0;
width: 180px;
}
button {
height: 32px;
flex: 0;
}
}
}
.offense-character {
grid-area: offense;
}
.defense-character {
grid-area: defense;
}
.summary {
grid-area: summary;
}
.scroll-y {
overflow: hidden;
overflow-y: auto;
height: calc(3 * (32px + 8px))
}
.actor {
height: 32px;
margin-bottom: 8px;
display: grid;
grid-template-columns: 32px 1fr;
img {
width: 32px;
height: 32px;
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.5);
border: 1px inset;
}
span {
height: 32px;
line-height: 32px;
vertical-align: middle;
}
&.selected {
background-color: rgba(255, 140, 0, 0.2);
border: 1px solid orange;
border-radius: 4px;
}
}
span.dummylabel {
height: 16px;
line-height: 16px;
display: block;
}
.attributes {
display: grid;
grid-template-columns: repeat(8, 1fr);
height: 48px;
.attribut {
label {
span {
display: block;
height: 16px;
line-height: 16px;
vertical-align: middle;
width: 100%;
text-align: center;
}
input {
height: 32px;
width: 100%;
}
output {
height: 32px;
width: 100%;
line-height: 32px;
vertical-align: middle;
display: block;
padding: 0 8px;
border: 1px inset;
border-radius: 4px;
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
}
}
}
}
.talent {
display: grid;
grid-template-columns: 1fr 32px 32px 32px 32px;
height: 48px;
label {
span {
display: block;
height: 16px;
line-height: 16px;
vertical-align: middle;
text-align: center;
width: 100%;
}
input {
height: 32px;
width: 100%;
&.attrib {
padding: 0;
text-align: center;
}
}
}
}
.summary {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-areas: "offense buttons defense";
button {
grid-area: buttons;
}
.offenseActorSave {
grid-area: offense;
input {
position: relative;
top: 2px;
}
}
.defenseActorSave {
grid-area: defense;
justify-self: end;
input {
position: relative;
top: 2px;
}
}
}
}
}

View File

@ -23,17 +23,9 @@
flex: 1;
border-bottom: 0;
border-left: 0;
border-right: 0;
padding: 0;
margin: 0;
legend {
text-align: center;
padding: 0 16px;
}
&.modding {
flex: 0;
}

View File

@ -30,14 +30,6 @@
fieldset {
grid-area: options;
border-left: 0;
border-bottom: 0;
border-right: 0;
legend {
padding: 0 16px;
text-align: center;
}
div {

View File

@ -10,6 +10,7 @@
@use "molecules/sheet-header";
@use "molecules/coins";
@use "molecules/weights";
@use "molecules/fieldset";
@use "molecules/tabs";
@use "molecules/paperdoll";
@ -30,4 +31,5 @@
@use "organisms/xml-import-dialog";
@use "organisms/combat-action-dialog";
@use "organisms/merchant-sheet";
@use "organisms/resting-dialog";
@use "organisms/resting-dialog";
@use "organisms/battle-dialog";

View File

@ -0,0 +1,348 @@
<section>
<div class="presets">
<label><span>Voreinstellungen</span>
<select name="battle-presets">
<option value="" selected></option>
<option value="zechen-v-zechen">Vergleichend: Zechen</option>
<option value="überreden-v-menschenkenntnis">Überreden</option>
<option value="überzeugen-v-menschenkenntnis">Überzeugen</option>
<option value="schleichen-v-sinnenschärfe">Schleichen</option>
<option value="sich-verstecken-v-sinnenschärfe">Sich verstecken</option>
<option value="glückspiel-v-glückspiel">Vergleichend: Glücksspiel</option>
<option value="falschspiel-v-sinnenschärfe">Schummeln</option>
</select>
<button data-action="applyPreset"><i class="fa-solid fa-fill"></i></button>
</label>
</div>
<div class="offense-character">
<fieldset>
<legend>Charakterauswahl</legend>
<div class="scroll-y">
{{#each actors}}
<div class="actor {{#if (eq ../offenseActor this._id)}}selected{{/if}}" data-id="{{this._id}}"
data-action="selectOffenseActor">
<img src="{{this.img}}" alt="{{this.name}}">
<span>{{this.name}}</span>
</div>
{{/each}}
</div>
</fieldset>
{{#if (not offenseAttributes)}}
<fieldset>
<legend>Eigenschaften</legend>
<div class="attributes">
<div class="attribut">
<label><span>MU</span>
<input name="offenseAttributes.mu" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>KL</span>
<input name="offenseAttributes.kl" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>IN</span>
<input name="offenseAttributes.in" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>CH</span>
<input name="offenseAttributes.ch" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>FF</span>
<input name="offenseAttributes.ff" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>GE</span>
<input name="offenseAttributes.ge" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>KO</span>
<input name="offenseAttributes.ko" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>KK</span>
<input name="offenseAttributes.kk" type="number"/>
</label>
</div>
</div>
</fieldset>
{{else}}
<fieldset>
<legend>Eigenschaften</legend>
<div class="attributes">
<div class="attribut">
<label><span>MU</span>
<output name="offenseAttributes.mu">{{offenseAttributes.mu}}</output>
</label>
</div>
<div class="attribut">
<label><span>KL</span>
<output name="offenseAttributes.mu">{{offenseAttributes.kl}}</output>
</label>
</div>
<div class="attribut">
<label><span>IN</span>
<output name="offenseAttributes.mu">{{offenseAttributes.in}}</output>
</label>
</div>
<div class="attribut">
<label><span>CH</span>
<output name="offenseAttributes.mu">{{offenseAttributes.ch}}</output>
</label>
</div>
<div class="attribut">
<label><span>FF</span>
<output name="offenseAttributes.mu">{{offenseAttributes.ff}}</output>
</label>
</div>
<div class="attribut">
<label><span>GE</span>
<output name="offenseAttributes.mu">{{offenseAttributes.ge}}</output>
</label>
</div>
<div class="attribut">
<label><span>KK</span>
<output name="offenseAttributes.mu">{{offenseAttributes.kk}}</output>
</label>
</div>
<div class="attribut">
<label><span>KO</span>
<output name="defenseAttributes.mu">{{offenseAttributes.ko}}</output>
</label>
</div>
</div>
</fieldset>
{{/if}}
{{#if offenseTalents}}
<fieldset>
<legend>Talent</legend>
<span class="dummylabel">&nbsp;</span>
<select name="offense.talent">
{{selectOptions offenseTalents selected=offenseTalent inverted=true}}
</select>
</fieldset>
{{else}}
<fieldset>
<legend>Talent</legend>
<div class="talent">
<label class="name"><span>Name</span> <input type="text" name="offense.talent.name"/></label>
<label class="value"><span>TaW</span> <input type="text" name="offense.talent.taw"/></label>
<label class="p1"><span></span> <input class="attrib" type="text"
name="offense.talent.probe.0.name"/></label>
<label class="p2"><span></span> <input class="attrib" type="text"
name="offense.talent.probe.1.name"/></label>
<label class="p3"><span></span> <input class="attrib" type="text"
name="offense.talent.probe.2.name"/></label>
</div>
</fieldset>
{{/if}}
<fieldset>
<legend>Erschwernisse</legend>
<input name="offense_penalty" type="number"/>
</fieldset>
</div>
<div class="defense-character">
<fieldset>
<legend>Charakterauswahl</legend>
<div class="scroll-y">
{{#each actors}}
<div class="actor {{#if (eq ../defenseActor this._id)}}selected{{/if}}" data-id="{{this._id}}"
data-action="selectDefenseActor">
<img src="{{this.img}}" alt="{{this.name}}">
<span>{{this.name}}</span>
</div>
{{/each}}
</div>
</fieldset>
{{#if (not defenseAttributes)}}
<fieldset>
<legend>Eigenschaften</legend>
<div class="attributes">
<div class="attribut">
<label><span>MU</span>
<input name="defenseAttributes.mu" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>KL</span>
<input name="defenseAttributes.kl" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>IN</span>
<input name="defenseAttributes.in" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>CH</span>
<input name="defenseAttributes.ch" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>FF</span>
<input name="defenseAttributes.ff" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>GE</span>
<input name="defenseAttributes.ge" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>KK</span>
<input name="defenseAttributes.ko" type="number"/>
</label>
</div>
<div class="attribut">
<label><span>KO</span>
<input name="defenseAttributes.kk" type="number"/>
</label>
</div>
</div>
</fieldset>
{{else}}
<fieldset>
<legend>Eigenschaften</legend>
<div class="attributes">
<div class="attribut">
<label><span>MU</span>
<output name="defenseAttributes.mu">{{defenseAttributes.mu}}</output>
</label>
</div>
<div class="attribut">
<label><span>KL</span>
<output name="defenseAttributes.kl">{{defenseAttributes.kl}}</output>
</label>
</div>
<div class="attribut">
<label><span>IN</span>
<output name="defenseAttributes.in">{{defenseAttributes.in}}</output>
</label>
</div>
<div class="attribut">
<label><span>CH</span>
<output name="defenseAttributes.ch">{{defenseAttributes.ch}}</output>
</label>
</div>
<div class="attribut">
<label><span>FF</span>
<output name="defenseAttributes.ff">{{defenseAttributes.ff}}</output>
</label>
</div>
<div class="attribut">
<label><span>GE</span>
<output name="defenseAttributes.ge">{{defenseAttributes.ge}}</output>
</label>
</div>
<div class="attribut">
<label><span>KO</span>
<output name="defenseAttributes.ko">{{defenseAttributes.ko}}</output>
</label>
</div>
<div class="attribut">
<label><span>KK</span>
<output name="defenseAttributes.kk">{{defenseAttributes.kk}}</output>
</label>
</div>
</div>
</fieldset>
{{/if}}
{{#if defenseTalents}}
<fieldset>
<legend>Talent</legend>
<span class="dummylabel">&nbsp;</span>
<select name="defense.talent">
{{selectOptions defenseTalents selected=defenseTalent inverted=true}}
</select>
</fieldset>
{{else}}
<fieldset>
<legend>Talent</legend>
<div class="talent">
<label class="name"><span>Name</span> <input type="text" name="defense.talent.name"/></label>
<label class="value"><span>TaW</span> <input type="text" name="defense.talent.taw"/></label>
<label class="p1"><span></span> <input class="attrib" type="text"
name="defense.talent.probe.0.name"/></label>
<label class="p2"><span></span> <input class="attrib" type="text"
name="defense.talent.probe.1.name"/></label>
<label class="p3"><span></span> <input class="attrib" type="text"
name="defense.talent.probe.2.name"/></label>
</div>
</fieldset>
{{/if}}
<fieldset>
<legend>Erschwernisse</legend>
<input name="defense_penalty" type="number"/>
</fieldset>
</div>
<div class="summary">
{{#if (or (not offenseTalents) (not offenseAttributes))}}
<div class="offenseActorSave"><label><input name="saveOffenseData" type="checkbox">Daten speichern</label>
</div>
{{/if}}
<button data-action="doBattle"><i class="fa-solid fa-user-secret"></i> Würfeln</button>
{{#if (or (not defenseTalents) (not defenseAttributes))}}
<div class="defenseActorSave"><label><input name="saveDefenseData" type="checkbox">Daten speichern</label>
</div>
{{/if}}
</div>
</section>