Ajout: Information de distance

This commit is contained in:
2025-12-11 03:12:49 +01:00
parent 697247e931
commit 4f4e00b298
5 changed files with 117 additions and 60 deletions
+3
View File
@@ -5,9 +5,12 @@
- Corrections diverses
- l'encombrement total est affiché avec deux décimales
- le poids des monnaies est de nouveau décimal
- Fenêtre de jets V2
- affichage des ajustements dans la fenêtre d'attaque avec arme à distance
- Corrections liées à Foundry v12/v13
- Fix ajout de notes dans le journal par un joueur (/chrono, ou clic sur le libelé de l'heure dans l'horloge)
## 13.0.21 - La folie d'Illysis
- Jets d'appréciation et de moral
+63
View File
@@ -0,0 +1,63 @@
import { Misc } from "../misc.js"
export class Distance {
static ajustements(token, defenderToken, arme){
const distance = Distance.distance(token, defenderToken)
const isVisible = Distance.isVisible(token, defenderToken)
const portee = Distance.ajustementPortee(distance, arme)
const taille = Distance.ajustementTaille(defenderToken.actor)
const activite = Distance.ajustementMouvement(defenderToken)
const total = Distance.diff(portee, taille, activite)
return {
isVisible: isVisible,
distance: distance,
portee: portee,
taille: taille,
activite: activite,
total: total
}
}
static distance(token, defenderToken) {
const ray = new foundry.canvas.geometry.Ray(
token.getCenterPoint(),
defenderToken.getCenterPoint()
)
return Number(canvas.grid.measureDistances([{ ray }], { gridSpaces: false }))
}
static isVisible(token, defenderToken) {
// TODO: regarder les StatusEffect aveuglé?
return canvas.effects.visibility.testVisibility(defenderToken.getCenterPoint(), { object: token })
}
static ajustementPortee(distance, arme) {
if (arme.system.portee_courte && distance <= arme.system.portee_courte) return { msg: "courte", diff: 0 };
if (arme.system.portee_moyenne && distance <= arme.system.portee_moyenne) return { msg: "moyenne", diff: -3 };
if (arme.system.portee_extreme && distance <= arme.system.portee_extreme) return { msg: "extrême", diff: -5 };
return { msg: "inatteignable", diff: -10 };
}
static ajustementTaille(actor) {
if (actor.isVehicule()) return { msg: "véhicule", diff: 0 }
const taille = actor.getCaracByName('TAILLE')?.value ?? 1;
if (taille <= 1) return { msg: "souris", diff: -8 };
if (taille <= 3) return { msg: "chat", diff: -4 };
if (taille <= 5) return { msg: "chien", diff: -2 };
if (taille <= 15) return { msg: "humanoïde", diff: 0 };
if (taille <= 20) return { msg: "ogre", diff: 2 };
return { msg: "gigantesque", diff: 4 };
}
static ajustementMouvement(defenderToken) {
if (defenderToken.actor?.getSurprise(true) != '') return { msg: "immobile (surprise)", diff: 0 };
if (defenderToken.inCombat) return { msg: "en mouvement (combat)", diff: -4 };
return { msg: "à déterminer (0 immobile, -3 actif, -4 en mouvement, -5 en zig-zag)", diff: -3 };
}
static diff(...ajustements){
return ajustements.map(it => it.diff).filter(d => !Number.isNaN(d)).reduce(Misc.sum(), 0)
}
}
+9 -50
View File
@@ -19,6 +19,7 @@ import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll/roll-constants.mjs"
import { OptionsAvancees, ROLL_DIALOG_V2 } from "./settings/options-avancees.js";
import { MappingCreatureArme } from "./item/mapping-creature-arme.mjs";
import { RollBasicParts } from "./roll/roll-basic-parts.mjs";
import { Distance } from "./combat/distance.mjs";
/* -------------------------------------------- */
const premierRoundInit = [
@@ -625,63 +626,22 @@ export class RdDCombat {
}
else {
const defenderToken = canvas.tokens.get(this.defenderTokenId)
const dist = this.distance(_token, defenderToken)
const isVisible = this.isVisible(_token, defenderToken)
const portee = this._ajustementPortee(dist, rollData.arme)
const taille = this._ajustementTaille(this.defender)
const activite = this._ajustementMouvement(this.defender)
const total = [portee, taille, activite].map(it => it.diff).filter(d => !Number.isNaN(d)).reduce(Misc.sum(), 0)
ChatMessage.create({
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-info-distance.hbs', {
const info = foundry.utils.mergeObject(
{
rollData: rollData,
attacker: _token,
isVisible: isVisible,
defender: defenderToken,
distance: dist,
portee: portee,
taille: taille,
activite: activite,
total: total
}),
},
Distance.ajustements(_token, defenderToken, rollData.arme)
)
ChatMessage.create({
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-info-distance.hbs', info),
whisper: ChatUtility.getGMs()
})
}
}
}
isVisible(token, defenderToken) {
return canvas.effects.visibility.testVisibility(defenderToken.center, { object: token })
}
distance(token, defenderToken) {
return Number(canvas.grid.measureDistances([{ ray: new Ray(token.center, defenderToken.center) }], { gridSpaces: false })).toFixed(1);
}
_ajustementPortee(dist, arme) {
if (dist <= arme.system.portee_courte) return { msg: "courte", diff: 0 };
if (dist <= arme.system.portee_moyenne) return { msg: "moyenne", diff: -3 };
if (dist <= arme.system.portee_extreme) return { msg: "extrême", diff: -5 };
return { msg: "inatteignable", diff: -10 };
}
_ajustementTaille(actor) {
if (actor.isVehicule()) return { msg: "véhicule", diff: 0 }
const taille = actor.getCaracByName('TAILLE')?.value ?? 1;
if (taille <= 1) return { msg: "souris", diff: -8 };
if (taille <= 3) return { msg: "chat", diff: -4 };
if (taille <= 5) return { msg: "chien", diff: -2 };
if (taille <= 15) return { msg: "humanoïde", diff: 0 };
if (taille <= 20) return { msg: "ogre", diff: 2 };
return { msg: "gigantesque", diff: 4 };
}
_ajustementMouvement(defender) {
if (defender.getSurprise(true) != '') return { msg: "immobile (surprise)", diff: 0 };
if (game.combat?.combatants.find(it => it.actorId == defender.id)) return { msg: "en mouvement (combat)", diff: -4 };
return { msg: "à déterminer (0 immobile, -3 actif, -4 en mouvement, -5 en zig-zag)", diff: -3 };
}
async attaqueV2() {
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
return
@@ -699,7 +659,6 @@ export class RdDCombat {
}
async doRollAttaque(rollData, callbacks = []) {
// TODO V2 await this.proposerAjustementTirLancer(rollData)
return await RollDialog.create(rollData, {
onRollDone: RollDialog.onRollDoneClose,
callbacks: [
@@ -740,7 +699,7 @@ export class RdDCombat {
}
_socketSendMessageDefenseV2(paramChatDefense) {
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_defense_v2", data: {paramChatDefense} })
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_defense_v2", data: { paramChatDefense } })
}
static async onMsgDefenseV2(msg) {
+26 -9
View File
@@ -1,3 +1,4 @@
import { Distance } from "../combat/distance.mjs"
import { RDD_CONFIG } from "../constants.js"
import { ATTAQUE_TYPE_MELEE } from "../item/arme.js"
import { RdDBonus } from "../rdd-bonus.js"
@@ -30,7 +31,7 @@ export class RollPartAttaque extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const attaques = rollData.active.actor.listAttaques()
refs.all = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData.active.actor))
refs.all = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData))
this.filterAttaquesEmpoignade(rollData)
refs.tactiques = TACTIQUES
if (refs.attaques.length > 0) {
@@ -64,13 +65,22 @@ export class RollPartAttaque extends RollPartSelect {
choices(refs) { return refs.attaques }
static $extractAttaque(attaque, actor) {
static $extractAttaque(attaque, rollData) {
attaque.key = `${attaque.action}::${attaque.label}`
attaque.tactique = TACTIQUES[0]
attaque.initialDiff = attaque.comp?.system.default_diffLibre ?? 0
if (RollPartAttaque.isAttaqueDistance(attaque)) {
attaque.distance = Distance.ajustements(rollData.active.token,
rollData.opponent.token,
attaque.arme)
}
return attaque
}
static isAttaqueDistance(attaque) {
return ['(lancer)', '(tir)'].includes(attaque.main)
}
prepareContext(rollData) {
this.filterAttaquesEmpoignade(rollData)
const current = this.getCurrent(rollData)
@@ -102,20 +112,22 @@ export class RollPartAttaque extends RollPartSelect {
}
async _onRender(rollDialog, context, options) {
const inputDiff = rollDialog.element.querySelector(`roll-section[name="diff"] input[name="diff"]`)
const selectAttaque = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-attaque"]`)
const selectTactique = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-tactique"]`)
const checkMortalite = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="check-mortalite"]`)
const utiliserDagueEmpoignade = rollDialog.element.querySelector(`roll-section[name="${this.code}"] a.utiliser-dague-empoignade`)
const current = this.getCurrent(rollDialog.rollData)
const rollData = rollDialog.rollData
const current = this.getCurrent(rollData)
selectAttaque.addEventListener("change", e => {
selectAttaque?.addEventListener("change", e => {
const selectOptions = e.currentTarget.options
const index = selectOptions.selectedIndex
this.$selectAttaque(rollDialog.rollData, selectOptions[index]?.value)
this.$selectAttaque(rollData, selectOptions[index]?.value)
rollDialog.render()
})
selectTactique.addEventListener("change", e => {
selectTactique?.addEventListener("change", e => {
const selectOptions = e.currentTarget.options
const index = selectOptions.selectedIndex
current.tactique = RdDBonus.find(selectOptions[index]?.value)
@@ -128,9 +140,11 @@ export class RollPartAttaque extends RollPartSelect {
})
utiliserDagueEmpoignade?.addEventListener("click", e => {
e.preventDefault()
const rollData = rollDialog.rollData
this.utiliserDagueEmpoignade(rollData)
})
inputDiff?.addEventListener("input", e => {
this.getRefs(rollData).all.forEach(attaque => attaque.initialDiff = undefined)
})
}
utiliserDagueEmpoignade(rollData) {
@@ -153,8 +167,11 @@ export class RollPartAttaque extends RollPartSelect {
case PART_CARAC: return part.filterCaracs(rollData, [current.carac.key])
case PART_COMP: return part.filterComps(rollData, [current.comp.name])
case PART_DIFF: {
if (current.initialDiff) {
part.setDiff(rollData, { type: DIFF.ATTAQUE, value: current.initialDiff })
if (RollPartAttaque.isAttaqueDistance(current)) {
part.setDiff(rollData, { type: DIFF.DEFAUT })
}
else {
part.setDiff(rollData, { type: DIFF.ATTAQUE, diff: current.initialDiff })
current.initialDiff = undefined
}
break
+16 -1
View File
@@ -63,7 +63,22 @@
</span>
</subline>
{{/if}}
{{#if current.distance}}
<subline>
<span>
<strong>Attaque à distance</strong> {{current.main}} proposé de <strong>{{current.distance.total}}</strong> à valider par le Gardien
</span>
</subline>
<subline>
<ul>
<li>La cible est à une distance indicative de {{numberFormat current.distance.distance decimals=1}} mètres.</li>
<li>Avec les murs et la lumière, la cible
{{#if current.distance.isVisible}}est{{else}}n'est pas{{/if}} visible.
</li>
<li>Portée {{current.distance.portee.msg}} : {{current.distance.portee.diff}}</li>
<li>De taille {{current.distance.taille.msg}}: {{current.distance.taille.diff}}</li>
<li>Mouvement {{current.distance.activite.msg}}: {{current.distance.activite.diff}}</li>
</ul>
</subline>
{{/if}}
</roll-part-detail>