Corrections diverses autout du combat
This commit is contained in:
@@ -13,6 +13,8 @@
|
||||
|
||||
import { SYSTEM } from "../config/system.mjs"
|
||||
|
||||
const WEAPON_DAMAGE_PRIORITY = { "0": 0, "1": 1, "2": 2, X: 3 }
|
||||
|
||||
export default class CelestopolCharacter extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields
|
||||
@@ -218,6 +220,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
|
||||
return CelestopolRoll.prompt({
|
||||
actorId: this.parent.id,
|
||||
actorUuid: this.parent.uuid,
|
||||
actorName: this.parent.name,
|
||||
actorImage: this.parent.img,
|
||||
statId,
|
||||
@@ -249,6 +252,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
|
||||
return CelestopolRoll.prompt({
|
||||
actorId: this.parent.id,
|
||||
actorUuid: this.parent.uuid,
|
||||
actorName: this.parent.name,
|
||||
actorImage: this.parent.img,
|
||||
statId,
|
||||
@@ -270,21 +274,47 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
/**
|
||||
* Collecte les cibles de combat sur la scène active.
|
||||
* Pour un PJ attaquant, seules les cibles PNJ présentes sur la scène sont proposées.
|
||||
* @returns {Array<{id:string, name:string, corps:number}>}
|
||||
* @param {object} options
|
||||
* @param {boolean} [options.onlyRanged=false]
|
||||
* @param {boolean} [options.fallbackToAll=false]
|
||||
* @returns {Array<{id:string, uuid:string, name:string, corps:number, weaponName?:string, weaponDegats?:string}>}
|
||||
*/
|
||||
_getCombatTargets() {
|
||||
_getCombatTargets({ onlyRanged = false, fallbackToAll = false } = {}) {
|
||||
const getBestRangedWeapon = actor => {
|
||||
const rangedWeapons = actor.itemTypes?.weapon?.filter(item => item.system.type === "distance") ?? []
|
||||
if (!rangedWeapons.length) return null
|
||||
return rangedWeapons.reduce((best, item) => {
|
||||
if (!best) return item
|
||||
const bestPriority = WEAPON_DAMAGE_PRIORITY[best.system.degats] ?? -1
|
||||
const itemPriority = WEAPON_DAMAGE_PRIORITY[item.system.degats] ?? -1
|
||||
if (itemPriority !== bestPriority) return itemPriority > bestPriority ? item : best
|
||||
return item.name.localeCompare(best.name) < 0 ? item : best
|
||||
}, null)
|
||||
}
|
||||
|
||||
const toEntry = actor => ({
|
||||
id: actor.id,
|
||||
uuid: actor.uuid,
|
||||
name: actor.name,
|
||||
corps: actor.system.stats?.corps?.res ?? 0,
|
||||
...(onlyRanged ? (() => {
|
||||
const weapon = getBestRangedWeapon(actor)
|
||||
return weapon ? {
|
||||
weaponName: weapon.name,
|
||||
weaponDegats: weapon.system.degats,
|
||||
} : {}
|
||||
})() : {}),
|
||||
})
|
||||
const sceneTokens = canvas?.scene?.isView ? (canvas.tokens?.placeables ?? []) : []
|
||||
return [...new Map(sceneTokens
|
||||
const targets = [...new Map(sceneTokens
|
||||
.filter(t => t.actor?.type === "npc" && t.actor.id !== this.parent.id)
|
||||
.filter(t => !onlyRanged || getBestRangedWeapon(t.actor))
|
||||
.map(t => {
|
||||
const actor = t.actor
|
||||
return [actor.id, toEntry(actor)]
|
||||
return [actor.uuid, toEntry(actor)]
|
||||
})).values()]
|
||||
if (!targets.length && onlyRanged && fallbackToAll) return this._getCombatTargets()
|
||||
return targets
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,6 +334,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
|
||||
return CelestopolRoll.prompt({
|
||||
actorId: this.parent.id,
|
||||
actorUuid: this.parent.uuid,
|
||||
actorName: this.parent.name,
|
||||
actorImage: this.parent.img,
|
||||
statId: "corps",
|
||||
@@ -326,6 +357,40 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Lance une attaque de mêlée à mains nues.
|
||||
* @returns {Promise<import("../documents/roll.mjs").CelestopolRoll|null>}
|
||||
*/
|
||||
async rollUnarmedAttack() {
|
||||
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
||||
const echauffouree = this.stats.corps.echauffouree
|
||||
if (!echauffouree) return null
|
||||
|
||||
return CelestopolRoll.prompt({
|
||||
actorId: this.parent.id,
|
||||
actorUuid: this.parent.uuid,
|
||||
actorName: this.parent.name,
|
||||
actorImage: this.parent.img,
|
||||
statId: "corps",
|
||||
skillId: "echauffouree",
|
||||
statLabel: SYSTEM.STATS.corps.label,
|
||||
skillLabel: SYSTEM.SKILLS.corps.echauffouree.label,
|
||||
skillValue: echauffouree.value,
|
||||
woundMalus: this.getWoundMalus(),
|
||||
armorMalus: this.getArmorMalusForRoll("corps", "echauffouree"),
|
||||
woundLevel: this.blessures.lvl,
|
||||
rollMoonDie: this.prefs.rollMoonDie ?? false,
|
||||
destGaugeFull: this.destin.lvl > 0,
|
||||
fortuneValue: this.attributs.fortune.value,
|
||||
isCombat: true,
|
||||
isRangedDefense: false,
|
||||
weaponType: "melee",
|
||||
weaponName: game.i18n.localize("CELESTOPOL.Combat.unarmedAttack"),
|
||||
weaponDegats: "0",
|
||||
availableTargets: this._getCombatTargets(),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Lance un jet de défense contre une attaque à distance (test Mobilité vs Corps PNJ).
|
||||
* Succès → esquive réussie.
|
||||
@@ -342,6 +407,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
|
||||
return CelestopolRoll.prompt({
|
||||
actorId: this.parent.id,
|
||||
actorUuid: this.parent.uuid,
|
||||
actorName: this.parent.name,
|
||||
actorImage: this.parent.img,
|
||||
statId: "corps",
|
||||
@@ -363,4 +429,38 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
availableTargets: this._getCombatTargets(),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Lance une esquive d'attaque à distance sans dépendre d'une arme possédée par le PJ.
|
||||
* @returns {Promise<import("../documents/roll.mjs").CelestopolRoll|null>}
|
||||
*/
|
||||
async rollRangedDefenseBase() {
|
||||
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
||||
const mobilite = this.stats.corps.mobilite
|
||||
if (!mobilite) return null
|
||||
|
||||
return CelestopolRoll.prompt({
|
||||
actorId: this.parent.id,
|
||||
actorUuid: this.parent.uuid,
|
||||
actorName: this.parent.name,
|
||||
actorImage: this.parent.img,
|
||||
statId: "corps",
|
||||
skillId: "mobilite",
|
||||
statLabel: SYSTEM.STATS.corps.label,
|
||||
skillLabel: SYSTEM.SKILLS.corps.mobilite.label,
|
||||
skillValue: mobilite.value,
|
||||
woundMalus: this.getWoundMalus(),
|
||||
armorMalus: this.getArmorMalusForRoll("corps", "mobilite"),
|
||||
woundLevel: this.blessures.lvl,
|
||||
rollMoonDie: this.prefs.rollMoonDie ?? false,
|
||||
destGaugeFull: this.destin.lvl > 0,
|
||||
fortuneValue: this.attributs.fortune.value,
|
||||
isCombat: true,
|
||||
isRangedDefense: true,
|
||||
weaponType: "distance",
|
||||
weaponName: game.i18n.localize("CELESTOPOL.Combat.rangedAttack"),
|
||||
weaponDegats: "0",
|
||||
availableTargets: this._getCombatTargets({ onlyRanged: true, fallbackToAll: true }),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,7 @@ export default class CelestopolNPC extends foundry.abstract.TypeDataModel {
|
||||
|
||||
return CelestopolRoll.prompt({
|
||||
actorId: this.parent.id,
|
||||
actorUuid: this.parent.uuid,
|
||||
actorName: this.parent.name,
|
||||
actorImage: this.parent.img,
|
||||
statId,
|
||||
@@ -127,4 +128,95 @@ export default class CelestopolNPC extends foundry.abstract.TypeDataModel {
|
||||
async rollResistance(statId) {
|
||||
return this.roll(statId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Collecte les cibles protagonistes de la scène active pour les jets de combat PNJ.
|
||||
* @returns {Array<{id:string, name:string, corps:number}>}
|
||||
*/
|
||||
_getCombatTargets() {
|
||||
const toEntry = actor => ({
|
||||
id: actor.id,
|
||||
uuid: actor.uuid,
|
||||
name: actor.name,
|
||||
corps: actor.system.stats?.corps?.res ?? 0,
|
||||
})
|
||||
const sceneTokens = canvas?.scene?.isView ? (canvas.tokens?.placeables ?? []) : []
|
||||
return [...new Map(sceneTokens
|
||||
.filter(t => t.actor?.type === "character" && t.actor.id !== this.parent.id)
|
||||
.map(t => {
|
||||
const actor = t.actor
|
||||
return [actor.id, toEntry(actor)]
|
||||
})).values()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Lance une attaque PNJ avec une arme.
|
||||
* Le test utilise le domaine Corps et transmet explicitement les dégâts de l'arme.
|
||||
* @param {string} itemId
|
||||
* @returns {Promise<import("../documents/roll.mjs").CelestopolRoll|null>}
|
||||
*/
|
||||
async rollAttack(itemId) {
|
||||
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
||||
const item = this.parent.items.get(itemId)
|
||||
if (!item || item.type !== "weapon") return null
|
||||
|
||||
return CelestopolRoll.prompt({
|
||||
actorId: this.parent.id,
|
||||
actorUuid: this.parent.uuid,
|
||||
actorName: this.parent.name,
|
||||
actorImage: this.parent.img,
|
||||
statId: "corps",
|
||||
skillId: null,
|
||||
statLabel: SYSTEM.STATS.corps.label,
|
||||
skillLabel: "CELESTOPOL.Combat.attack",
|
||||
skillValue: this.stats.corps.res,
|
||||
woundMalus: this.getWoundMalus(),
|
||||
armorMalus: this.getArmorMalusForRoll("corps"),
|
||||
woundLevel: this.blessures.lvl,
|
||||
rollMoonDie: false,
|
||||
destGaugeFull: false,
|
||||
fortuneValue: 0,
|
||||
isCombat: true,
|
||||
isRangedDefense: false,
|
||||
weaponType: item.system.type,
|
||||
weaponName: item.name,
|
||||
weaponDegats: item.system.degats,
|
||||
availableTargets: this._getCombatTargets(),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Lance un jet de tir/esquive PNJ avec une arme à distance.
|
||||
* @param {string} itemId
|
||||
* @returns {Promise<import("../documents/roll.mjs").CelestopolRoll|null>}
|
||||
*/
|
||||
async rollRangedDefense(itemId) {
|
||||
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
||||
const item = this.parent.items.get(itemId)
|
||||
if (!item || item.type !== "weapon" || item.system.type !== "distance") return null
|
||||
|
||||
return CelestopolRoll.prompt({
|
||||
actorId: this.parent.id,
|
||||
actorUuid: this.parent.uuid,
|
||||
actorName: this.parent.name,
|
||||
actorImage: this.parent.img,
|
||||
statId: "corps",
|
||||
skillId: null,
|
||||
statLabel: SYSTEM.STATS.corps.label,
|
||||
skillLabel: "CELESTOPOL.Combat.rangedDefenseTitle",
|
||||
skillValue: this.stats.corps.res,
|
||||
woundMalus: this.getWoundMalus(),
|
||||
armorMalus: this.getArmorMalusForRoll("corps"),
|
||||
woundLevel: this.blessures.lvl,
|
||||
rollMoonDie: false,
|
||||
destGaugeFull: false,
|
||||
fortuneValue: 0,
|
||||
isCombat: true,
|
||||
isRangedDefense: true,
|
||||
weaponType: "distance",
|
||||
weaponName: item.name,
|
||||
weaponDegats: item.system.degats,
|
||||
availableTargets: this._getCombatTargets(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user