implements combat attack dialog
parent
b2d9a82af8
commit
7f18c62270
|
|
@ -48,7 +48,11 @@ export class SpecialAbilityDataModel extends BaseItem {
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
waffenLimit: new ArrayField(
|
waffenLimit: new ArrayField(
|
||||||
new StringField(),
|
new SchemaField({
|
||||||
|
waffe: new StringField(),
|
||||||
|
gruppe: new StringField(),
|
||||||
|
mod: new NumberField(),
|
||||||
|
})
|
||||||
),
|
),
|
||||||
mod: new ArrayField(new SchemaField({
|
mod: new ArrayField(new SchemaField({
|
||||||
name: new StringField(),
|
name: new StringField(),
|
||||||
|
|
@ -67,11 +71,13 @@ export class SpecialAbilityDataModel extends BaseItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isActive() { // TODO also handle Waffenlimit
|
isActive(options) { // TODO also handle Waffenlimit
|
||||||
|
|
||||||
const requirements = this.#getRequirements()
|
const requirements = this.#getRequirements()
|
||||||
|
|
||||||
let passes = false
|
let passes = false
|
||||||
|
let mod = 0
|
||||||
|
|
||||||
const flatActor = foundry.utils.flattenObject(this.parent.actor.system)
|
const flatActor = foundry.utils.flattenObject(this.parent.actor.system)
|
||||||
for (let requirement of requirements) {
|
for (let requirement of requirements) {
|
||||||
|
|
||||||
|
|
@ -90,11 +96,55 @@ export class SpecialAbilityDataModel extends BaseItem {
|
||||||
passes = targetField <= requirement.maxValue
|
passes = targetField <= requirement.maxValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (requirement["compare"]) {
|
||||||
|
const {ownAttribute, operation, targetAttribute} = requirement["compare"]
|
||||||
|
if (options.target) {
|
||||||
|
const flatTarget = foundry.utils.flattenObject(options.target.system)
|
||||||
|
const foreignTargetField = flatTarget[targetAttribute]
|
||||||
|
const ourTargetField = flatActor[ownAttribute]
|
||||||
|
switch (operation) {
|
||||||
|
case "lt":
|
||||||
|
passes = ourTargetField < foreignTargetField;
|
||||||
|
break;
|
||||||
|
case "lte":
|
||||||
|
passes = ourTargetField <= foreignTargetField;
|
||||||
|
break;
|
||||||
|
case "eq":
|
||||||
|
passes = ourTargetField == foreignTargetField;
|
||||||
|
break;
|
||||||
|
case "neq":
|
||||||
|
passes = ourTargetField != foreignTargetField;
|
||||||
|
break;
|
||||||
|
case "gte":
|
||||||
|
passes = ourTargetField >= foreignTargetField;
|
||||||
|
break;
|
||||||
|
case "gt":
|
||||||
|
passes = ourTargetField > foreignTargetField;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
passes = false
|
||||||
|
}
|
||||||
|
|
||||||
if (!passes) {
|
if (!passes) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return passes
|
if (passes) { // TODO: how are we going to communicate the malus?
|
||||||
|
this.system.waffenLimit.forEach(waff => {
|
||||||
|
if (waff.waffe) {
|
||||||
|
passes = options.weapon.name === waff.waffe
|
||||||
|
if (waff.mod) mod = waff.mod
|
||||||
|
} else if (waff.gruppe) {
|
||||||
|
passes = options.skill.name === waff.gruppe
|
||||||
|
if (waff.mod) mod = waff.mod
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {passes, mod}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
import {XmlImport} from "../xml-import/xml-import.mjs";
|
||||||
|
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 CombatActionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ['dsa41', 'dialog', 'combat'],
|
||||||
|
tag: "form",
|
||||||
|
position: {
|
||||||
|
width: 320,
|
||||||
|
height: 540
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
resizable: false,
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
submitOnChange: false,
|
||||||
|
closeOnSubmit: true,
|
||||||
|
handler: CombatActionDialog.#onSubmitForm
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
selectTarget: CombatActionDialog.#onSelectTarget,
|
||||||
|
selectWeaponAndSkill: CombatActionDialog.#onSelectWeaponAndSkill,
|
||||||
|
selectManeuver: CombatActionDialog.#onSelectManeuver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
form: {
|
||||||
|
template: 'systems/DSA_4-1/templates/dialog/combat-action.hbs',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Actor}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_actor = null
|
||||||
|
|
||||||
|
constructor(actor) {
|
||||||
|
super();
|
||||||
|
this._actor = actor
|
||||||
|
this._targetId = null
|
||||||
|
this._skillId = null
|
||||||
|
this._weaponId = null
|
||||||
|
this._combatManeuverId = null
|
||||||
|
this._actionManager = new ActionManager(this._actor)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static async #onSelectTarget(event, target) {
|
||||||
|
const {targetId} = target.dataset
|
||||||
|
this._targetId = this._targetId === targetId ? null : targetId
|
||||||
|
this.render({parts: ["form"]})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onSelectManeuver(event, target) {
|
||||||
|
const {maneuverId} = target.dataset
|
||||||
|
this._combatManeuverId = this._combatManeuverId === maneuverId ? null : maneuverId
|
||||||
|
this.render({parts: ["form"]})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static async #onSelectWeaponAndSkill(event, target) {
|
||||||
|
const {weaponId, skillId} = target.dataset
|
||||||
|
this._weaponId = this._weaponId === weaponId ? null : weaponId
|
||||||
|
this._skillId = this._skillId === skillId ? null : skillId
|
||||||
|
this.render({parts: ["form"]})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onSubmitForm(event, form, formData) {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
_configureRenderOptions(options) {
|
||||||
|
super._configureRenderOptions(options)
|
||||||
|
if (options.window) {
|
||||||
|
options.window.title = `Mit ${this._actor.name} angreifen`
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
#getDistanceBetween(originToken, targetToken) {
|
||||||
|
const distance = game.scenes.current.dimensions.distancePixels
|
||||||
|
|
||||||
|
// get distances between of tokens and thisTokenRepresentative
|
||||||
|
const actorOfToken = game.actors.get(targetToken.actorId)
|
||||||
|
return {
|
||||||
|
id: targetToken._id,
|
||||||
|
x: targetToken.x,
|
||||||
|
y: targetToken.y,
|
||||||
|
actor: actorOfToken,
|
||||||
|
token: targetToken,
|
||||||
|
d: ((Math.sqrt(Math.pow(targetToken.x - originToken.x, 2) + Math.pow(targetToken.y - originToken.y, 2))) / distance).toFixed(2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#evaluateDistances() {
|
||||||
|
|
||||||
|
// get tokens in range on the current scene
|
||||||
|
const scene = game.scenes.current
|
||||||
|
const tokens = scene.tokens
|
||||||
|
const tokenId = this._actor.getActiveTokens()[0].id
|
||||||
|
const thisTokenRepresentative = tokens.get(tokenId)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {[TokenDistance]}
|
||||||
|
*/
|
||||||
|
return tokens.map(token => {
|
||||||
|
return {
|
||||||
|
isSelected: this._targetId === token.id,
|
||||||
|
...this.#getDistanceBetween(thisTokenRepresentative, token)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#evaluateWeapons() {
|
||||||
|
|
||||||
|
// get equipped weapons and adjust AT/PA values by basis values from actor TODO: and W/M of weapons
|
||||||
|
const equips = this._actor.system.heldenausruestung[this._actor.system.setEquipped]
|
||||||
|
const weapons = []
|
||||||
|
|
||||||
|
const equippedWeapons = ["links", "rechts", "fernkampf"]
|
||||||
|
|
||||||
|
const baseAt = {
|
||||||
|
links: this._actor.system.at.links.aktuell, // TODO hook Beidhändigerkampf/linkhand
|
||||||
|
rechts: this._actor.system.at.rechts.aktuell,
|
||||||
|
fernkampf: this._actor.system.fk.aktuell,
|
||||||
|
}
|
||||||
|
const basePa = {
|
||||||
|
links: this._actor.system.pa.links.aktuell, // TODO hook Beidhändigerkampf/linkhand
|
||||||
|
rechts: this._actor.system.pa.rechts.aktuell,
|
||||||
|
fernkampf: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
equippedWeapons.forEach(slot => {
|
||||||
|
const equip = equips[slot]
|
||||||
|
const weapon = this._actor.itemTypes["Equipment"].find(p => p._id === equip)
|
||||||
|
if (weapon) {
|
||||||
|
|
||||||
|
const variantWeaponSkills = [...weapon.system.rangedSkills, ...weapon.system.meleeSkills]
|
||||||
|
|
||||||
|
variantWeaponSkills.forEach(weaponSkill => {
|
||||||
|
|
||||||
|
const skill = this._actor.itemTypes["Skill"].find(p => p.name === weaponSkill)
|
||||||
|
if (skill) {
|
||||||
|
|
||||||
|
const skillAt = skill.system.at
|
||||||
|
const skillPa = skill.system.pa
|
||||||
|
|
||||||
|
weapons.push({
|
||||||
|
isSelected: this._skillId === skill._id && this._weaponId === weapon._id,
|
||||||
|
weaponId: weapon._id,
|
||||||
|
skillId: skill._id,
|
||||||
|
name: weapon.name,
|
||||||
|
skillName: skill.name,
|
||||||
|
img: weapon.img,
|
||||||
|
combatStatistics: {
|
||||||
|
at: baseAt[slot] + weapon.system.attackModifier + skillAt,
|
||||||
|
pa: basePa[slot] + weapon.system.parryModifier + skillPa
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this._weapons = weapons
|
||||||
|
return this._weapons
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#evaluateManeuvers() {
|
||||||
|
const manager = this._actionManager
|
||||||
|
const weapon = this._actor.itemTypes["Equipment"].find(p => p._id === this._weaponId)
|
||||||
|
const skill = this._actor.itemTypes["Skill"].find(p => p._id === this._skillId)
|
||||||
|
const target = game.actors.get(this._targetId)
|
||||||
|
this._maneuvers = manager.evaluate({
|
||||||
|
target,
|
||||||
|
weapon,
|
||||||
|
skill
|
||||||
|
}).filter(p => p.type === ActionManager.ATTACK).map(action => {
|
||||||
|
return {
|
||||||
|
isSelected: this._combatManeuverId === action.name,
|
||||||
|
id: action.name,
|
||||||
|
name: action.name,
|
||||||
|
type: action.type,
|
||||||
|
source: action.source,
|
||||||
|
cost: action.cost,
|
||||||
|
mod: action.mod,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return this._maneuvers
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareContext(options) {
|
||||||
|
const context = await super._prepareContext(options)
|
||||||
|
|
||||||
|
context.actor = this._actor
|
||||||
|
context.distanceUnit = game.scenes.current.grid.units
|
||||||
|
|
||||||
|
if (this._actor.getActiveTokens()[0]?.id) {
|
||||||
|
|
||||||
|
context.tokenDistances = this.#evaluateDistances()
|
||||||
|
context.weapons = this.#evaluateWeapons()
|
||||||
|
|
||||||
|
if (this._targetId && this._weaponId && this._skillId) {
|
||||||
|
context.maneuver = this.#evaluateManeuvers()
|
||||||
|
}
|
||||||
|
const maneuver = this._maneuvers?.find(p => p.id === this._combatManeuverId)
|
||||||
|
if (maneuver) {
|
||||||
|
context.canMod = maneuver.mod != undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
context.targetNumber = context.weapons.find(p => p.weaponId === this._weaponId && p.skillId === this._skillId)?.combatStatistics.at
|
||||||
|
|
||||||
|
// TODO get W/M of weapon NOW
|
||||||
|
|
||||||
|
context.ready = this._targetId && this._weaponId && this._skillId && this._combatManeuverId
|
||||||
|
return context
|
||||||
|
} else {
|
||||||
|
ui.notifications.error(`Feature funktioniert nur wenn der Akteur ein Token auf der aktuellen Szene hat`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_onRender(context, options) {
|
||||||
|
const target = this.element.querySelector(".actions button .value")
|
||||||
|
this.element.querySelectorAll('[name="mod"], [name="malus"]').forEach(e => e.addEventListener('change', (event) => {
|
||||||
|
|
||||||
|
const at = Number(context.targetNumber)
|
||||||
|
const malus = Number(this.element.querySelector('[name="malus"]').value)
|
||||||
|
const mod = Number(this.element.querySelector('[name="mod"]').value)
|
||||||
|
const maneuver = this._maneuvers?.find(p => p.id === this._combatManeuverId)
|
||||||
|
const result = at + (maneuver.mod?.(mod) ?? 0) + malus
|
||||||
|
target.textContent = `(${result})`
|
||||||
|
}))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -16,8 +16,4 @@ export class SpecialAbility extends Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isActive() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,97 +23,121 @@ export class ActionManager {
|
||||||
cost: ActionManager.FREE,
|
cost: ActionManager.FREE,
|
||||||
type: ActionManager.DEFENSE,
|
type: ActionManager.DEFENSE,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Rufen",
|
name: "Rufen",
|
||||||
cost: ActionManager.FREE,
|
cost: ActionManager.FREE,
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sich zu Boden fallen lassen",
|
name: "Sich zu Boden fallen lassen",
|
||||||
cost: ActionManager.FREE,
|
cost: ActionManager.FREE,
|
||||||
type: ActionManager.MOVEMENT,
|
type: ActionManager.MOVEMENT,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Waffe oder Gegenstand fallen lassen",
|
name: "Waffe oder Gegenstand fallen lassen",
|
||||||
cost: ActionManager.FREE,
|
cost: ActionManager.FREE,
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "getragenes Artefakt aktivieren",
|
name: "getragenes Artefakt aktivieren",
|
||||||
cost: ActionManager.FREE,
|
cost: ActionManager.FREE,
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Schnellziehen",
|
name: "Schnellziehen",
|
||||||
cost: ActionManager.FREE,
|
cost: ActionManager.FREE,
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
source: ActionManager.SF,
|
source: ActionManager.SF,
|
||||||
eval: () => this.#hatSonderfertigkeit("Schnellziehen") && this.#evalSonderfertigkeitRequirements("Schnellziehen")
|
eval: (options) => this.#hatSonderfertigkeit("Schnellziehen", options) && this.#evalSonderfertigkeitRequirements("Schnellziehen", options)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
#regularActions = [
|
#regularActions = [
|
||||||
{
|
{
|
||||||
name: "Angriffsaktion",
|
name: "Nahkampfangriff",
|
||||||
type: ActionManager.ATTACK,
|
type: ActionManager.ATTACK,
|
||||||
cost: ActionManager.REGULAR,
|
cost: ActionManager.REGULAR,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => this.#hatWaffeinHand()
|
eval: (options) => this.#hatWaffeinHand() && !this.#hatFernkampfWaffeinHand()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Fernkampfangriff",
|
||||||
|
type: ActionManager.ATTACK,
|
||||||
|
cost: ActionManager.REGULAR,
|
||||||
|
source: ActionManager.DEFAULT,
|
||||||
|
eval: (options) => this.#hatFernkampfWaffeinHand(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Angesagter Fernkampfangriff",
|
||||||
|
type: ActionManager.ATTACK,
|
||||||
|
cost: ActionManager.CONTINUING,
|
||||||
|
source: ActionManager.DEFAULT,
|
||||||
|
eval: (options) => this.#hatFernkampfWaffeinHand(),
|
||||||
|
mod: (value) => -value * 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Scharfer Schuss",
|
||||||
|
type: ActionManager.ATTACK,
|
||||||
|
cost: ActionManager.CONTINUING,
|
||||||
|
source: ActionManager.SF,
|
||||||
|
eval: (options) => this.#hatFernkampfWaffeinHand() && this.#hatSonderfertigkeit("Scharfschütze", options) && this.#evalSonderfertigkeitRequirements("Scharfschütze", options),
|
||||||
|
mod: (value) => -value
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Schnellschuss",
|
name: "Schnellschuss",
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => this.#hatFernkampfWaffeinHand()
|
eval: (options) => this.#hatFernkampfWaffeinHand()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Schnellschuss (Scharfschütze)",
|
name: "Schnellschuss (Scharfschütze)",
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.SF,
|
source: ActionManager.SF,
|
||||||
eval: () => this.#hatFernkampfWaffeinHand() && this.#hatSonderfertigkeit("Scharfschütze") && this.#evalSonderfertigkeitRequirements("Scharfschütze")
|
eval: (options) => this.#hatFernkampfWaffeinHand() && this.#hatSonderfertigkeit("Scharfschütze", options) && this.#evalSonderfertigkeitRequirements("Scharfschütze", options)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Abwehraktion",
|
name: "Abwehraktion",
|
||||||
type: ActionManager.DEFENSE,
|
type: ActionManager.DEFENSE,
|
||||||
cost: ActionManager.REGULAR,
|
cost: ActionManager.REGULAR,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Bewegen",
|
name: "Bewegen",
|
||||||
type: ActionManager.MOVEMENT,
|
type: ActionManager.MOVEMENT,
|
||||||
cost: ActionManager.REGULAR,
|
cost: ActionManager.REGULAR,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Position",
|
name: "Position",
|
||||||
type: ActionManager.MOVEMENT,
|
type: ActionManager.MOVEMENT,
|
||||||
cost: ActionManager.REGULAR,
|
cost: ActionManager.REGULAR,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Finte",
|
name: "Finte",
|
||||||
type: ActionManager.ATTACK,
|
type: ActionManager.ATTACK,
|
||||||
cost: ActionManager.REGULAR,
|
cost: ActionManager.REGULAR,
|
||||||
source: ActionManager.SF,
|
source: ActionManager.SF,
|
||||||
eval: () =>
|
mod: (value) => value,
|
||||||
|
eval: (options) =>
|
||||||
this.#hatWaffeinHand() &&
|
this.#hatWaffeinHand() &&
|
||||||
this.#hatSonderfertigkeit("Finte") &&
|
this.#hatSonderfertigkeit("Finte", options) &&
|
||||||
this.#evalSonderfertigkeitRequirements("Finte")
|
this.#evalSonderfertigkeitRequirements("Finte", options)
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -121,23 +145,25 @@ export class ActionManager {
|
||||||
type: ActionManager.ATTACK,
|
type: ActionManager.ATTACK,
|
||||||
cost: ActionManager.REGULAR,
|
cost: ActionManager.REGULAR,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
mod: (value) => -(value),
|
||||||
|
eval: (options) => !this.#hatFernkampfWaffeinHand()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Wuchtschlag",
|
name: "Wuchtschlag",
|
||||||
type: ActionManager.ATTACK,
|
type: ActionManager.ATTACK,
|
||||||
cost: ActionManager.REGULAR,
|
cost: ActionManager.REGULAR,
|
||||||
source: ActionManager.SF,
|
source: ActionManager.SF,
|
||||||
eval: () => this.#hatSonderfertigkeit("Wuchtschlag")
|
mod: (value) => -(value),
|
||||||
&& this.#evalSonderfertigkeitRequirements("Wuchtschlag")
|
eval: (options) => !this.#hatFernkampfWaffeinHand() && this.#hatSonderfertigkeit("Wuchtschlag", options)
|
||||||
|
&& this.#evalSonderfertigkeitRequirements("Wuchtschlag", options)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Betäubungsschlag",
|
name: "Betäubungsschlag",
|
||||||
type: ActionManager.ATTACK,
|
type: ActionManager.ATTACK,
|
||||||
cost: ActionManager.REGULAR,
|
cost: ActionManager.REGULAR,
|
||||||
source: ActionManager.SF,
|
source: ActionManager.SF,
|
||||||
eval: () => this.#hatSonderfertigkeit("Betäubungsschlag")
|
eval: (options) => !this.#hatFernkampfWaffeinHand() && this.#hatSonderfertigkeit("Betäubungsschlag", options)
|
||||||
&& this.#evalSonderfertigkeitRequirements("Betäubungsschlag")
|
&& this.#evalSonderfertigkeitRequirements("Betäubungsschlag", options)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -147,76 +173,76 @@ export class ActionManager {
|
||||||
type: ActionManager.TALENT,
|
type: ActionManager.TALENT,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Waffe ziehen",
|
name: "Waffe ziehen",
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sprinten",
|
name: "Sprinten",
|
||||||
type: ActionManager.MOVEMENT,
|
type: ActionManager.MOVEMENT,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Gegenstand benutzen",
|
name: "Gegenstand benutzen",
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Schnellladen (Bogen)",
|
name: "Schnellladen (Bogen)",
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.SF,
|
source: ActionManager.SF,
|
||||||
eval: () => this.#hatMunition()
|
eval: (options) => this.#hatMunition()
|
||||||
&& this.#hatFernkampfWaffeinHand("Bogen")
|
&& this.#hatFernkampfWaffeinHand("Bogen", options)
|
||||||
&& this.#hatSonderfertigkeit("Schnellladen (Bogen)")
|
&& this.#hatSonderfertigkeit("Schnellladen (Bogen)", options)
|
||||||
&& this.#evalSonderfertigkeitRequirements("Schnellladen (Bogen)")
|
&& this.#evalSonderfertigkeitRequirements("Schnellladen (Bogen)", options)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Schnellladen (Armbrust)",
|
name: "Schnellladen (Armbrust)",
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.SF,
|
source: ActionManager.SF,
|
||||||
eval: () => this.#hatMunition()
|
eval: (options) => this.#hatMunition()
|
||||||
&& this.#hatFernkampfWaffeinHand("Armbrust")
|
&& this.#hatFernkampfWaffeinHand("Armbrust", options)
|
||||||
&& this.#hatSonderfertigkeit("Schnellladen (Armbrust)")
|
&& this.#hatSonderfertigkeit("Schnellladen (Armbrust)", options)
|
||||||
&& this.#evalSonderfertigkeitRequirements("Schnellladen (Armbrust)")
|
&& this.#evalSonderfertigkeitRequirements("Schnellladen (Armbrust)", options)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Nachladen",
|
name: "Nachladen",
|
||||||
type: ActionManager.INTERACTION,
|
type: ActionManager.INTERACTION,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => this.#hatMunition()
|
eval: (options) => this.#hatMunition()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Talenteinsatz",
|
name: "Talenteinsatz",
|
||||||
type: ActionManager.TALENT,
|
type: ActionManager.TALENT,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.DEFAULT,
|
source: ActionManager.DEFAULT,
|
||||||
eval: () => true
|
eval: (options) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Zaubern",
|
name: "Zaubern",
|
||||||
type: ActionManager.SPELL,
|
type: ActionManager.SPELL,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.SF,
|
source: ActionManager.SF,
|
||||||
eval: () => this.#hatSonderfertigkeitBeginnendMit("Repräsentation:")
|
eval: (options) => this.#hatSonderfertigkeitBeginnendMit("Repräsentation:", options)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Liturgie wirken",
|
name: "Liturgie wirken",
|
||||||
type: ActionManager.SPELL,
|
type: ActionManager.SPELL,
|
||||||
cost: ActionManager.CONTINUING,
|
cost: ActionManager.CONTINUING,
|
||||||
source: ActionManager.SF,
|
source: ActionManager.SF,
|
||||||
eval: () => this.#hatSonderfertigkeitBeginnendMit("Liturgiekenntnis")
|
eval: (options) => this.#hatSonderfertigkeitBeginnendMit("Liturgiekenntnis", options)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -236,25 +262,30 @@ export class ActionManager {
|
||||||
return item != null
|
return item != null
|
||||||
}
|
}
|
||||||
|
|
||||||
#hatSonderfertigkeitBeginnendMit(name) {
|
#hatSonderfertigkeitBeginnendMit(name, options) {
|
||||||
return this.actor.itemTypes["SpecialAbility"]?.find(p => p.name.startsWith(name)) != null
|
return this.actor.itemTypes["SpecialAbility"]?.find(p => p.name.startsWith(name)) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
#hatSonderfertigkeit(name) {
|
#hatSonderfertigkeit(name, options) {
|
||||||
return this.actor.itemTypes["SpecialAbility"]?.find(p => p.name === name) != null
|
return this.actor.itemTypes["SpecialAbility"]?.find(p => p.name === name) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
#evalSonderfertigkeitRequirements(nameOfSF) {
|
#evalSonderfertigkeitRequirements(nameOfSF, options) {
|
||||||
const sf = this.actor.itemTypes["SpecialAbility"].find(p => p.name === nameOfSF)
|
const sf = this.actor.itemTypes["SpecialAbility"].find(p => p.name === nameOfSF)
|
||||||
return sf.system.isActive()
|
return sf.system.isActive(options).passes
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate() {
|
/**
|
||||||
|
*
|
||||||
|
* @param {{target: String?}} options
|
||||||
|
*/
|
||||||
|
|
||||||
|
evaluate(options) {
|
||||||
let actionArray = [...this.#freeActions, ...this.#regularActions, ...this.#continuingActions]
|
let actionArray = [...this.#freeActions, ...this.#regularActions, ...this.#continuingActions]
|
||||||
console.log(this.actor, actionArray.map((action) => {
|
console.log(this.actor, actionArray.map((action) => {
|
||||||
return {
|
return {
|
||||||
...action,
|
...action,
|
||||||
eval: action.eval()
|
eval: action.eval(options)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
const validActions = actionArray.filter(action => action.eval())
|
const validActions = actionArray.filter(action => action.eval())
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import Meta from "./character/meta.mjs"
|
||||||
import Skills from "./character/skills.mjs"
|
import Skills from "./character/skills.mjs"
|
||||||
import Social from "./character/social.mjs";
|
import Social from "./character/social.mjs";
|
||||||
import Spells from "./character/spells.mjs"
|
import Spells from "./character/spells.mjs"
|
||||||
|
import {CombatActionDialog} from "../dialog/combatAction.mjs";
|
||||||
|
|
||||||
const {HandlebarsApplicationMixin} = foundry.applications.api
|
const {HandlebarsApplicationMixin} = foundry.applications.api
|
||||||
const {ActorSheetV2} = foundry.applications.sheets
|
const {ActorSheetV2} = foundry.applications.sheets
|
||||||
|
|
@ -36,6 +37,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
|
||||||
openEmbeddedDocument: CharacterSheet.#openEmbeddedDocument,
|
openEmbeddedDocument: CharacterSheet.#openEmbeddedDocument,
|
||||||
openCultureDocument: CharacterSheet.#openCultureDocument,
|
openCultureDocument: CharacterSheet.#openCultureDocument,
|
||||||
openSpeciesDocument: CharacterSheet.#openSpeciesDocument,
|
openSpeciesDocument: CharacterSheet.#openSpeciesDocument,
|
||||||
|
openCombatAction: CharacterSheet.#openCombatAction,
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,6 +145,10 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
|
||||||
this.document.itemTypes["Species"]?.[0]?.sheet.render(true)
|
this.document.itemTypes["Species"]?.[0]?.sheet.render(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static #openCombatAction() {
|
||||||
|
new CombatActionDialog(this.document).render(true)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle form submission
|
* Handle form submission
|
||||||
* @this {AdvantageSheet}
|
* @this {AdvantageSheet}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
text-shadow: 0 0 0 red;
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
text-shadow: 0 0 4px red;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
text-shadow: 0 0 0 red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dsa41.dialog.combat {
|
||||||
|
|
||||||
|
.window-content > section {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
border-bottom: 0;
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
legend {
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
text-indent: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
|
||||||
|
height: 32px;
|
||||||
|
display: grid;
|
||||||
|
line-height: 32px;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
grid-template-columns: 32px 1fr 83px;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
border: 1px transparent;
|
||||||
|
margin: 8px;
|
||||||
|
gap: 0 8px;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: rgba(255, 140, 0, 0.2);
|
||||||
|
border: 1px solid orange;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.name-only {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.malus-and-mod {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
gap: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
flex: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
button.ready {
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -24,4 +24,5 @@
|
||||||
@use "organisms/species-sheet";
|
@use "organisms/species-sheet";
|
||||||
@use "organisms/profession-sheet";
|
@use "organisms/profession-sheet";
|
||||||
@use "organisms/xml-import-dialog";
|
@use "organisms/xml-import-dialog";
|
||||||
|
@use "organisms/combat-action-dialog";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<section>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Ziel auswählen</legend>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{{#each tokenDistances}}
|
||||||
|
<li class="token {{#if isSelected}}selected{{/if}}" data-action="selectTarget"
|
||||||
|
data-target-id="{{this.id}}"><img src="{{this.token.texture.src}}"
|
||||||
|
style="width: 32px; height: 32px"/><span>{{this.actor.name}}</span><span>({{this.d}} {{../distanceUnit}}
|
||||||
|
)</span></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Waffe auswählen</legend>
|
||||||
|
<ul>
|
||||||
|
{{#each weapons}}
|
||||||
|
<li class="{{#if isSelected}}selected{{/if}}" data-action="selectWeaponAndSkill"
|
||||||
|
data-weapon-id="{{this.weaponId}}" data-skill-id="{{this.skillId}}"><img src="{{this.img}}"
|
||||||
|
style="width: 32px; height: 32px"/><span>{{this.name}}
|
||||||
|
({{this.skillName}})</span><span>{{#if this.combatStatistics}}
|
||||||
|
(AT: {{this.combatStatistics.at}} PA: {{this.combatStatistics.pa}}){{/if}}</span></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Manöver auswählen</legend>
|
||||||
|
<ul>
|
||||||
|
{{#each maneuver}}
|
||||||
|
<li class="{{#if isSelected}}selected{{/if}} name-only" data-action="selectManeuver"
|
||||||
|
data-maneuver-id="{{this.id}}" class="{{this.source}}">{{this.name}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Erschwernisse und Ansagen</legend>
|
||||||
|
<div class="malus-and-mod">
|
||||||
|
<label>Umstände
|
||||||
|
<input name="malus" type="number">
|
||||||
|
</label>
|
||||||
|
<label>Ansage
|
||||||
|
<input name="mod" type="number" {{disabled (not canMod)}}>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button {{#if ready}}class="ready"{{/if}} type="submit"><i class="fa-solid fa-swords"></i>Angreifen <span
|
||||||
|
class="value"></span></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="player-action {{this.source}} {{this.type}}">
|
<div class="player-action {{this.source}} {{this.type}}" data-action="openCombatAction">
|
||||||
|
|
||||||
<span class="name">{{this.name}}</span>
|
<span class="name">{{this.name}}</span>
|
||||||
<span class="time">{{this.cost}}</span>
|
<span class="time">{{this.cost}}</span>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue