16 Commits

Author SHA1 Message Date
52877e3a68 New combat management and various improvments
All checks were successful
Release Creation / build (release) Successful in 48s
2026-01-19 23:22:32 +01:00
a06dfa0ae9 Update skills/shield DR
All checks were successful
Release Creation / build (release) Successful in 1m8s
2026-01-12 14:27:29 +01:00
0836cada75 Re-org folders and enhance minor CSS stuff 2026-01-04 09:21:21 +01:00
61ed1597e7 Add damage management and DR for monsters also
All checks were successful
Release Creation / build (release) Successful in 1m48s
2025-12-19 15:41:27 +01:00
96062c6fd9 Roll damages and so on
All checks were successful
Release Creation / build (release) Successful in 56s
2025-12-14 21:18:00 +01:00
f6b35536de Manage DR and damage roll 2025-12-14 20:48:33 +01:00
7d27562bb4 Fix DR and defense values
All checks were successful
Release Creation / build (release) Successful in 1m7s
2025-12-08 11:54:36 +01:00
64f2efdcb9 Fix last HD Roll
All checks were successful
Release Creation / build (release) Successful in 1m35s
2025-11-07 07:58:58 +01:00
66f7aade25 Fix jog + update compendiums
All checks were successful
Release Creation / build (release) Successful in 52s
2025-10-17 20:06:49 +02:00
8f682a1458 Auto-publish package on releas 2025-10-01 17:26:29 +02:00
fa3054f24b Some granted dice/favor fixes
All checks were successful
Release Creation / build (release) Successful in 2m48s
2025-10-01 17:17:33 +02:00
59a891630e Fix tooltip
All checks were successful
Release Creation / build (release) Successful in 38s
2025-09-20 09:34:13 +02:00
35b88b3914 Poison/Contagion fixes again
All checks were successful
Release Creation / build (release) Successful in 42s
2025-09-19 23:32:15 +02:00
cb8bcfd9ea Ranged defense fixes again
All checks were successful
Release Creation / build (release) Successful in 43s
2025-09-19 22:30:26 +02:00
eedce1a498 Latest fixes
All checks were successful
Release Creation / build (release) Successful in 46s
2025-09-17 07:47:40 +02:00
76a99fe33f Various fixes 2025-09-16 23:49:02 +02:00
68 changed files with 6767 additions and 1060 deletions

View File

@@ -1,6 +1,6 @@
name: Release Creation name: Release Creation
on: on:
release: release:
types: [published] types: [published]
@@ -8,45 +8,56 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner." - run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner."
#- uses: actions/checkout@v3
- uses: RouxAntoine/checkout@v3.5.4
# get part of the tag after the `v` #- uses: actions/checkout@v3
- name: Extract tag version number - uses: RouxAntoine/checkout@v3.5.4
id: get_version
uses: battila7/get-version-action@v2
# Substitute the Manifest and Download URLs in the module.json # get part of the tag after the `v`
- name: Substitute Manifest and Download Links For Versioned Ones - name: Extract tag version number
id: sub_manifest_link_version id: get_version
uses: microsoft/variable-substitution@v1 uses: battila7/get-version-action@v2
with:
files: 'system.json'
env:
version: ${{steps.get_version.outputs.version-without-v}}
url: https://www.uberwald.me/gitea/${{gitea.repository}}
manifest: https://www.uberwald.me/gitea/public/${{gitea.repository}}/releases/download/latest/system.json
download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-lethal-fantasy-${{github.event.release.tag_name}}.zip
# Create a zip file with all files required by the module to add to the release
- run: |
apt update -y
apt install -y zip
- run: zip -r ./fvtt-lethal-fantasy-${{github.event.release.tag_name}}.zip system.json README.md LICENSE assets/ css/ lang/ module/ packs-system/ templates/ lethal-fantasy.mjs # Substitute the Manifest and Download URLs in the module.json
- name: Substitute Manifest and Download Links For Versioned Ones
- name: setup go id: sub_manifest_link_version
uses: https://github.com/actions/setup-go@v4 uses: microsoft/variable-substitution@v1
with: with:
go-version: '>=1.20.1' files: "system.json"
env:
- name: Use Go Action version: ${{steps.get_version.outputs.version-without-v}}
id: use-go-action url: https://www.uberwald.me/gitea/${{gitea.repository}}
uses: https://gitea.com/actions/release-action@main manifest: https://www.uberwald.me/gitea/public/${{gitea.repository}}/releases/download/latest/system.json
with: download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-lethal-fantasy-${{github.event.release.tag_name}}.zip
files: |-
./fvtt-lethal-fantasy-${{github.event.release.tag_name}}.zip # Create a zip file with all files required by the module to add to the release
system.json - run: |
api_key: '${{secrets.ALLOW_PUSH_RELEASE}}' apt update -y
apt install -y zip
- run: zip -r ./fvtt-lethal-fantasy-${{github.event.release.tag_name}}.zip system.json README.md LICENSE assets/ css/ lang/ module/ packs-system/ templates/ lethal-fantasy.mjs
- name: setup go
uses: https://github.com/actions/setup-go@v4
with:
go-version: ">=1.20.1"
- name: Use Go Action
id: use-go-action
uses: https://gitea.com/actions/release-action@main
with:
files: |-
./fvtt-lethal-fantasy-${{github.event.release.tag_name}}.zip
system.json
api_key: "${{secrets.ALLOW_PUSH_RELEASE}}"
- name: Publish to Foundry server
uses: djlechuck/foundryvtt-publish-package-action@v1
with:
token: ${{ secrets.FOUNDRY_PUBLISH_KEY }}
id: "fvtt-lethal-fantasy"
version: ${{github.event.release.tag_name}}
manifest: "https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/system.json"
notes: "https://www.uberwald.me/gitea/public/fvtt-lethal-fantasy/raw/branch/main/changelog.md"
compatibility-minimum: "13"
compatibility-verified: "13"

5
changelog.md Normal file
View File

@@ -0,0 +1,5 @@
## v13.0.12
- Fix favor/disfavor
- Fix granted dice
- Cosmetic fixes

File diff suppressed because it is too large Load Diff

View File

@@ -282,6 +282,9 @@
}, },
"Label": { "Label": {
"agility": "Dexterity", "agility": "Dexterity",
"applyDamage": "Apply damage to:",
"selectTarget": "Select target for attack:",
"rollDamage": "Roll Damage",
"gotoToken": "Go to token", "gotoToken": "Go to token",
"combatAction": "Combat action", "combatAction": "Combat action",
"currentAction": "Current ongoing action", "currentAction": "Current ongoing action",
@@ -304,8 +307,10 @@
"grantedAttackDice": "Granted attack", "grantedAttackDice": "Granted attack",
"grantedDamageDice": "Granted damage", "grantedDamageDice": "Granted damage",
"grantedDefenseDice": "Granted defense", "grantedDefenseDice": "Granted defense",
"damageResistance": "Damage resistance", "damageResistance": "DR",
"damageResistanceShort": "DR", "damageResistanceShort": "DR",
"shieldDamageReduction": "Shield DR",
"shieldDefenseDice": "Shield dice",
"stealth": "Stealth", "stealth": "Stealth",
"progressionDice": "Progression/Lethargy dice", "progressionDice": "Progression/Lethargy dice",
"rollProgressionCount": "Roll progression count", "rollProgressionCount": "Roll progression count",
@@ -421,7 +426,9 @@
"monster-damage": "Monster damage", "monster-damage": "Monster damage",
"monster-defense": "Monster defense", "monster-defense": "Monster defense",
"weapons": "Weapons", "weapons": "Weapons",
"wis": "WIS" "wis": "WIS",
"weapon-damage-medium": "Weapon damage medium",
"weapon-damage-small": "Weapon damage small"
}, },
"Miracle": { "Miracle": {
"FIELDS": { "FIELDS": {
@@ -553,7 +560,7 @@
"label": "Min" "label": "Min"
} }
}, },
"damagereduction": { "damageReduction": {
"label": "Damage reduction" "label": "Damage reduction"
}, },
"defense": { "defense": {
@@ -856,6 +863,31 @@
"melee": "Melee", "melee": "Melee",
"ranged": "Ranged" "ranged": "Ranged"
} }
},
"Dialog": {
"applyDamageTo": "Apply damage to",
"weapon": "Weapon",
"totalDamage": "Total Damage",
"damageReduction": "Damage Reduction",
"armorDR": "Armor DR",
"shieldDR": "Shield DR",
"totalDR": "Total DR",
"selectOption": "Select damage application option",
"noDR": "No DR",
"withArmor": "With Armor DR only",
"withAll": "With Armor + Shield DR",
"damage": "damage"
},
"DamageApplied": {
"subtitle": "suffered damage",
"damageDealt": "Damage dealt",
"from": "from"
},
"ProgressionMessage": {
"canAct": "ready to act!",
"cannotAct": "cannot act this second",
"diceResult": "Dice result",
"progressionCount": "Progression count:"
} }
}, },
"TYPES": { "TYPES": {

View File

@@ -110,6 +110,9 @@ function preLocalizeConfig() {
Hooks.once("ready", function () { Hooks.once("ready", function () {
console.info("LETHAL FANTASY | Ready") console.info("LETHAL FANTASY | Ready")
// Initialiser la table des résultats D30
documents.D30Roll.initialize()
if (!SYSTEM.DEV_MODE) { if (!SYSTEM.DEV_MODE) {
registerWorldCount("lethalFantasy") registerWorldCount("lethalFantasy")
} }
@@ -152,12 +155,422 @@ Hooks.on(hookName, (message, html, data) => {
}) })
} }
} }
// Gestion du survol et du clic sur les boutons de dégâts pour les GMs
if (game.user.isGM) {
// Show damage buttons only for GM
$(html).find(".li-apply-wounds").each((i, btn) => {
btn.style.display = "block"
})
$(html).find(".apply-wounds-btn").hover(
function (event) {
// Mouse enter - select the token and pan to it
let combatantId = $(this).data("combatant-id")
if (combatantId && game.combat) {
let combatant = game.combat.combatants.get(combatantId)
if (combatant?.token) {
let token = canvas.tokens.get(combatant.token.id)
if (token) {
token.control({ releaseOthers: true })
canvas.animatePan(token.center)
}
}
}
},
function (event) {
// Mouse leave - release selection
canvas.tokens.releaseAll()
}
)
$(html).find(".apply-wounds-btn").click((event) => {
LethalFantasyUtils.applyDamage(message, event)
})
}
// Gestion du survol et du clic sur les boutons de défense
$(html).find(".request-defense-btn").hover(
function (event) {
// Mouse enter - select the token and pan to it
let tokenId = $(this).data("token-id")
if (tokenId) {
let token = canvas.tokens.get(tokenId)
if (token) {
token.control({ releaseOthers: true })
canvas.animatePan(token.center)
}
}
},
function (event) {
// Mouse leave - release selection
canvas.tokens.releaseAll()
}
)
// Gestionnaire pour les boutons de demande de défense
$(html).find(".request-defense-btn").off("click").on("click", (event) => {
event.preventDefault()
event.stopPropagation()
const button = $(event.currentTarget)
const combatantId = button.data("combatant-id")
const tokenId = button.data("token-id")
// Récupérer le combattant soit du combat, soit directement du token
let combatant = null
let token = null
if (game.combat && combatantId) {
combatant = game.combat.combatants.get(combatantId)
}
// Si pas de combattant trouvé, chercher le token directement
if (!combatant && tokenId) {
token = canvas.tokens.get(tokenId)
if (token) {
// Créer un pseudo-combattant avec les infos du token
combatant = {
actor: token.actor,
name: token.name,
token: token,
actorId: token.actorId
}
}
}
if (!combatant) return
// Récupérer les informations de l'attaquant depuis le message
const attackerName = message.rolls[0]?.actorName || "Unknown"
const attackerId = message.rolls[0]?.actorId
const weaponName = message.rolls[0]?.rollName || "weapon"
const attackRoll = message.rolls[0]?.rollTotal || 0
const defenderName = combatant.name
const attackWeaponId = message.rolls[0]?.rollTarget?.weapon?.id || message.rolls[0]?.rollTarget?.weapon?._id
const attackRollType = message.rolls[0]?.type
const attackRollKey = message.rolls[0]?.rollTarget?.rollKey
// Préparer le message de demande de défense
const defenseMsg = {
type: "requestDefense",
attackerName: attackerName,
attackerId: attackerId,
defenderName: defenderName,
weaponName: weaponName,
attackRoll: attackRoll,
attackWeaponId: attackWeaponId,
attackRollType: attackRollType,
attackRollKey: attackRollKey,
combatantId: combatantId,
tokenId: tokenId
}
// Envoyer le message socket à l'utilisateur contrôlant le combatant
const owners = game.users.filter(u =>
combatant.actor.testUserPermission(u, "OWNER")
)
// Récupérer l'acteur attaquant pour vérifier qui l'a lancé
const attacker = game.actors.get(attackerId)
const attackerOwners = attacker ? game.users.filter(u => attacker.testUserPermission(u, "OWNER")).map(u => u.id) : []
let messageSent = false
owners.forEach(owner => {
// Ne pas afficher le dialogue à l'attaquant lui-même s'il contrôle aussi le défenseur
if (attackerOwners.includes(owner.id) && owner.id === game.user.id) {
// Ne rien faire - on ne veut pas que l'attaquant se défende contre lui-même
return
}
if (owner.id === game.user.id) {
// Si l'utilisateur actuel est le propriétaire du défenseur (mais pas l'attaquant), appeler directement
LethalFantasyUtils.showDefenseRequest({ ...defenseMsg, userId: owner.id })
messageSent = true
} else {
// Sinon, envoyer via socket
game.socket.emit(`system.${SYSTEM.id}`, {
...defenseMsg,
userId: owner.id
})
messageSent = true
}
})
// Notification pour l'attaquant
if (messageSent) {
ui.notifications.info(`Defense request sent to ${defenderName}'s controller`)
}
})
// Gestionnaire pour les boutons de jet de dégâts (armes et résultats de combat)
$(html).find(".damage-roll-btn, .roll-damage-btn").off("click").on("click", async (event) => {
event.preventDefault()
event.stopPropagation()
const button = $(event.currentTarget)
const weaponId = button.data("weapon-id")
const attackKey = button.data("attack-key")
let attackerId = button.data("attacker-id")
const defenderId = button.data("defender-id")
const damageType = button.data("damage-type")
const damageFormula = button.data("damage-formula")
const damageModifier = button.data("damage-modifier")
const isMonster = button.data("is-monster")
// Récupérer l'acteur (soit depuis le message, soit depuis attackerId)
let actor = attackerId ? game.actors.get(attackerId) : game.actors.get(message.rolls[0]?.actorId)
if (!actor) {
ui.notifications.error("Actor not found")
return
}
// Pour les boutons de résultat de combat (monster damage)
if (damageType === "monster" && attackKey) {
await actor.system.prepareMonsterRoll("monster-damage", attackKey, undefined, undefined, undefined, defenderId)
return
}
// Pour les monstres, utiliser prepareMonsterRoll
if (isMonster || actor.type === "monster") {
await actor.system.prepareMonsterRoll("monster-damage", weaponId, undefined, undefined, damageModifier)
return
}
// Pour les personnages, récupérer l'arme
const weapon = actor.items.get(weaponId)
if (!weapon) {
ui.notifications.error("Weapon not found")
return
}
// Lancer les dégâts avec la bonne méthode
const rollType = damageType === "small" ? "weapon-damage-small" : "weapon-damage-medium"
await actor.prepareRoll(rollType, weaponId, undefined, defenderId)
})
// Masquer les boutons de dommages dans les messages de résultat de combat si l'utilisateur n'est pas l'attaquant
$(html).find(".roll-damage-btn").each(function() {
const button = $(this)
const attackerId = button.data("attacker-id")
if (attackerId) {
const attacker = game.actors.get(attackerId)
// Masquer le bouton si l'utilisateur n'est pas GM et ne possède pas l'attaquant
if (!game.user.isGM && !attacker?.testUserPermission(game.user, "OWNER")) {
button.hide()
}
}
})
}) })
Hooks.on("getCombatTrackerEntryContext", (html, options) => { Hooks.on("getCombatTrackerEntryContext", (html, options) => {
LethalFantasyUtils.pushCombatOptions(html, options); LethalFantasyUtils.pushCombatOptions(html, options);
}); });
// Hook pour ajouter les données d'attaque au message de défense
Hooks.on("preCreateChatMessage", (message) => {
const rollType = message.rolls[0]?.options?.rollType
// Si c'est un message de défense et qu'on a des données en attente
if ((rollType === "weapon-defense" || rollType === "monster-defense") && game.lethalFantasy?.nextDefenseData) {
// Ajouter les données dans les flags du message
message.updateSource({
[`flags.${SYSTEM.id}.attackData`]: game.lethalFantasy.nextDefenseData
})
console.log("Added attack data to defense message:", game.lethalFantasy.nextDefenseData)
// Nettoyer
delete game.lethalFantasy.nextDefenseData
}
})
// Hook global pour gérer l'offre de Grit à l'attaquant après une défense
Hooks.on("createChatMessage", async (message) => {
const rollType = message.rolls[0]?.options?.rollType
console.log("Defense hook checking message, rollType:", rollType)
// Vérifier si c'est un message de défense
if (rollType !== "weapon-defense" && rollType !== "monster-defense") return
// Récupérer les données d'attaque depuis les flags
const attackData = message.flags?.[SYSTEM.id]?.attackData
console.log("Defense message confirmed, attackData:", attackData)
if (!attackData) {
console.log("No attack data found in message flags")
return
}
const { attackerId, attackRoll, attackerName, defenderName, attackWeaponId, attackRollType, attackRollKey, defenderId } = attackData
let defenseRoll = message.rolls[0]?.options?.rollTotal || message.rolls[0]?.total || 0
console.log("Processing defense:", { attackRoll, defenseRoll, attackerId, defenderId })
// Attendre l'animation 3D
if (game?.dice3d) {
await game.dice3d.waitFor3DAnimationByMessageID(message.id)
}
// Récupérer le défenseur et l'attaquant
const defender = game.actors.get(defenderId)
const attacker = game.actors.get(attackerId)
// Si le défenseur est un personnage qui perd, proposer Grit/Luck (seulement s'il a des points)
// Seulement si l'utilisateur actuel est le propriétaire du défenseur
let defenderHandledBonus = false
if (defender?.type === "character" && defenseRoll < attackRoll && !game.user.isGM && defender.isOwner) {
const hasGritOrLuck = (defender.system.grit.current > 0) || (defender.system.luck.current > 0)
if (hasGritOrLuck) {
const bonusRoll = await LethalFantasyUtils.offerGritLuckBonus(
defender,
attackRoll,
defenseRoll,
attackerName,
defenderName
)
if (bonusRoll > 0) {
defenseRoll += bonusRoll
}
}
defenderHandledBonus = true
}
let attackRollFinal = attackRoll
let attackerHandledBonus = false
// Si l'attaquant est un personnage qui perd et a du Grit
// Seulement si l'utilisateur actuel est le propriétaire de l'attaquant (pas le MJ)
if (attacker?.type === "character" && attackRollFinal <= defenseRoll && attacker.system.grit.current > 0) {
// Vérifier si l'utilisateur est un propriétaire non-GM de l'attaquant
const isAttackerOwner = !game.user.isGM && attacker.testUserPermission(game.user, "OWNER")
if (isAttackerOwner) {
console.log("Offering Grit to attacker")
const attackBonus = await LethalFantasyUtils.offerAttackerGritBonus(
attacker,
attackRollFinal,
defenseRoll,
attackerName,
defenderName
)
attackRollFinal += attackBonus
attackerHandledBonus = true
} else {
console.log("Not attacker owner or is GM, skipping Grit offer")
}
}
// Créer le message de comparaison - uniquement par le client qui a géré le dernier bonus
// Priorité: attaquant si il a géré le bonus, sinon défenseur si il a géré le bonus, sinon défenseur
const shouldCreateMessage = attackerHandledBonus || (!attackerHandledBonus && defenderHandledBonus) || (!attackerHandledBonus && !defenderHandledBonus && defender.isOwner)
if (shouldCreateMessage) {
console.log("Creating comparison message", { attackerHandledBonus, defenderHandledBonus, isDefenderOwner: defender.isOwner })
await LethalFantasyUtils.compareAttackDefense({
attackerName,
attackerId,
attackRoll: attackRollFinal,
attackWeaponId,
attackRollType,
attackRollKey,
defenderName,
defenderId,
defenseRoll
})
} else {
console.log("Skipping message creation", { attackerHandledBonus, defenderHandledBonus })
}
})
// Hook pour appliquer automatiquement les dégâts si une cible est définie
Hooks.on("createChatMessage", async (message) => {
// Vérifier si c'est un message de dégâts avec un defenderId
const defenderId = message.rolls[0]?.options?.defenderId
const isDamage = message.rolls[0]?.options?.rollData?.isDamage
console.log("Auto-damage hook:", { defenderId, isDamage, rollType: message.rolls[0]?.options?.rollType })
if (!defenderId || !isDamage) return
// Récupérer l'attaquant depuis le roll
const attackerId = message.rolls[0]?.options?.actorId
const attacker = attackerId ? game.actors.get(attackerId) : null
// Déterminer qui doit appliquer les dégâts :
// 1. Si l'attaquant a un propriétaire joueur, seul ce joueur applique
// 2. Si l'attaquant n'a que le MJ comme propriétaire (monstre), seul le MJ applique
const attackerOwners = attacker ? game.users.filter(u =>
!u.isGM && attacker.testUserPermission(u, "OWNER")
) : []
let shouldApplyDamage = false
if (attackerOwners.length > 0) {
// L'attaquant a des propriétaires joueurs, seul le premier propriétaire applique
shouldApplyDamage = attackerOwners[0].id === game.user.id
} else {
// L'attaquant n'a que le MJ, seul le MJ applique
shouldApplyDamage = game.user.isGM
}
if (!shouldApplyDamage) {
console.log("Auto-damage hook: Not responsible for applying damage, skipping")
return
}
console.log("Auto-damage hook: Applying damage as responsible user")
// Attendre l'animation 3D avant d'appliquer les dégâts
if (game?.dice3d) {
await game.dice3d.waitFor3DAnimationByMessageID(message.id)
}
// Récupérer le défenseur
const defender = game.actors.get(defenderId)
if (!defender) {
console.warn("Defender not found:", defenderId)
return
}
// Récupérer les dégâts (utiliser rollTotal qui contient le total calculé)
const damageTotal = message.rolls[0]?.options?.rollTotal || message.rolls[0]?.total || 0
const weaponName = message.rolls[0]?.options?.rollName || "Unknown Weapon"
const attackerName = message.rolls[0]?.options?.actorName || "Unknown Attacker"
// Calculer les DR
const armorDR = defender.computeDamageReduction() || 0
// Appliquer les dégâts avec armure DR par défaut
const finalDamage = Math.max(0, damageTotal - armorDR)
await defender.applyDamage(-finalDamage)
// Créer un message de confirmation
const messageContent = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-lethal-fantasy/templates/damage-applied-message.hbs",
{
targetName: defender.name,
damage: finalDamage,
drText: armorDR > 0 ? `Armor DR: ${armorDR}` : "",
weaponName: weaponName,
attackerName: attackerName,
rawDamage: damageTotal
}
)
await ChatMessage.create({
content: messageContent,
speaker: ChatMessage.getSpeaker({ actor: defender })
})
})
/** /**
* Create a macro when dropping an entity on the hotbar * Create a macro when dropping an entity on the hotbar
* Item - open roll dialog * Item - open roll dialog

View File

@@ -140,7 +140,7 @@ export default class LethalFantasyActorSheet extends HandlebarsApplicationMixin(
if ("link" in event.target.dataset) return if ("link" in event.target.dataset) return
const el = event.currentTarget.closest('[data-drag="true"]') const el = event.currentTarget.closest('[data-drag="true"]')
const dragType = el.dataset.dragType const dragType = el?.dataset?.dragType
let dragData = {} let dragData = {}

View File

@@ -95,13 +95,26 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
const doc = this.document const doc = this.document
switch (partId) { switch (partId) {
case "main": case "main":
context.armorDR = this.actor.getArmorDR()
context.damageReduction = this.actor.computeDamageReduction()
context.damageReductionShield = this.actor.getShieldDR()
break break
case "skills": case "skills": {
context.tab = context.tabs.skills context.tab = context.tabs.skills
context.skills = doc.itemTypes.skill context.skills = doc.itemTypes.skill
// Organiser les skills par catégorie
const categories = ['layperson', 'professional', 'weapon', 'armor', 'resist']
context.skillsByCategory = categories.map(cat => {
return {
category: cat,
label: `LETHALFANTASY.Skill.Category.${cat}`,
skills: context.skills.filter(s => s.system.category === cat)
}
}).filter(catData => catData.skills.length > 0)
context.gifts = doc.itemTypes.gift context.gifts = doc.itemTypes.gift
context.vulnerabilities = doc.itemTypes.vulnerability context.vulnerabilities = doc.itemTypes.vulnerability
break break
}
case "spells": case "spells":
context.tab = context.tabs.spells context.tab = context.tabs.spells
context.spells = doc.itemTypes.spell context.spells = doc.itemTypes.spell
@@ -143,7 +156,7 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event) const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
// Handle different data types // Handle different data types
if (data.type === "Item") { if (data.type === "Item") {
const item = await fromUuid(data.uuid) const item = await fromUuid(data.uuid)
return this._onDropItem(item) return this._onDropItem(item)
} }
@@ -246,7 +259,6 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
async _onRoll(event, target) { async _onRoll(event, target) {
if (this.isEditMode) return if (this.isEditMode) return
console.log("Roll event", event)
const rollType = event.target.dataset.rollType const rollType = event.target.dataset.rollType
let rollKey = event.target.dataset.rollKey; let rollKey = event.target.dataset.rollKey;
let rollDice = event.target.dataset?.rollDice; let rollDice = event.target.dataset?.rollDice;

View File

@@ -162,9 +162,11 @@ export default class LethalFantasyMonsterSheet extends LethalFantasyActorSheet {
async _onRoll(event, target) { async _onRoll(event, target) {
if (this.isEditMode) return if (this.isEditMode) return
const rollType = event.target.dataset.rollType const rollable = event.target.closest('.rollable')
let rollKey = event.target.dataset.rollKey if (!rollable) return
let rollDice = event.target.dataset?.rollDice || "0" const rollType = rollable.dataset.rollType
let rollKey = rollable.dataset.rollKey
let rollDice = rollable.dataset?.rollDice || "0"
this.actor.system.prepareMonsterRoll(rollType, rollKey, rollDice) this.actor.system.prepareMonsterRoll(rollType, rollKey, rollDice)
} }
} }

View File

@@ -0,0 +1,152 @@
{
"d30_dice_results": {
"30": {
"melee_attack": "Possible Lethal or Vital Strike or Add D20E to Attack",
"ranged_attack": "Possible Lethal or Vital Strike or Add D20E to Attack",
"melee_defense": "Possible Flawless or Legendary Defense or Add D20E to Defense",
"arcane_spell_attack": "Possible Lethal or Vital Magical Strike or Add D20E to Spell Attack",
"arcane_spell_defense": "Possible Spell Catastrophe or adds D20E to Spell Defense",
"skill_rolls": "Skill Succeeds Regardless of Opposing Roll / Success at highest level / Matching 30s cancel each other out"
},
"29": {
"melee_attack": "Gain 1 Grit",
"ranged_attack": "Gain 1 Grit",
"melee_defense": "Gain 1 Grit",
"arcane_spell_attack": "Gain 1 Grit",
"arcane_spell_defense": "Gain 1 Grit",
"skill_rolls": "Gain 1 Grit"
},
"28": {
"melee_attack": "Shield Destruction",
"ranged_attack": "empty",
"melee_defense": "empty",
"arcane_spell_attack": "empty",
"arcane_spell_defense": "empty",
"skill_rolls": "empty"
},
"27": {
"melee_attack": "Granted D6 (1-6) Attack Modifier for This Melee Attack",
"ranged_attack": "Granted D6 (1-6) Attack Modifier for This Ranged Attack",
"melee_defense": "Granted 1 Luck dice for Use in This Combat Only",
"arcane_spell_attack": "No Spell Lethargy (the Aether Approves)",
"arcane_spell_defense": "Caster Suffers Severe pain and will be under a flash of pain for 1D6E seconds",
"skill_rolls": "Granted D6 (1-6) Skill Modifier for this Skill Attempt"
},
"26": {
"melee_attack": "Shield Destruction",
"ranged_attack": "empty",
"melee_defense": "empty",
"arcane_spell_attack": "empty",
"arcane_spell_defense": "empty",
"skill_rolls": "empty"
},
"25": {
"melee_attack": "Bleed, Knock-Back on Hit",
"ranged_attack": "Bleed",
"melee_defense": "Kick, Punch or Shield Bash",
"arcane_spell_attack": "empty",
"arcane_spell_defense": "empty",
"skill_rolls": "Add 1 to Skill Roll"
},
"21": {
"melee_attack": "Hit Inflicts Flash of Pain 1D6E seconds",
"ranged_attack": "Hit Inflicts Flash of Pain 1D6E seconds",
"melee_defense": "Defender Recovers or ignores any flash of pain",
"arcane_spell_attack": "Magical Damage inflicts Flash of pain 1D6E seconds",
"arcane_spell_defense": "Caster Suffers Severe pain and will be under a flash of pain for 1D6E seconds",
"skill_rolls": "empty"
},
"20": {
"melee_attack": "Possible Vicious Strike. Bleed, Knock-back on Hit",
"ranged_attack": "Possible Vicious Strike. Bleeding wound inflicted on hit.",
"melee_defense": "Possible 20/20 defense (avoids Any Attack Except a Lethal Strike). Grants a Kick, Punch or Shield Bash counter",
"arcane_spell_attack": "Possible Vicious Application of a Magical Attack",
"arcane_spell_defense": "Possible 20/20 Spell defense (Saves Against Any Magical Attack Except a Lethal Magical Strike)",
"skill_rolls": "20 Added to Skill Roll"
},
"15": {
"melee_attack": "Bleed, Knock-back on Hit",
"ranged_attack": "Bleed",
"melee_defense": "Kick, Punch or Shield Bash",
"arcane_spell_attack": "empty",
"arcane_spell_defense": "empty",
"skill_rolls": "Add 1 to Skill Roll"
},
"13": {
"melee_attack": "empty",
"ranged_attack": "empty",
"melee_defense": "empty",
"arcane_spell_attack": "empty",
"arcane_spell_defense": "empty",
"skill_rolls": "empty"
},
"11": {
"melee_attack": "Flurry Attack or Hit to Miss",
"ranged_attack": "Roll 2x Damage Dice",
"melee_defense": "empty",
"arcane_spell_attack": "empty",
"arcane_spell_defense": "empty",
"skill_rolls": "empty"
},
"10": {
"melee_attack": "Bleed, Knock-back on Hit",
"ranged_attack": "Bleed",
"melee_defense": "Kick, Punch or Shield Bash",
"arcane_spell_attack": "empty",
"arcane_spell_defense": "empty",
"skill_rolls": "Add 1 to Skill Roll"
},
"8": {
"melee_attack": "Mulligan, Can Choose to Re-roll This Attack",
"ranged_attack": "Mulligan, Can Choose to Re-Roll This Attack",
"melee_defense": "Mulligan, Can Choose to Re-Roll This Defense",
"arcane_spell_attack": "Mulligan, Can Re-Roll This Spell Attack",
"arcane_spell_defense": "Mulligan, Can Re-Roll This Spell Defense",
"skill_rolls": "Mulligan, Can Re-Roll This Skill roll"
},
"7": {
"melee_attack": "Flurry Attack on Hit or Miss",
"ranged_attack": "Roll 2x Double Damage Dice",
"melee_defense": "empty",
"arcane_spell_attack": "empty",
"arcane_spell_defense": "empty",
"skill_rolls": "empty"
},
"5": {
"melee_attack": "Bleed, Knock-back on Hit",
"ranged_attack": "Bleed",
"melee_defense": "Kick, Punch, or Shield Bash",
"arcane_spell_attack": "empty",
"arcane_spell_defense": "empty",
"skill_rolls": "Add 1 to Skill Roll"
},
"3": {
"melee_attack": "Triple Damage",
"ranged_attack": "Triple Damage",
"melee_defense": "DR Tripled including Shield",
"arcane_spell_attack": "Triple Damage on Spell Damage",
"arcane_spell_defense": "D12 Added to Spell Defense Modifier",
"skill_rolls": "empty"
},
"2": {
"melee_attack": "Double Damage",
"ranged_attack": "Double Damage",
"melee_defense": "DR Doubled including Shield",
"arcane_spell_attack": "Double Damage on Spell Damage",
"arcane_spell_defense": "D6 Added to Spell Defense Modifier",
"skill_rolls": "empty"
},
"1": {
"melee_attack": "empty",
"ranged_attack": "Possible Fumble Ranged ammo is broken unrecoverable",
"melee_defense": "empty",
"arcane_spell_attack": "Possible Spell Calamity or Catastrophe",
"arcane_spell_defense": "empty",
"skill_rolls": "empty"
}
},
"definitions": {
"flash_of_pain": "Causes the victim to defend with disfavor. They can only walk and cannot attack, cast spells, call miracles or perform skills.",
"shield_destruction_condition": "Occurs only if damage exceeds the shields DR."
}
}

View File

@@ -104,6 +104,18 @@ export const SPELL_LETHARGY_DICE = [
{ dice: "D20", value: "20", level: "21-25", maxLevel: 25 } { dice: "D20", value: "20", level: "21-25", maxLevel: 25 }
] ]
export const GRANTED_DICE_CHOICES = {
"0": { label: "None", value: "0" },
"D2": { label: "D2", value: "D2" },
"D3": { label: "D3", value: "D3" },
"D4": { label: "D4", value: "D4" },
"D6": { label: "D6", value: "D6" },
"D8": { label: "D8", value: "D8" },
"D10": { label: "D10", value: "D10" },
"D12": { label: "D12", value: "D12" },
"D20": { label: "D20", value: "D20" }
}
export const INITIATIVE_DICE_CHOICES_PER_CLASS = { export const INITIATIVE_DICE_CHOICES_PER_CLASS = {
"untrained": [ "untrained": [
{ "name": "Asleep or totally distracted (2D12)", "value": "2D12" }, { "name": "Asleep or totally distracted (2D12)", "value": "2D12" },
@@ -136,7 +148,7 @@ export const INITIATIVE_DICE_CHOICES_PER_CLASS = {
"cleric": [ "cleric": [
{ "name": "Asleep or totally distracted (1D12)", "value": "1D12" }, { "name": "Asleep or totally distracted (1D12)", "value": "1D12" },
{ "name": "Awake but unsuspecting (1D10)", "value": "1D10" }, { "name": "Awake but unsuspecting (1D10)", "value": "1D10" },
{ "name": "Declared Ready on Alert (1D)", "value": "1D" }, { "name": "Declared Ready on Alert (1)", "value": "1" },
/*{ "name": "Aware of the enemy, can hear them but not see (1D6)", "value": "1D6" }, /*{ "name": "Aware of the enemy, can hear them but not see (1D6)", "value": "1D6" },
{ "name": "Aware and know exactly where the enemy is (1D4)", "value": "1D4" }*/ { "name": "Aware and know exactly where the enemy is (1D4)", "value": "1D4" }*/
], ],
@@ -310,5 +322,6 @@ export const SYSTEM = {
MORTAL_CHOICES, MORTAL_CHOICES,
SPELL_CRITICAL, SPELL_CRITICAL,
MIRACLE_TYPES, MIRACLE_TYPES,
SPELL_LETHARGY_DICE SPELL_LETHARGY_DICE,
GRANTED_DICE_CHOICES
} }

View File

@@ -2,3 +2,4 @@ export { default as LethalFantasyActor } from "./actor.mjs"
export { default as LethalFantasyItem } from "./item.mjs" export { default as LethalFantasyItem } from "./item.mjs"
export { default as LethalFantasyRoll } from "./roll.mjs" export { default as LethalFantasyRoll } from "./roll.mjs"
export { default as LethalFantasyChatMessage } from "./chat-message.mjs" export { default as LethalFantasyChatMessage } from "./chat-message.mjs"
export { default as D30Roll } from "./d30-roll.mjs"

View File

@@ -65,7 +65,7 @@ export default class LethalFantasyActor extends Actor {
} }
} }
} }
goodSkill.weaponSkillModifier = maxValue * multiplier goodSkill.weaponSkillModifier = Math.ceil(maxValue * multiplier)
return goodSkill return goodSkill
} }
@@ -79,8 +79,82 @@ export default class LethalFantasyActor extends Actor {
} }
/* *************************************************/ /* *************************************************/
async prepareRoll(rollType, rollKey, rollDice ) { computeDamageReduction() {
console.log("Preparing roll", rollType, rollKey, rollDice) // Pour les monstres, utiliser hp.damageResistance et combat.damageReduction
if (this.type === "monster") {
let hpDR = Number(this.system.hp?.damageResistance) || 0
let combatDR = Number(this.system.combat?.damageReduction) || 0
return hpDR + combatDR
}
// Pour les personnages, utiliser biodata et items
let naturalDR = Number(this.system.biodata?.naturalDR) || 0
let magicDR = Number(this.system.biodata?.magicDR) || 0
let armorDR = this.getArmorDR()
return naturalDR + magicDR + armorDR
}
/* *************************************************/
getShieldDR() {
// Pour les monstres, utiliser combat.shieldDamageReduction
if (this.type === "monster") {
return Number(this.system.combat?.shieldDamageReduction) || 0
}
// Pour les personnages, utiliser les items de type shield
let dr = 0
for (let item of this.items) {
if (item.type === "shield" && item.system.equipped) {
dr += Number(item.system.damageReduction)
}
}
return dr
}
/* *************************************************/
getArmorDR() {
let dr = 0
for (let item of this.items) {
if (item.type === "armor" && item.system.equipped) {
dr += Number(item.system.damageReduction)
}
}
return dr
}
/* *************************************************/
getArmorDefenseValue() {
let defenseValue = 0
for (let item of this.items) {
if (item.type === "armor" && item.system.equipped) {
defenseValue += Number(item.system.defense)
}
}
return defenseValue
}
/* *************************************************/
fuzzyNameSearchWeaponSkills(weaponName, weaponClass = null) {
// Get all weapon skills without the " skill" suffix
let skills = this.items.filter((i) => i.type === "skill" && i.system.weaponClass === weaponClass && i.system.category === "weapon")
// Remove parenthesis in the weapon name for better matching
weaponName = weaponName.replace(/\(.*?\)/g, "").trim()
// Now search if we find all the words of the weapon name in the skill name
skills = skills.filter((s) => {
let skillName = s.name.toLowerCase().replace(" skill", "").trim()
let wName = weaponName.toLowerCase().trim()
let wWords = wName.split(" ")
for (let w of wWords) {
if (!skillName.includes(w)) {
return false
}
}
return true
})
return skills
}
/* *************************************************/
async prepareRoll(rollType, rollKey, rollDice, defenderId) {
console.log("Preparing roll", rollType, rollKey, rollDice, defenderId)
let rollTarget let rollTarget
switch (rollType) { switch (rollType) {
case "granted": case "granted":
@@ -89,7 +163,7 @@ export default class LethalFantasyActor extends Actor {
formula: foundry.utils.duplicate(this.system.granted[rollKey]), formula: foundry.utils.duplicate(this.system.granted[rollKey]),
rollKey: rollKey rollKey: rollKey
} }
if ( rollTarget.formula === "" || rollTarget.formula === undefined) { if (rollTarget.formula === "" || rollTarget.formula === undefined) {
rollTarget.formula = 0 rollTarget.formula = 0
} }
break; break;
@@ -126,60 +200,64 @@ export default class LethalFantasyActor extends Actor {
rollTarget.rollKey = rollKey rollTarget.rollKey = rollKey
break break
case "shield-roll": { case "shield-roll": {
rollTarget = this.items.find((i) => i.type === "shield" && i.id === rollKey) rollTarget = this.items.find((i) => i.type === "shield" && i.id === rollKey)
let shieldSkill = this.items.find((i) => i.type === "skill" && i.name.toLowerCase() === rollTarget.name.toLowerCase()) let shieldSkill = this.items.find((i) => i.type === "skill" && i.name.toLowerCase() === rollTarget.name.toLowerCase())
rollTarget.skill = shieldSkill rollTarget.skill = shieldSkill
rollTarget.rollKey = rollKey rollTarget.rollKey = rollKey
} }
break; break;
case "weapon-damage-small": case "weapon-damage-small":
case "weapon-damage-medium": case "weapon-damage-medium":
case "weapon-attack": case "weapon-attack":
case "weapon-defense": { case "weapon-defense": {
let weapon = this.items.find((i) => i.type === "weapon" && i.id === rollKey) let weapon = this.items.find((i) => i.type === "weapon" && i.id === rollKey)
let skill let skill
let skills = this.items.filter((i) => i.type === "skill" && i.name.toLowerCase() === weapon.name.toLowerCase()) let skills = this.items.filter((i) => i.type === "skill" && i.system.category === "weapon" && i.name.toLowerCase() === weapon.name.toLowerCase())
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0)
} else {
skills = this.fuzzyNameSearchWeaponSkills(weapon.name, weapon.system.weaponClass)
if (skills.length > 0) { if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0) skill = this.getBestWeaponClassSkill(skills, rollType, 1.0)
} else { } else {
skills = this.items.filter((i) => i.type === "skill" && i.name.toLowerCase().replace(" skill", "") === weapon.name.toLowerCase()) skills = this.items.filter((i) => i.type === "skill" && i.system.category === "weapon" && i.system.weaponClass === weapon.system.weaponClass)
if (skills.length > 0) { if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0) skill = this.getBestWeaponClassSkill(skills, rollType, 0.5)
} else { } else {
skills = this.items.filter((i) => i.type === "skill" && i.system.weaponClass === weapon.system.weaponClass) skills = this.items.filter((i) => i.type === "skill" && i.system.category === "weapon" && i.system.weaponClass.includes(SYSTEM.WEAPON_CATEGORIES[weapon.system.weaponClass]))
if (skills.length > 0) { if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 0.5) skill = this.getBestWeaponClassSkill(skills, rollType, 0.25)
} else { } else {
skills = this.items.filter((i) => i.type === "skill" && i.system.weaponClass.includes(SYSTEM.WEAPON_CATEGORIES[weapon.system.weaponClass])) ui.notifications.warn(game.i18n.localize("LETHALFANTASY.Notifications.skillNotFound"))
if (skills.length > 0) { return
skill = this.getBestWeaponClassSkill(skills, rollType, 0.25)
} else {
ui.notifications.warn(game.i18n.localize("LETHALFANTASY.Notifications.skillNotFound"))
return
}
} }
} }
} }
if (!weapon || !skill) {
console.error("Weapon or skill not found", weapon, skill)
ui.notifications.warn(game.i18n.localize("LETHALFANTASY.Notifications.skillNotFound"))
return
}
rollTarget = skill
rollTarget.weapon = weapon
rollTarget.weaponSkillModifier = skill.weaponSkillModifier
rollTarget.rollKey = rollKey
rollTarget.combat = foundry.utils.duplicate(this.system.combat)
if ( rollType === "weapon-damage-small" || rollType === "weapon-damage-medium") {
rollTarget.grantedDice = this.system.granted.damageDice
}
if ( rollType === "weapon-attack") {
rollTarget.grantedDice = this.system.granted.attackDice
}
if ( rollType === "weapon-defense") {
rollTarget.grantedDice = this.system.granted.defenseDice
}
} }
if (!weapon || !skill) {
console.error("Weapon or skill not found", weapon, skill)
ui.notifications.warn(game.i18n.localize("LETHALFANTASY.Notifications.skillNotFound"))
return
}
// Créer un objet plain au lieu de modifier directement le skill
rollTarget = {
...skill,
weapon: weapon,
weaponSkillModifier: skill.weaponSkillModifier,
rollKey: rollKey,
combat: foundry.utils.duplicate(this.system.combat)
}
if (rollType === "weapon-damage-small" || rollType === "weapon-damage-medium") {
rollTarget.grantedDice = this.system.granted.damageDice
}
if (rollType === "weapon-attack") {
rollTarget.grantedDice = this.system.granted.attackDice
}
if (rollType === "weapon-defense") {
rollTarget.armorDefense = this.getArmorDefenseValue()
rollTarget.grantedDice = this.system.granted.defenseDice
}
}
break break
default: default:
ui.notifications.error(game.i18n.localize("LETHALFANTASY.Notifications.rollTypeNotFound") + String(rollType)) ui.notifications.error(game.i18n.localize("LETHALFANTASY.Notifications.rollTypeNotFound") + String(rollType))
@@ -190,7 +268,7 @@ export default class LethalFantasyActor extends Actor {
rollTarget.magicUser = this.system.biodata.magicUser rollTarget.magicUser = this.system.biodata.magicUser
rollTarget.actorModifiers = foundry.utils.duplicate(this.system.modifiers) rollTarget.actorModifiers = foundry.utils.duplicate(this.system.modifiers)
rollTarget.actorLevel = this.system.biodata.level rollTarget.actorLevel = this.system.biodata.level
await this.system.roll(rollType, rollTarget) await this.system.roll(rollType, rollTarget, defenderId)
} }
} }

View File

@@ -0,0 +1,215 @@
/**
* Classe pour gérer les résultats du D30 dans Lethal Fantasy
*/
export default class D30Roll {
/**
* Table des résultats D30 chargée depuis le fichier JSON
* @type {Object}
*/
static resultsTable = null
/**
* Définitions des conditions spéciales
* @type {Object}
*/
static definitions = null
/**
* Types de jets supportés
* @type {Object}
*/
static ROLL_TYPES = {
MELEE_ATTACK: "melee_attack",
RANGED_ATTACK: "ranged_attack",
MELEE_DEFENSE: "melee_defense",
ARCANE_SPELL_ATTACK: "arcane_spell_attack",
ARCANE_SPELL_DEFENSE: "arcane_spell_defense",
SKILL_ROLLS: "skill_rolls"
}
/**
* Initialise la classe en chargeant la table des résultats
* @returns {Promise<void>}
*/
static async initialize() {
try {
const response = await fetch("systems/fvtt-lethal-fantasy/module/config/d30_results_tables.json")
const data = await response.json()
this.resultsTable = data.d30_dice_results
this.definitions = data.definitions
console.log("D30Roll | D30 results table loaded successfully")
} catch (error) {
console.error("D30Roll | Error loading D30 table:", error)
ui.notifications.error("Unable to load D30 results table")
}
}
/**
* Récupère le résultat d'un jet de D30
* @param {number} diceValue La valeur du dé (1-30)
* @param {string} rollType Le type de jet externe (ex: "weapon-attack", "spell-attack", etc.)
* @param {Object} weapon L'arme ou l'objet utilisé (optionnel, nécessaire pour certains types)
* @returns {string|null} Le résultat correspondant ou null si vide/non trouvé
*/
static getResult(diceValue, rollType, weapon = null) {
if (!this.resultsTable) {
console.warn("D30Roll | Results table is not initialized. Call D30Roll.initialize() first.")
return null
}
// Validation des paramètres
if (diceValue < 1 || diceValue > 30) {
console.warn(`D30Roll | Invalid dice value: ${diceValue}. Must be between 1 and 30.`)
return null
}
// Convert external rollType to internal rollType
const internalType = this.convertToInternalType(rollType, weapon)
if (!internalType) {
console.warn(`D30Roll | Could not convert roll type: ${rollType}`)
return null
}
if (!Object.values(this.ROLL_TYPES).includes(internalType)) {
console.warn(`D30Roll | Invalid internal roll type: ${internalType}`)
return null
}
const resultEntry = this.resultsTable[diceValue]
if (!resultEntry) {
console.warn(`D30Roll | No entry found for value ${diceValue}`)
return null
}
const result = resultEntry[internalType]
// Retourne null si le résultat est "empty"
if (result === "empty" || !result) {
return null
}
return result
}
/**
* Convertit un rollType externe en rollType interne
* @param {string} externalType Le type de jet externe (ex: "weapon-attack")
* @param {Object} weapon L'arme ou l'objet utilisé (optionnel)
* @returns {string|null} Le type interne correspondant ou null
*/
static convertToInternalType(externalType, weapon = null) {
// Attack types - need weapon to determine if melee or ranged
if (externalType === "weapon-attack") {
if (!weapon) {
console.warn("D30Roll | Weapon object required for weapon-attack type")
return this.ROLL_TYPES.MELEE_ATTACK // Default to melee
}
return weapon.system?.weaponType === "ranged"
? this.ROLL_TYPES.RANGED_ATTACK
: this.ROLL_TYPES.MELEE_ATTACK
}
// Monster attacks - default to melee
if (externalType === "monster-attack") {
// Check if weapon object has range information
if (weapon?.system?.weaponType === "ranged") {
return this.ROLL_TYPES.RANGED_ATTACK
}
return this.ROLL_TYPES.MELEE_ATTACK
}
// Defense types
if (externalType === "weapon-defense" || externalType === "monster-defense") {
return this.ROLL_TYPES.MELEE_DEFENSE
}
// Spell types
if (externalType === "spell-attack" || externalType === "spell" || externalType === "spell-power") {
return this.ROLL_TYPES.ARCANE_SPELL_ATTACK
}
// Skill types
if (externalType === "skill" || externalType === "monster-skill" ||
externalType === "save" || externalType === "challenge") {
return this.ROLL_TYPES.SKILL_ROLLS
}
// If no match, return null
console.warn(`D30Roll | Unknown external roll type: ${externalType}`)
return null
}
/**
* Récupère toutes les informations pour une valeur de dé donnée
* @param {number} diceValue La valeur du dé (1-30)
* @returns {Object|null} Tous les résultats pour cette valeur ou null
*/
static getAllResultsForValue(diceValue) {
if (!this.resultsTable) {
console.warn("D30Roll | Results table is not initialized.")
return null
}
if (diceValue < 1 || diceValue > 30) {
console.warn(`D30Roll | Invalid dice value: ${diceValue}`)
return null
}
return this.resultsTable[diceValue]
}
/**
* Récupère la définition d'une condition spéciale
* @param {string} definitionKey La clé de la définition (ex: "flash_of_pain")
* @returns {string|null} La définition ou null
*/
static getDefinition(definitionKey) {
if (!this.definitions) {
console.warn("D30Roll | Definitions are not initialized.")
return null
}
return this.definitions[definitionKey] || null
}
/**
* Vérifie si un résultat est vide
* @param {string} result Le résultat à vérifier
* @returns {boolean} True si le résultat est vide
*/
static isEmptyResult(result) {
return !result || result === "empty"
}
/**
* Récupère un résultat formaté pour l'affichage
* @param {number} diceValue La valeur du dé (1-30)
* @param {string} rollType Le type de jet externe
* @param {Object} weapon L'arme ou l'objet utilisé (optionnel)
* @returns {Object} Un objet avec le résultat et des informations de formatage
*/
static getFormattedResult(diceValue, rollType, weapon = null) {
const result = this.getResult(diceValue, rollType, weapon)
const internalType = this.convertToInternalType(rollType, weapon)
return {
value: diceValue,
rollType: rollType,
internalType: internalType,
result: result,
isEmpty: this.isEmptyResult(result),
hasResult: !this.isEmptyResult(result)
}
}
/**
* Vérifie si la table est chargée
* @returns {boolean} True si la table est chargée
*/
static isInitialized() {
return this.resultsTable !== null && this.definitions !== null
}
}

View File

@@ -1,5 +1,6 @@
import { SYSTEM } from "../config/system.mjs" import { SYSTEM } from "../config/system.mjs"
import LethalFantasyUtils from "../utils.mjs" import LethalFantasyUtils from "../utils.mjs"
import D30Roll from "./d30-roll.mjs"
export default class LethalFantasyRoll extends Roll { export default class LethalFantasyRoll extends Roll {
/** /**
@@ -92,6 +93,10 @@ export default class LethalFantasyRoll extends Roll {
return this.options.D30result return this.options.D30result
} }
get D30message() {
return this.options.D30message
}
get badResult() { get badResult() {
return this.options.badResult return this.options.badResult
} }
@@ -100,6 +105,10 @@ export default class LethalFantasyRoll extends Roll {
return this.options.rollData return this.options.rollData
} }
get defenderId() {
return this.options.defenderId
}
/** /**
* Prompt the user with a dialog to configure and execute a roll. * Prompt the user with a dialog to configure and execute a roll.
* *
@@ -130,9 +139,11 @@ export default class LethalFantasyRoll extends Roll {
let hasGrantedDice = false let hasGrantedDice = false
let pointBlank = false let pointBlank = false
let letItFly = false let letItFly = false
let saveSpell = false
let beyondSkill = false let beyondSkill = false
let hasStaticModifier = false let hasStaticModifier = false
let hasExplode = true let hasExplode = true
let actor = game.actors.get(options.actorId)
if (options.rollType === "challenge" || options.rollType === "save") { if (options.rollType === "challenge" || options.rollType === "save") {
options.rollName = game.i18n.localize(`LETHALFANTASY.Label.${options.rollTarget.rollKey}`) options.rollName = game.i18n.localize(`LETHALFANTASY.Label.${options.rollTarget.rollKey}`)
@@ -208,7 +219,7 @@ export default class LethalFantasyRoll extends Roll {
options.rollTarget.charModifier = options.rollTarget.combat.rangedAttackModifier options.rollTarget.charModifier = options.rollTarget.combat.rangedAttackModifier
} }
} else { } else {
options.rollTarget.value = options.rollTarget.combat.defenseModifier + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.defenseBonus options.rollTarget.value = options.rollTarget.combat.defenseModifier + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.defenseBonus + options.rollTarget.armorDefense
options.rollTarget.charModifier = options.rollTarget.combat.defenseModifier options.rollTarget.charModifier = options.rollTarget.combat.defenseModifier
} }
@@ -258,14 +269,17 @@ export default class LethalFantasyRoll extends Roll {
} else if (options.rollType.includes("weapon-damage")) { } else if (options.rollType.includes("weapon-damage")) {
options.rollName = options.rollTarget.name options.rollName = options.rollTarget.name
options.isDamage = true
hasModifier = true hasModifier = true
hasChangeDice = false hasChangeDice = false
let damageBonus = (options.rollTarget.weapon.system.applyStrengthDamageBonus) ? options.rollTarget.combat.damageModifier : 0 let damageBonus = (options.rollTarget.weapon.system.applyStrengthDamageBonus) ? options.rollTarget.combat.damageModifier : 0
options.rollTarget.value = damageBonus + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.damageBonus options.rollTarget.value = damageBonus + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.damageBonus
options.rollTarget.charModifier = damageBonus options.rollTarget.charModifier = damageBonus
if (options.rollType.includes("small")) { if (options.rollType.includes("small")) {
options.damageSmall = true
dice = options.rollTarget.weapon.system.damage.damageS dice = options.rollTarget.weapon.system.damage.damageS
} else { } else {
options.damageMedium = true
dice = options.rollTarget.weapon.system.damage.damageM dice = options.rollTarget.weapon.system.damage.damageM
} }
dice = dice.replace("E", "") dice = dice.replace("E", "")
@@ -273,6 +287,7 @@ export default class LethalFantasyRoll extends Roll {
} else if (options.rollType.includes("monster-damage")) { } else if (options.rollType.includes("monster-damage")) {
options.rollName = options.rollTarget.name options.rollName = options.rollTarget.name
options.isDamage = true
hasModifier = true hasModifier = true
hasChangeDice = false hasChangeDice = false
options.rollTarget.value = options.rollTarget.damageModifier options.rollTarget.value = options.rollTarget.damageModifier
@@ -357,7 +372,7 @@ export default class LethalFantasyRoll extends Roll {
], ],
actions: { actions: {
"selectGranted": (event, button, dialog) => { "selectGranted": (event, button, dialog) => {
hasGrantedDice = true hasGrantedDice = event.target.checked
}, },
"selectBeyondSkill": (event, button, dialog) => { "selectBeyondSkill": (event, button, dialog) => {
beyondSkill = button.checked beyondSkill = button.checked
@@ -368,6 +383,9 @@ export default class LethalFantasyRoll extends Roll {
"selectLetItFly": (event, button, dialog) => { "selectLetItFly": (event, button, dialog) => {
letItFly = button.checked letItFly = button.checked
}, },
"saveSpellCheck": (event, button, dialog) => {
saveSpell = button.checked
},
"gotoToken": (event, button, dialog) => { "gotoToken": (event, button, dialog) => {
let tokenId = $(button).data("tokenId") let tokenId = $(button).data("tokenId")
let token = canvas.tokens?.get(tokenId) let token = canvas.tokens?.get(tokenId)
@@ -384,6 +402,7 @@ export default class LethalFantasyRoll extends Roll {
// If the user cancels the dialog, exit // If the user cancels the dialog, exit
if (rollContext === null) return if (rollContext === null) return
console.log("rollContext", rollContext, hasGrantedDice) console.log("rollContext", rollContext, hasGrantedDice)
rollContext.saveSpell = saveSpell // Update fucking flag
let fullModifier = 0 let fullModifier = 0
let titleFormula = "" let titleFormula = ""
@@ -405,7 +424,6 @@ export default class LethalFantasyRoll extends Roll {
if (hasStaticModifier) { if (hasStaticModifier) {
modifierFormula += ` + ${options.rollTarget.staticModifier}` modifierFormula += ` + ${options.rollTarget.staticModifier}`
} }
// modifierFormula += ` + ${options.rollTarget.charModifier}`
let sign = fullModifier < 0 ? "-" : "+" let sign = fullModifier < 0 ? "-" : "+"
if (hasExplode) { if (hasExplode) {
titleFormula = `${dice}E ${sign} ${modifierFormula}` titleFormula = `${dice}E ${sign} ${modifierFormula}`
@@ -440,8 +458,11 @@ export default class LethalFantasyRoll extends Roll {
} }
// Specific pain/poison/contagion case // Specific pain/poison/contagion case
if (options.rollType === "save" && (options.rollTarget.rollKey === "pain" || options.rollTarget.rollKey === "paincourage" || options.rollTarget.rollKey === "poison" || options.rollTarget.rollKey === "contagion")) { if (options.rollType === "save" && (options.rollTarget.rollKey === "poison" || options.rollTarget.rollKey === "contagion")) {
hasD30 = false hasD30 = false
hasStaticModifier = true
modifierFormula = ` + ${Math.abs(fullModifier)}`
titleFormula = `${dice}E + ${Math.abs(fullModifier)}`
} }
if (letItFly) { if (letItFly) {
@@ -466,6 +487,9 @@ export default class LethalFantasyRoll extends Roll {
actorImage: options.actorImage, actorImage: options.actorImage,
rollMode: rollContext.visibility, rollMode: rollContext.visibility,
hasTarget: options.hasTarget, hasTarget: options.hasTarget,
isDamage: options.isDamage,
damageSmall: options.damageSmall,
damageMedium: options.damageMedium,
pointBlank, pointBlank,
beyondSkill, beyondSkill,
letItFly, letItFly,
@@ -495,15 +519,17 @@ export default class LethalFantasyRoll extends Roll {
if (rollContext.favor === "favor") { if (rollContext.favor === "favor") {
rollFavor = new this(baseFormula, options.data, rollData) rollFavor = new this(baseFormula, options.data, rollData)
await rollFavor.evaluate() await rollFavor.evaluate()
console.log("Rolling with favor", rollFavor)
if (game?.dice3d) { if (game?.dice3d) {
game.dice3d.showForRoll(rollFavor, game.user, true) game.dice3d.showForRoll(rollFavor, game.user, true)
} }
if (rollFavor.result > rollBase.result) { if (Number(rollFavor.result) > Number(rollBase.result)) {
badResult = rollBase.result badResult = rollBase.result
rollBase = rollFavor rollBase = rollFavor
} else { } else {
badResult = rollFavor.result badResult = rollFavor.result
} }
rollFavor = null
} }
if (rollContext.favor === "disfavor") { if (rollContext.favor === "disfavor") {
@@ -512,12 +538,13 @@ export default class LethalFantasyRoll extends Roll {
if (game?.dice3d) { if (game?.dice3d) {
game.dice3d.showForRoll(rollFavor, game.user, true) game.dice3d.showForRoll(rollFavor, game.user, true)
} }
if (rollFavor.result < rollBase.result) { if (Number(rollFavor.result) < Number(rollBase.result)) {
badResult = rollBase.result badResult = rollBase.result
rollBase = rollFavor rollBase = rollFavor
} else { } else {
badResult = rollFavor.result badResult = rollFavor.result
} }
rollFavor = null
} }
if (hasD30) { if (hasD30) {
@@ -526,9 +553,17 @@ export default class LethalFantasyRoll extends Roll {
game.dice3d.showForRoll(rollD30, game.user, true) game.dice3d.showForRoll(rollD30, game.user, true)
} }
options.D30result = rollD30.total options.D30result = rollD30.total
// Récupérer le message D30 correspondant
const d30Message = D30Roll.getResult(
rollD30.total,
options.rollType,
options.rollTarget?.weapon
)
options.D30message = d30Message
} }
let rollTotal = -1 let rollTotal = 0
let diceResults = [] let diceResults = []
let resultType let resultType
let diceSum = 0 let diceSum = 0
@@ -553,7 +588,8 @@ export default class LethalFantasyRoll extends Roll {
} }
} }
if (hasGrantedDice) { if (hasGrantedDice && options.rollTarget.grantedDice && options.rollTarget.grantedDice !== "") {
titleFormula += ` + ${options.rollTarget.grantedDice.toUpperCase()}`
let grantedRoll = new Roll(options.rollTarget.grantedDice) let grantedRoll = new Roll(options.rollTarget.grantedDice)
await grantedRoll.evaluate() await grantedRoll.evaluate()
if (game?.dice3d) { if (game?.dice3d) {
@@ -566,12 +602,12 @@ export default class LethalFantasyRoll extends Roll {
if (fullModifier !== 0) { if (fullModifier !== 0) {
diceResults.push({ dice: `${rollModifier.formula.toUpperCase()}`, value: rollModifier.total }) diceResults.push({ dice: `${rollModifier.formula.toUpperCase()}`, value: rollModifier.total })
if (fullModifier < 0) { if (fullModifier < 0) {
rollTotal = Math.max(diceSum - rollModifier.total, 0) rollTotal += Math.max(diceSum - rollModifier.total, 0)
} else { } else {
rollTotal = diceSum + rollModifier.total rollTotal += diceSum + rollModifier.total
} }
} else { } else {
rollTotal = diceSum rollTotal += diceSum
} }
rollBase.options.resultType = resultType rollBase.options.resultType = resultType
@@ -580,8 +616,10 @@ export default class LethalFantasyRoll extends Roll {
rollBase.options.rollTarget = options.rollTarget rollBase.options.rollTarget = options.rollTarget
rollBase.options.titleFormula = titleFormula rollBase.options.titleFormula = titleFormula
rollBase.options.D30result = options.D30result rollBase.options.D30result = options.D30result
rollBase.options.D30message = options.D30message
rollBase.options.badResult = badResult rollBase.options.badResult = badResult
rollBase.options.rollData = foundry.utils.duplicate(rollData) rollBase.options.rollData = foundry.utils.duplicate(rollData)
rollBase.options.defenderId = options.defenderId
/** /**
* A hook event that fires after the roll has been made. * A hook event that fires after the roll has been made.
@@ -620,7 +658,6 @@ export default class LethalFantasyRoll extends Roll {
fieldRollMode, fieldRollMode,
rollModes rollModes
} }
console.log("CTX", dialogContext)
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/roll-initiative-dialog.hbs", dialogContext) const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/roll-initiative-dialog.hbs", dialogContext)
@@ -655,7 +692,6 @@ export default class LethalFantasyRoll extends Roll {
let combat = game.combats.get(options.combatId) let combat = game.combats.get(options.combatId)
combat.updateEmbeddedDocuments("Combatant", [{ _id: options.combatantId, initiative: initRoll.total, 'system.progressionCount': 0 }]); combat.updateEmbeddedDocuments("Combatant", [{ _id: options.combatantId, initiative: initRoll.total, 'system.progressionCount': 0 }]);
} }
} }
/* ***********************************************************/ /* ***********************************************************/
@@ -845,8 +881,17 @@ export default class LethalFantasyRoll extends Roll {
let toCompare = Math.min(currentAction.progressionCount, max) let toCompare = Math.min(currentAction.progressionCount, max)
if (roll.total <= toCompare) { if (roll.total <= toCompare) {
// Notify that the player can act now with a chat message // Notify that the player can act now with a chat message
let message = game.i18n.format("LETHALFANTASY.Notifications.messageLethargyOK", { name: combatant.actor.name, spellName: currentAction.name, roll: roll.total }) const messageContent = await foundry.applications.handlebars.renderTemplate(
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) "systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
success: true,
actorName: combatant.actor.name,
weaponName: currentAction.name,
rollResult: roll.total,
isLethargy: true
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
// Update the combatant progression count // Update the combatant progression count
await combatant.setFlag(SYSTEM.id, "currentAction", "") await combatant.setFlag(SYSTEM.id, "currentAction", "")
// Display the action selection window again // Display the action selection window again
@@ -855,8 +900,18 @@ export default class LethalFantasyRoll extends Roll {
// Notify that the player cannot act now with a chat message // Notify that the player cannot act now with a chat message
currentAction.progressionCount += 1 currentAction.progressionCount += 1
await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction)) await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
let message = game.i18n.format("LETHALFANTASY.Notifications.messageLethargyKO", { name: combatant.actor.name, spellName: currentAction.name, roll: roll.total }) const messageContent = await foundry.applications.handlebars.renderTemplate(
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) "systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
success: false,
actorName: combatant.actor.name,
weaponName: currentAction.name,
rollResult: roll.total,
progressionCount: currentAction.progressionCount,
isLethargy: true
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
} }
} }
} }
@@ -902,25 +957,42 @@ export default class LethalFantasyRoll extends Roll {
if (roll.total <= max) { if (roll.total <= max) {
// Notify that the player can act now with a chat message // Notify that the player can act now with a chat message
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionOK", { isMonster, name: combatant.actor.name, weapon: currentAction.name, roll: roll.total }) const messageContent = await foundry.applications.handlebars.renderTemplate(
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) "systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
success: true,
actorName: combatant.actor.name,
weaponName: currentAction.name,
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
await combatant.setFlag(SYSTEM.id, "currentAction", "") await combatant.setFlag(SYSTEM.id, "currentAction", "")
combatant.actor.prepareRoll(currentAction.type === "weapon" ? "weapon-attack" : "spell-attack", currentAction._id) combatant.actor.prepareRoll(currentAction.type === "weapon" ? "weapon-attack" : "spell-attack", currentAction._id)
} else { } else {
// Notify that the player cannot act now with a chat message // Notify that the player cannot act now with a chat message
currentAction.progressionCount += 1 currentAction.progressionCount += 1
combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction)) combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionKO", { isMonster, name: combatant.actor.name, weapon: currentAction.name, roll: roll.total }) const messageContent = await foundry.applications.handlebars.renderTemplate(
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) "systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
success: false,
actorName: combatant.actor.name,
weaponName: currentAction.name,
rollResult: roll.total,
progressionCount: currentAction.progressionCount
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
} }
} }
} }
} }
/* ***********************************************************/ /* ***********************************************************/
static async promptRangedDefense(rollTarget) { static async promptRangedDefense(options = {}) {
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes); // v12 : Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)])) const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes);
const fieldRollMode = new foundry.data.fields.StringField({ const fieldRollMode = new foundry.data.fields.StringField({
choices: rollModes, choices: rollModes,
blank: false, blank: false,
@@ -935,13 +1007,12 @@ export default class LethalFantasyRoll extends Roll {
attackerAimChoices: SYSTEM.ATTACKER_AIM_CHOICES, attackerAimChoices: SYSTEM.ATTACKER_AIM_CHOICES,
movement: "none", movement: "none",
moveDirection: "none", moveDirection: "none",
size: "medium", size: "+5",
range: "short", range: "short",
attackerAim: "simple", attackerAim: "simple",
fieldRollMode, fieldRollMode,
rollModes rollModes
} }
console.log("CTX", dialogContext)
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/range-defense-dialog.hbs", dialogContext) const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/range-defense-dialog.hbs", dialogContext)
@@ -979,7 +1050,7 @@ export default class LethalFantasyRoll extends Roll {
if (rollContext.range === "beyondskill") { if (rollContext.range === "beyondskill") {
rollContext.movement = rollContext.movement.replace("kh", "") rollContext.movement = rollContext.movement.replace("kh", "")
rollContext.movement = rollContext.movement.replace("kl", "") rollContext.movement = rollContext.movement.replace("kl", "")
rollContext.movement += "kh" // Add the kl to the movement (disfavor for point blank range) rollContext.movement += "kh" // Add the kl to the movement (favor for point blank range)
rollContext.range = "+11" rollContext.range = "+11"
} }
@@ -987,19 +1058,19 @@ export default class LethalFantasyRoll extends Roll {
let fullModifier = Number(rollContext.moveDirection) + let fullModifier = Number(rollContext.moveDirection) +
Number(rollContext.size) + Number(rollContext.size) +
Number(rollContext.range) + Number(rollContext.range) +
Number(rollContext.attackerAim) Number(rollContext?.attackerAim || 0)
console.log("Modifier", fullModifier)
let modifierFormula let modifierFormula
if (fullModifier === 0) { if (fullModifier === 0) {
modifierFormula = "0" modifierFormula = "0"
} else { } else {
let modAbs = Math.abs(fullModifier) let modAbs = Math.abs(fullModifier)
modifierFormula = `${modAbs}` modifierFormula = `D${modAbs + 1} -1`
} }
let rollData = { ...rollContext } let rollData = { ...rollContext }
let options = { ...rollContext } // Merge rollContext object into options object
options = { ...options, ...rollContext }
options.rollName = "Ranged Defense" options.rollName = "Ranged Defense"
const rollBase = new this(rollContext.movement, options.data, rollData) const rollBase = new this(rollContext.movement, options.data, rollData)
@@ -1009,7 +1080,15 @@ export default class LethalFantasyRoll extends Roll {
let rollD30 = await new Roll("1D30").evaluate() let rollD30 = await new Roll("1D30").evaluate()
options.D30result = rollD30.total options.D30result = rollD30.total
let badResult = 0
if (rollContext.movement.includes("kh")) {
rollData.favor = "favor"
badResult = Math.min(rollBase.terms[0].results[0].result, rollBase.terms[0].results[1]?.result || 20)
}
if (rollContext.movement.includes("kl")) {
rollData.favor = "disfavor"
badResult = Math.max(rollBase.terms[0].results[0].result, rollBase.terms[0].results[1]?.result || 1)
}
let dice = rollContext.movement let dice = rollContext.movement
let maxValue = 20 // As per latest changes (was : Number(dice.match(/\d+$/)[0]) let maxValue = 20 // As per latest changes (was : Number(dice.match(/\d+$/)[0])
let rollTotal = -1 let rollTotal = -1
@@ -1035,15 +1114,16 @@ export default class LethalFantasyRoll extends Roll {
} else { } else {
rollTotal = diceSum rollTotal = diceSum
} }
rollBase.options = { ...rollBase.options, ...options }
rollBase.options.resultType = resultType rollBase.options.resultType = resultType
rollBase.options.rollTotal = rollTotal rollBase.options.rollTotal = rollTotal
rollBase.options.diceResults = diceResults rollBase.options.diceResults = diceResults
rollBase.options.rollTarget = options.rollTarget rollBase.options.rollTarget = options.rollTarget
rollBase.options.titleFormula = `${dice}E + ${modifierFormula}` rollBase.options.titleFormula = `1D20E + ${modifierFormula}`
rollBase.options.D30result = options.D30result rollBase.options.D30result = options.D30result
rollBase.options.rollName = "Ranged Defense" rollBase.options.rollName = "Ranged Defense"
rollBase.options.badResult = badResult
rollBase.options.rollData = foundry.utils.duplicate(rollData)
/** /**
* A hook event that fires after the roll has been made. * A hook event that fires after the roll has been made.
* @function * @function
@@ -1105,6 +1185,57 @@ export default class LethalFantasyRoll extends Roll {
* Generates the data required for rendering a roll chat card. * Generates the data required for rendering a roll chat card.
*/ */
async _getChatCardData(isPrivate) { async _getChatCardData(isPrivate) {
// Générer la liste des combatants de la scène
let combatants = []
let isAttack = this.type === "weapon-attack" || this.type === "monster-attack" || this.type === "spell-attack" || this.type === "miracle-attack"
if (this.rollData?.isDamage || isAttack) {
// D'abord, ajouter les combattants du combat actif
if (game?.combat?.combatants) {
for (let c of game.combat.combatants) {
if (c.actorId !== this.actorId) {
combatants.push({ id: c.id, name: c.name, tokenId: c.token.id })
}
}
}
// Ensuite, ajouter tous les tokens de la scène active qui ne sont pas déjà dans la liste
if (canvas?.scene?.tokens) {
const existingTokenIds = new Set(combatants.map(c => c.tokenId))
for (let token of canvas.scene.tokens) {
if (token.actorId !== this.actorId && !existingTokenIds.has(token.id)) {
combatants.push({
id: token.id,
name: token.name,
tokenId: token.id
})
}
}
}
}
// Récupérer les informations de l'arme pour les attaques réussies
let weaponDamageOptions = null
console.log("Roll type:", this.type, "rollTarget:", this.rollTarget, "Has weapon:", !!this.rollTarget?.weapon)
if (this.type === "weapon-attack" && this.rollTarget?.weapon) {
const weapon = this.rollTarget.weapon
weaponDamageOptions = {
weaponId: weapon._id || weapon.id,
weaponName: weapon.name,
damageS: weapon.system?.damage?.damageS,
damageM: weapon.system?.damage?.damageM
}
console.log("Weapon damage options:", weaponDamageOptions)
} else if (this.type === "monster-attack" && this.rollTarget) {
weaponDamageOptions = {
weaponId: this.rollTarget.rollKey,
weaponName: this.rollTarget.name,
damageFormula: this.rollTarget.damageDice,
damageModifier: this.rollTarget.damageModifier,
isMonster: true
}
console.log("Monster damage options:", weaponDamageOptions)
}
const cardData = { const cardData = {
css: [SYSTEM.id, "dice-roll"], css: [SYSTEM.id, "dice-roll"],
data: this.data, data: this.data,
@@ -1126,9 +1257,16 @@ export default class LethalFantasyRoll extends Roll {
targetName: this.targetName, targetName: this.targetName,
targetArmor: this.targetArmor, targetArmor: this.targetArmor,
D30result: this.D30result, D30result: this.D30result,
D30message: this.D30message,
badResult: this.badResult, badResult: this.badResult,
rollData: this.rollData, rollData: this.rollData,
isPrivate: isPrivate isPrivate: isPrivate,
combatants: combatants,
weaponDamageOptions: weaponDamageOptions,
isAttack: isAttack,
defenderId: this.defenderId,
// Vérifier si l'utilisateur peut sélectionner une cible (est GM ou possède l'acteur)
canSelectTarget: game.user.isGM || game.actors.get(this.actorId)?.testUserPermission(game.user, "OWNER")
} }
cardData.cssClass = cardData.css.join(" ") cardData.cssClass = cardData.css.join(" ")
cardData.tooltip = isPrivate ? "" : await this.getTooltip() cardData.tooltip = isPrivate ? "" : await this.getTooltip()

View File

@@ -90,9 +90,9 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
}) })
schema.granted = new fields.SchemaField({ schema.granted = new fields.SchemaField({
attackDice: new fields.StringField({ required: true, nullable: false, initial: "" }), attackDice: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.GRANTED_DICE_CHOICES }),
defenseDice: new fields.StringField({ required: true, nullable: false, initial: "" }), defenseDice: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.GRANTED_DICE_CHOICES }),
damageDice: new fields.StringField({ required: true, nullable: false, initial: "" }) damageDice: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.GRANTED_DICE_CHOICES })
}) })
schema.movement = new fields.SchemaField({ schema.movement = new fields.SchemaField({
@@ -120,6 +120,8 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
magicUser: new fields.BooleanField({ initial: false }), magicUser: new fields.BooleanField({ initial: false }),
clericUser: new fields.BooleanField({ initial: false }), clericUser: new fields.BooleanField({ initial: false }),
hpPerLevel: new fields.StringField({ required: true, nullable: false, initial: "" }), hpPerLevel: new fields.StringField({ required: true, nullable: false, initial: "" }),
naturalDR: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
magicDR: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
}) })
schema.modifiers = new fields.SchemaField({ schema.modifiers = new fields.SchemaField({
@@ -179,13 +181,13 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
if (!SYSTEM.MORTAL_CHOICES[data.biodata.mortal]) { if (!SYSTEM.MORTAL_CHOICES[data.biodata.mortal]) {
for (let key in SYSTEM.MORTAL_CHOICES) { for (let key in SYSTEM.MORTAL_CHOICES) {
let mortal = SYSTEM.MORTAL_CHOICES[key] let mortal = SYSTEM.MORTAL_CHOICES[key]
if ( mortal.label.toLowerCase() === data.biodata.mortal.toLowerCase()) { if (mortal.label.toLowerCase() === data.biodata.mortal.toLowerCase()) {
data.biodata.mortal = mortal.id data.biodata.mortal = mortal.id
} }
if ( data.biodata.mortal.toLowerCase().includes("shire")) { if (data.biodata.mortal.toLowerCase().includes("shire")) {
data.biodata.mortal = "halflings" data.biodata.mortal = "halflings"
} }
if ( data.biodata.mortal.toLowerCase().includes("human")) { if (data.biodata.mortal.toLowerCase().includes("human")) {
data.biodata.mortal = "mankind" data.biodata.mortal = "mankind"
} }
} }
@@ -235,8 +237,8 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
this.saves.toughness.value = conDef.toughness_save + this.modifiers.saveModifier this.saves.toughness.value = conDef.toughness_save + this.modifiers.saveModifier
this.challenges.dying.value = conDef.stabilization_dice this.challenges.dying.value = conDef.stabilization_dice
this.saves.contagion.value = this.characteristics.con.value + this.modifiers.saveModifier this.saves.contagion.value = this.characteristics.con.value;// + this.modifiers.saveModifier
this.saves.poison.value = this.characteristics.con.value + this.modifiers.saveModifier this.saves.poison.value = this.characteristics.con.value; // + this.modifiers.saveModifier
this.combat.attackModifier = 0 this.combat.attackModifier = 0
for (let chaKey of SYSTEM.CHARACTERISTIC_ATTACK) { for (let chaKey of SYSTEM.CHARACTERISTIC_ATTACK) {
@@ -271,7 +273,7 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
* @param {"="|"+"|"++"|"-"|"--"} rollAdvantage If there is an avantage (+), a disadvantage (-), a double advantage (++), a double disadvantage (--) or a normal roll (=). * @param {"="|"+"|"++"|"-"|"--"} rollAdvantage If there is an avantage (+), a disadvantage (-), a double advantage (++), a double disadvantage (--) or a normal roll (=).
* @returns {Promise<null>} - A promise that resolves to null if the roll is cancelled. * @returns {Promise<null>} - A promise that resolves to null if the roll is cancelled.
*/ */
async roll(rollType, rollTarget) { async roll(rollType, rollTarget, defenderId) {
const hasTarget = false const hasTarget = false
let roll = await LethalFantasyRoll.prompt({ let roll = await LethalFantasyRoll.prompt({
rollType, rollType,
@@ -280,7 +282,8 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
actorName: this.parent.name, actorName: this.parent.name,
actorImage: this.parent.img, actorImage: this.parent.img,
hasTarget, hasTarget,
target: false target: false,
defenderId
}) })
if (!roll) return null if (!roll) return null
@@ -293,7 +296,6 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find((c) => c.value === this.characteristics.wis.value) let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find((c) => c.value === this.characteristics.wis.value)
let maxInit = Number(wisDef.init_cap) || 1000 let maxInit = Number(wisDef.init_cap) || 1000
console.log("Rolling initiative for", this)
let roll = await LethalFantasyRoll.promptInitiative({ let roll = await LethalFantasyRoll.promptInitiative({
actorId: this.parent.id, actorId: this.parent.id,
@@ -310,6 +312,13 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
} }
async rollProgressionDice(combatId, combatantId, rollProgressionCount) { async rollProgressionDice(combatId, combatantId, rollProgressionCount) {
let combatant = game.combats.get(combatId)?.combatants?.get(combatantId)
// Don't roll if the combatant is defeated
if (combatant?.isDefeated) {
ui.notifications.warn(`${this.parent.name} is defeated and cannot attack.`)
return
}
// Get all weapons from the actor // Get all weapons from the actor
let weapons = this.parent.items.filter(i => i.type === "weapon" && i.system.weaponType === "melee") let weapons = this.parent.items.filter(i => i.type === "weapon" && i.system.weaponType === "melee")

View File

@@ -118,6 +118,13 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
defenseModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), defenseModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
damageModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), damageModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
armorHitPoints: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), armorHitPoints: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
damageReduction: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
shieldDamageReduction: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
shieldDefenseDice: new fields.StringField({ required: true, nullable: false, initial: "d4" })
})
schema.combatHTH = new fields.SchemaField({
attack1: attackField("1"),
attack2: attackField("2")
}) })
@@ -134,7 +141,7 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
* @param {"="|"+"|"++"|"-"|"--"} rollAdvantage If there is an avantage (+), a disadvantage (-), a double advantage (++), a double disadvantage (--) or a normal roll (=). * @param {"="|"+"|"++"|"-"|"--"} rollAdvantage If there is an avantage (+), a disadvantage (-), a double advantage (++), a double disadvantage (--) or a normal roll (=).
* @returns {Promise<null>} - A promise that resolves to null if the roll is cancelled. * @returns {Promise<null>} - A promise that resolves to null if the roll is cancelled.
*/ */
async roll(rollType, rollTarget) { async roll(rollType, rollTarget, defenderId = undefined) {
const hasTarget = false const hasTarget = false
let roll = await LethalFantasyRoll.prompt({ let roll = await LethalFantasyRoll.prompt({
rollType, rollType,
@@ -143,14 +150,15 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
actorName: this.parent.name, actorName: this.parent.name,
actorImage: this.parent.img, actorImage: this.parent.img,
hasTarget, hasTarget,
target: false target: false,
defenderId
}) })
if (!roll) return null if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode }) await roll.toMessage({}, { rollMode: roll.options.rollMode })
} }
async prepareMonsterRoll(rollType, rollKey, rollDice = undefined, tokenId = undefined) { async prepareMonsterRoll(rollType, rollKey, rollDice = undefined, tokenId = undefined, damageModifier = undefined, defenderId = undefined) {
let rollTarget let rollTarget
switch (rollType) { switch (rollType) {
case "monster-attack": case "monster-attack":
@@ -158,6 +166,22 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
case "monster-damage": case "monster-damage":
rollTarget = foundry.utils.duplicate(this.attacks[rollKey]) rollTarget = foundry.utils.duplicate(this.attacks[rollKey])
rollTarget.rollKey = rollKey rollTarget.rollKey = rollKey
// Si damageModifier est fourni (depuis le chat), l'utiliser au lieu de celui de la fiche
if (damageModifier !== undefined && rollType === "monster-damage") {
rollTarget.damageModifier = damageModifier
}
break
case "monster-attack-hth":
case "monster-defense-hth":
case "monster-damage-hth":
rollTarget = foundry.utils.duplicate(this.combatHTH[rollKey])
rollTarget.rollKey = rollKey
// Si damageModifier est fourni (depuis le chat), l'utiliser au lieu de celui de la fiche
if (damageModifier !== undefined && rollType === "monster-damage-hth") {
rollTarget.damageModifier = damageModifier
}
// Convertir le type de roll pour utiliser les mêmes handlers que les attaques normales
rollType = rollType.replace("-hth", "")
break break
case "monster-skill": case "monster-skill":
rollTarget = foundry.utils.duplicate(this.resists[rollKey]) rollTarget = foundry.utils.duplicate(this.resists[rollKey])
@@ -168,6 +192,19 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
rollTarget.rollKey = rollKey rollTarget.rollKey = rollKey
rollTarget.rollDice = rollDice rollTarget.rollDice = rollDice
break break
case "shield-defense": {
// Lance directement le dé de défense du bouclier
const formula = rollDice || this.combat.shieldDefenseDice
const roll = new Roll(formula)
await roll.evaluate()
const flavor = game.i18n.localize("LETHALFANTASY.Label.shieldDefenseDice")
await roll.toMessage({
flavor,
speaker: ChatMessage.getSpeaker({ actor: this.parent })
})
return
}
case "weapon-damage-small": case "weapon-damage-small":
case "weapon-damage-medium": case "weapon-damage-medium":
case "weapon-attack": case "weapon-attack":
@@ -210,13 +247,15 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
break break
default: default:
ui.notifications.error(game.i18n.localize("LETHALFANTASY.Notifications.rollTypeNotFound") + String(rollType)) ui.notifications.error(game.i18n.localize("LETHALFANTASY.Notifications.rollTypeNotFound") + String(rollType))
break return
} }
// In all cases // In all cases
rollTarget.tokenId = tokenId if (rollTarget) {
console.log(rollTarget) rollTarget.tokenId = tokenId
await this.roll(rollType, rollTarget) console.log(rollTarget)
await this.roll(rollType, rollTarget, defenderId)
}
} }
async rollInitiative(combatId = undefined, combatantId = undefined) { async rollInitiative(combatId = undefined, combatantId = undefined) {
@@ -239,6 +278,13 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
} }
async rollProgressionDice(combatId, combatantId) { async rollProgressionDice(combatId, combatantId) {
let combatant = game.combats.get(combatId)?.combatants?.get(combatantId)
// Don't roll if the combatant is defeated
if (combatant?.isDefeated) {
ui.notifications.warn(`${this.parent.name} is defeated and cannot attack.`)
return
}
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes) const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes)
const fieldRollMode = new foundry.data.fields.StringField({ const fieldRollMode = new foundry.data.fields.StringField({
@@ -249,7 +295,6 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
let roll = new Roll("1D12") let roll = new Roll("1D12")
await roll.evaluate() await roll.evaluate()
let combatant = game.combats.get(combatId)?.combatants?.get(combatantId)
let msg = await roll.toMessage({ flavor: `Progression Roll for ${this.parent.name}` }) let msg = await roll.toMessage({ flavor: `Progression Roll for ${this.parent.name}` })
if (game?.dice3d) { if (game?.dice3d) {
@@ -261,8 +306,16 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
let attack = this.attacks[key] let attack = this.attacks[key]
if (attack.enabled && attack.attackScore > 0 && attack.attackScore === roll.total) { if (attack.enabled && attack.attackScore > 0 && attack.attackScore === roll.total) {
hasAttack = true hasAttack = true
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionOKMonster", { isMonster: true, name: this.parent.name, weapon: attack.name, roll: roll.total }) const messageContent = await foundry.applications.handlebars.renderTemplate(
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: this.parent }) }) "systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
success: true,
actorName: this.parent.name,
weaponName: attack.name,
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
let token = combatant?.token let token = combatant?.token
this.prepareMonsterRoll("monster-attack", key, undefined, token?.id) this.prepareMonsterRoll("monster-attack", key, undefined, token?.id)
if (token?.object) { if (token?.object) {
@@ -271,9 +324,41 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
} }
} }
} }
// Check Hand To Hand attacks as well
if (!hasAttack) { if (!hasAttack) {
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionKOMonster", { isMonster: true, name: this.parent.name, roll: roll.total }) for (let key in this.combatHTH) {
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: this.parent }) }) let attack = this.combatHTH[key]
if (attack.enabled && attack.attackScore > 0 && attack.attackScore === roll.total) {
hasAttack = true
const messageContent = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
success: true,
actorName: this.parent.name,
weaponName: `${attack.name} (HTH)`,
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
let token = combatant?.token
this.prepareMonsterRoll("monster-attack-hth", key, undefined, token?.id)
if (token?.object) {
token.object?.control({ releaseOthers: true });
return canvas.animatePan(token.object.center);
}
}
}
}
if (!hasAttack) {
const messageContent = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
success: false,
actorName: this.parent.name,
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
} }
} }

View File

@@ -6,9 +6,10 @@ export default class LethalFantasyShield extends foundry.abstract.TypeDataModel
const requiredInteger = { required: true, nullable: false, integer: true } const requiredInteger = { required: true, nullable: false, integer: true }
schema.description = new fields.HTMLField({ required: true, textSearch: true }) schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.defense = new fields.StringField({required: true, initial: "d4", choices: SYSTEM.SHIELD_DEFENSE_DICE}) schema.defense = new fields.StringField({ required: true, initial: "d4", choices: SYSTEM.SHIELD_DEFENSE_DICE })
schema.movementreduction = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }) schema.movementreduction = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.hascover = new fields.BooleanField({ required: true, initial: false }) schema.hascover = new fields.BooleanField({ required: true, initial: false })
schema.damageReduction = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.standing = new fields.SchemaField({ schema.standing = new fields.SchemaField({
min: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }), min: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),

View File

@@ -1,3 +1,9 @@
import { SYSTEM } from "./config/system.mjs"
// Map temporaire pour stocker les données d'attaque en attente de défense
if (!globalThis.pendingDefenses) {
globalThis.pendingDefenses = new Map()
}
export default class LethalFantasyUtils { export default class LethalFantasyUtils {
@@ -22,43 +28,43 @@ export default class LethalFantasyUtils {
static setHookListeners() { static setHookListeners() {
Hooks.on('renderTokenHUD', async (hud, html, token) => { Hooks.on('renderTokenHUD', async (hud, html, token) => {
const lossHPButton = await foundry.applications.handlebars.renderTemplate('systems/fvtt-lethal-fantasy/templates/loss-hp-hud.hbs', {} ) const lossHPButton = await foundry.applications.handlebars.renderTemplate('systems/fvtt-lethal-fantasy/templates/loss-hp-hud.hbs', {})
$(html).find('div.left').append(lossHPButton); $(html).find('div.left').append(lossHPButton);
$(html).find('img.lethal-hp-loss-hud').click((event) => { $(html).find('img.lethal-hp-loss-hud').click((event) => {
event.preventDefault(); event.preventDefault();
let hpMenu = $(html).find('.hp-loss-wrap')[0] let hpMenu = $(html).find('.hp-loss-wrap')[0]
if (hpMenu.classList.contains("hp-loss-hud-disabled")) { if (hpMenu.classList.contains("hp-loss-hud-disabled")) {
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-active'); $(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-disabled'); $(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-active'); $(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-disabled'); $(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-active'); $(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-disabled'); $(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-disabled');
} else { } else {
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active'); $(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled'); $(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active'); $(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled'); $(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active'); $(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled'); $(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
} }
}) })
$(html).find('.loss-hp-hud-click').click((event) => { $(html).find('.loss-hp-hud-click').click((event) => {
event.preventDefault(); event.preventDefault();
let hpLoss = event.currentTarget.dataset.hpValue; let hpLoss = event.currentTarget.dataset.hpValue;
if (token) { if (token) {
let tokenFull = canvas.tokens.placeables.find( t => t.id === token._id); let tokenFull = canvas.tokens.placeables.find(t => t.id === token._id);
console.log(tokenFull, token) console.log(tokenFull, token)
let actor = tokenFull.actor; let actor = tokenFull.actor;
actor.applyDamage(Number(hpLoss)); actor.applyDamage(Number(hpLoss));
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active'); $(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled'); $(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active'); $(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled'); $(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active'); $(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled'); $(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
} }
}) })
}) })
} }
@@ -75,9 +81,468 @@ export default class LethalFantasyUtils {
actor = game.actors.get(msg.actorId) actor = game.actors.get(msg.actorId)
actor.system.rollProgressionDice(msg.combatId, msg.combatantId, msg.rollProgressionCount) actor.system.rollProgressionDice(msg.combatId, msg.combatantId, msg.rollProgressionCount)
break break
case "requestDefense":
// Vérifier si le message est destiné à cet utilisateur
if (msg.userId === game.user.id) {
LethalFantasyUtils.showDefenseRequest(msg)
}
break
case "offerAttackerGrit":
// Vérifier si le message est destiné à cet utilisateur
if (msg.userId === game.user.id) {
LethalFantasyUtils.handleAttackerGritOffer(msg)
}
break
} }
} }
/* -------------------------------------------- */
static async handleAttackerGritOffer(msg) {
const { attackerId, attackRoll, defenseRoll, attackerName, defenderName, attackWeaponId, attackRollType, attackRollKey, defenderId } = msg
const attacker = game.actors.get(attackerId)
if (!attacker) {
console.warn("Attacker not found:", attackerId)
return
}
const attackBonus = await LethalFantasyUtils.offerAttackerGritBonus(
attacker,
attackRoll,
defenseRoll,
attackerName,
defenderName
)
const attackRollFinal = attackRoll + attackBonus
// Maintenant créer le message de comparaison
await LethalFantasyUtils.compareAttackDefense({
attackerName,
attackerId,
attackRoll: attackRollFinal,
attackWeaponId,
attackRollType,
attackRollKey,
defenderName,
defenderId,
defenseRoll
})
}
/* -------------------------------------------- */
static async showDefenseRequest(msg) {
const attackerName = msg.attackerName
const attackerId = msg.attackerId
const defenderName = msg.defenderName
const weaponName = msg.weaponName || "attack"
const attackRoll = msg.attackRoll
const attackWeaponId = msg.attackWeaponId
const attackRollType = msg.attackRollType
const attackRollKey = msg.attackRollKey
const combatantId = msg.combatantId
const tokenId = msg.tokenId
// Récupérer le défenseur - essayer d'abord depuis le combat, puis depuis le token
let defender = null
if (game.combat && combatantId) {
const combatant = game.combat.combatants.get(combatantId)
if (combatant) {
defender = combatant.actor
}
}
// Si pas trouvé dans le combat, chercher le token directement
if (!defender && tokenId) {
const token = canvas.tokens.get(tokenId)
if (token) {
defender = token.actor
}
}
if (!defender) {
ui.notifications.error("Defender actor not found")
return
}
const isMonster = defender.type === "monster"
// Pour les monstres, récupérer les attaques activées
if (isMonster) {
const enabledAttacks = Object.entries(defender.system.attacks).filter(([key, attack]) => attack.enabled)
if (enabledAttacks.length === 0) {
ui.notifications.warn("No enabled attacks available for defense")
return
}
// Créer le contenu du dialogue pour monstre
let attacksHTML = enabledAttacks.map(([key, attack]) =>
`<option value="${key}">${attack.name}</option>`
).join("")
const content = `
<div class="defense-request-dialog">
<div class="attack-info">
<p><strong>${attackerName}</strong> attacks <strong>${defenderName}</strong> with <strong>${weaponName}</strong>!</p>
<p>Attack roll: <strong>${attackRoll}</strong></p>
</div>
<div class="weapon-selection">
<label for="defense-attack">Choose your defense attack:</label>
<select id="defense-attack" name="attackKey" style="width: 100%; margin-top: 8px;">
${attacksHTML}
</select>
</div>
</div>
`
// Afficher le dialogue
const result = await foundry.applications.api.DialogV2.wait({
window: { title: "Defense Roll" },
classes: ["lethalfantasy"],
content,
buttons: [
{
label: "Roll Defense",
icon: "fa-solid fa-shield",
callback: (event, button, dialog) => {
const attackKey = button.form.elements.attackKey.value
return attackKey
},
},
],
rejectClose: false
})
// Si l'utilisateur a validé, lancer le jet de défense
if (result) {
// Stocker temporairement les données pour le hook preCreateChatMessage
game.lethalFantasy = game.lethalFantasy || {}
game.lethalFantasy.nextDefenseData = {
attackerId,
attackRoll,
attackerName,
defenderName,
attackWeaponId,
attackRollType,
attackRollKey,
defenderId: defender.id
}
console.log("Storing defense data for monster:", defender.id)
defender.system.prepareMonsterRoll("monster-defense", result)
}
return
}
// Pour les personnages, récupérer les armes équipées
const equippedWeapons = defender.items.filter(i =>
i.type === "weapon" && i.system.equipped === true
)
if (equippedWeapons.length === 0) {
ui.notifications.warn("No equipped weapons for defense")
return
}
// Créer le contenu du dialogue pour personnage
let weaponsHTML = equippedWeapons.map(w =>
`<option value="${w.id}">${w.name}</option>`
).join("")
const content = `
<div class="defense-request-dialog">
<div class="attack-info">
<p><strong>${attackerName}</strong> attacks <strong>${defenderName}</strong> with <strong>${weaponName}</strong>!</p>
<p>Attack roll: <strong>${attackRoll}</strong></p>
</div>
<div class="weapon-selection">
<label for="defense-weapon">Choose your defense weapon:</label>
<select id="defense-weapon" name="weaponId" style="width: 100%; margin-top: 8px;">
${weaponsHTML}
</select>
</div>
</div>
`
// Afficher le dialogue
const result = await foundry.applications.api.DialogV2.wait({
window: { title: "Defense Roll" },
classes: ["lethalfantasy"],
content,
buttons: [
{
label: "Roll Defense",
icon: "fa-solid fa-shield",
callback: (event, button, dialog) => {
const weaponId = button.form.elements.weaponId.value
return weaponId
},
},
],
rejectClose: false
})
// Si l'utilisateur a validé, lancer le jet de défense
if (result) {
// Stocker temporairement les données pour le hook preCreateChatMessage
game.lethalFantasy = game.lethalFantasy || {}
game.lethalFantasy.nextDefenseData = {
attackerId,
attackRoll,
attackerName,
defenderName,
attackWeaponId,
attackRollType,
attackRollKey,
defenderId: defender.id
}
console.log("Storing defense data for character:", defender.id)
defender.prepareRoll("weapon-defense", result)
}
}
/* -------------------------------------------- */
static async offerGritLuckBonus(defender, attackRoll, currentDefenseRoll, attackerName, defenderName) {
let totalBonus = 0
let keepOffering = true
while (keepOffering && currentDefenseRoll + totalBonus < attackRoll) {
const currentGrit = defender.system.grit.current
const currentLuck = defender.system.luck.current
// Si plus de points disponibles, sortir
if (currentGrit <= 0 && currentLuck <= 0) {
break
}
const buttons = []
if (currentGrit > 0) {
buttons.push({
action: "grit",
label: `Spend 1 Grit (+1D6) [${currentGrit} left]`,
icon: "fa-solid fa-fist-raised",
callback: () => "grit"
})
}
if (currentLuck > 0) {
buttons.push({
action: "luck",
label: `Spend 1 Luck (+1D6) [${currentLuck} left]`,
icon: "fa-solid fa-clover",
callback: () => "luck"
})
}
buttons.push({
action: "continue",
label: "Continue (no bonus)",
icon: "fa-solid fa-forward",
callback: () => "continue"
})
const content = `
<div class="grit-luck-dialog">
<div class="combat-status">
<p><strong>${attackerName}</strong> rolled <strong>${attackRoll}</strong></p>
<p><strong>${defenderName}</strong> currently has <strong>${currentDefenseRoll + totalBonus}</strong></p>
${totalBonus > 0 ? `<p class="bonus-info">Bonus already added: +${totalBonus}</p>` : ''}
</div>
<p class="offer-text">You are losing! Spend Grit or Luck to add 1D6 to your defense?</p>
</div>
`
const choice = await foundry.applications.api.DialogV2.wait({
window: { title: "Defend with Grit or Luck" },
classes: ["lethalfantasy"],
content,
buttons,
rejectClose: false
})
if (!choice || choice === "continue") {
keepOffering = false
break
}
// Lancer 1D6
const bonusRoll = new Roll("1d6")
await bonusRoll.evaluate()
if (game?.dice3d) {
await game.dice3d.showForRoll(bonusRoll, game.user, true)
}
totalBonus += bonusRoll.total
// Déduire le point de Grit ou Luck
if (choice === "grit") {
await defender.update({ "system.grit.current": currentGrit - 1 })
await ChatMessage.create({
content: `<p><strong>${defenderName}</strong> spends 1 Grit and rolls <strong>${bonusRoll.total}</strong>! (Total defense bonus: +${totalBonus})</p>`,
speaker: ChatMessage.getSpeaker({ actor: defender })
})
} else if (choice === "luck") {
await defender.update({ "system.luck.current": currentLuck - 1 })
await ChatMessage.create({
content: `<p><strong>${defenderName}</strong> spends 1 Luck and rolls <strong>${bonusRoll.total}</strong>! (Total defense bonus: +${totalBonus})</p>`,
speaker: ChatMessage.getSpeaker({ actor: defender })
})
}
}
return totalBonus
}
/* -------------------------------------------- */
static async offerAttackerGritBonus(attacker, currentAttackRoll, defenseRoll, attackerName, defenderName) {
let totalBonus = 0
let keepOffering = true
while (keepOffering && currentAttackRoll + totalBonus <= defenseRoll) {
const currentGrit = attacker.system.grit.current
// Si plus de points de Grit disponibles, sortir
if (currentGrit <= 0) {
break
}
const buttons = [
{
action: "grit",
label: `Spend 1 Grit (+1D6) [${currentGrit} left]`,
icon: "fa-solid fa-fist-raised",
callback: () => "grit"
},
{
action: "continue",
label: "Continue (no bonus)",
icon: "fa-solid fa-forward",
callback: () => "continue"
}
]
const content = `
<div class="grit-luck-dialog">
<div class="combat-status">
<p><strong>${attackerName}</strong> currently has <strong>${currentAttackRoll + totalBonus}</strong></p>
<p><strong>${defenderName}</strong> rolled <strong>${defenseRoll}</strong></p>
${totalBonus > 0 ? `<p class="bonus-info">Bonus already added: +${totalBonus}</p>` : ''}
</div>
<p class="offer-text">You are losing! Spend Grit to add 1D6 to your attack?</p>
</div>
`
const choice = await foundry.applications.api.DialogV2.wait({
window: { title: "Attack with Grit" },
classes: ["lethalfantasy"],
content,
buttons,
rejectClose: false
})
if (!choice || choice === "continue") {
keepOffering = false
break
}
// Lancer 1D6
const bonusRoll = new Roll("1d6")
await bonusRoll.evaluate()
if (game?.dice3d) {
await game.dice3d.showForRoll(bonusRoll, game.user, true)
}
totalBonus += bonusRoll.total
// Déduire le point de Grit
await attacker.update({ "system.grit.current": currentGrit - 1 })
await ChatMessage.create({
content: `<p><strong>${attackerName}</strong> spends 1 Grit and rolls <strong>${bonusRoll.total}</strong>! (Total attack bonus: +${totalBonus})</p>`,
speaker: ChatMessage.getSpeaker({ actor: attacker })
})
}
return totalBonus
}
/* -------------------------------------------- */
static async compareAttackDefense(data) {
console.log("compareAttackDefense called with:", data)
const isAttackWin = data.attackRoll > data.defenseRoll
console.log("isAttackWin:", isAttackWin, "attackRoll:", data.attackRoll, "defenseRoll:", data.defenseRoll)
let damageButton = ""
if (isAttackWin && (data.attackWeaponId || data.attackRollKey)) {
console.log("Creating damage button. defenderId:", data.defenderId)
// Déterminer le type de dégâts à lancer
if (data.attackRollType === "weapon-attack") {
damageButton = `
<div class="attack-result-damage">
<button class="roll-damage-btn" data-attacker-id="${data.attackerId}" data-defender-id="${data.defenderId}" data-weapon-id="${data.attackWeaponId}" data-damage-type="small">
<i class="fa-solid fa-dice-d6"></i> Damage (Small)
</button>
<button class="roll-damage-btn" data-attacker-id="${data.attackerId}" data-defender-id="${data.defenderId}" data-weapon-id="${data.attackWeaponId}" data-damage-type="medium">
<i class="fa-solid fa-dice-d20"></i> Damage (Medium)
</button>
</div>
`
} else if (data.attackRollType === "monster-attack") {
damageButton = `
<div class="attack-result-damage">
<button class="roll-damage-btn" data-attacker-id="${data.attackerId}" data-defender-id="${data.defenderId}" data-attack-key="${data.attackRollKey}" data-damage-type="monster">
<i class="fa-solid fa-burst"></i> Damage
</button>
</div>
`
}
}
const resultMessage = `
<div class="attack-result ${isAttackWin ? 'attack-success' : 'attack-failure'}">
<h3><i class="fa-solid ${isAttackWin ? 'fa-sword' : 'fa-shield'}"></i> Combat Result</h3>
<div class="combat-comparison">
<div class="combat-side attacker ${isAttackWin ? 'winner' : 'loser'}">
<div class="side-label">Attacker</div>
<div class="side-info">
<div class="side-name">${data.attackerName}</div>
<div class="side-roll">${data.attackRoll}</div>
</div>
</div>
<div class="combat-vs">VS</div>
<div class="combat-side defender ${isAttackWin ? 'loser' : 'winner'}">
<div class="side-label">Defender</div>
<div class="side-info">
<div class="side-name">${data.defenderName}</div>
<div class="side-roll">${data.defenseRoll}</div>
</div>
</div>
</div>
<div class="combat-result-text">
${isAttackWin ?
`<i class="fa-solid fa-circle-check"></i> <strong>${data.attackerName}</strong> hits <strong>${data.defenderName}</strong>!` :
`<i class="fa-solid fa-shield-halved"></i> <strong>${data.defenderName}</strong> parries the attack!`
}
</div>
${damageButton}
</div>
`
console.log("Creating combat result message...")
await ChatMessage.create({
content: resultMessage,
speaker: { alias: "Combat System" }
})
console.log("Combat result message created!")
}
static registerHandlebarsHelpers() { static registerHandlebarsHelpers() {
Handlebars.registerHelper('isNull', function (val) { Handlebars.registerHelper('isNull', function (val) {
@@ -246,4 +711,110 @@ export default class LethalFantasyUtils {
} }
} }
/* -------------------------------------------- */
static async applyDamage(message, event) {
// Récupérer les données du message
let combatantId = event.currentTarget.dataset.combatantId
if (!combatantId || !game.combat) {
ui.notifications.error("No combatant selected")
return
}
let combatant = game.combat.combatants.get(combatantId)
if (!combatant) {
ui.notifications.error("Combatant not found")
return
}
let targetActor = combatant.token?.actor || game.actors.get(combatant.actorId)
if (!targetActor) {
ui.notifications.error("Target actor not found")
return
}
// Récupérer les données de dégâts du message
let damageTotal = message.rolls[0]?.total || 0
let weaponName = message.rolls[0]?.options?.rollName || "Unknown Weapon"
// Calculer les DR
let armorDR = targetActor.computeDamageReduction() || 0
let shieldDR = targetActor.getShieldDR() || 0
let totalDR = armorDR + shieldDR
// Créer le dialogue
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-lethal-fantasy/templates/apply-damage-dialog.hbs",
{
targetName: targetActor.name,
weaponName: weaponName,
damageTotal: damageTotal,
armorDR: armorDR,
shieldDR: shieldDR,
totalDR: totalDR,
damageNoDR: damageTotal,
damageWithArmor: Math.max(0, damageTotal - armorDR),
damageWithAll: Math.max(0, damageTotal - totalDR)
}
)
const result = await foundry.applications.api.DialogV2.wait({
window: { title: "Apply Damage" },
classes: ["lethalfantasy"],
position: { width: 280 },
content,
buttons: [
{
action: "noDR",
label: "No DR",
callback: () => ({ drType: "none", damage: damageTotal })
},
{
action: "armorDR",
label: "With Armor DR",
callback: () => ({ drType: "armor", damage: Math.max(0, damageTotal - armorDR) })
},
{
action: "allDR",
label: "With Armor + Shield DR",
callback: () => ({ drType: "all", damage: Math.max(0, damageTotal - totalDR) })
},
{
action: "cancel",
label: "Cancel",
callback: () => null
}
],
rejectClose: false
})
if (result && result.damage !== undefined) {
await targetActor.applyDamage(-result.damage)
// Message de confirmation
let drText = ""
if (result.drType === "armor") {
drText = `Armor DR: ${armorDR}`
} else if (result.drType === "all") {
drText = `Total DR: ${totalDR}`
}
const messageContent = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-lethal-fantasy/templates/damage-applied-message.hbs",
{
targetName: targetActor.name,
damage: result.damage,
drText: drText,
weaponName: weaponName
}
)
ChatMessage.create({
user: game.user.id,
speaker: { alias: targetActor.name },
rollMode: "gmroll",
content: messageContent
})
}
}
} }

View File

@@ -1 +1 @@
MANIFEST-000408 MANIFEST-000527

View File

@@ -1,8 +1,8 @@
2025/09/09-20:04:59.829367 7f811affd6c0 Recovering log #406 2026/01/19-20:32:07.348929 7f14d8ff96c0 Recovering log #525
2025/09/09-20:04:59.839654 7f811affd6c0 Delete type=3 #404 2026/01/19-20:32:07.358983 7f14d8ff96c0 Delete type=3 #523
2025/09/09-20:04:59.839707 7f811affd6c0 Delete type=0 #406 2026/01/19-20:32:07.359059 7f14d8ff96c0 Delete type=0 #525
2025/09/09-20:14:01.132105 7f811a7fc6c0 Level-0 table #411: started 2026/01/19-22:34:04.043006 7f1243fff6c0 Level-0 table #530: started
2025/09/09-20:14:01.132136 7f811a7fc6c0 Level-0 table #411: 0 bytes OK 2026/01/19-22:34:04.043050 7f1243fff6c0 Level-0 table #530: 0 bytes OK
2025/09/09-20:14:01.139097 7f811a7fc6c0 Delete type=0 #409 2026/01/19-22:34:04.052889 7f1243fff6c0 Delete type=0 #528
2025/09/09-20:14:01.170651 7f811a7fc6c0 Manual compaction at level-0 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!zw9RQocTdz3HRjZK' @ 0 : 0; will stop at (end) 2026/01/19-22:34:04.082744 7f1243fff6c0 Manual compaction at level-0 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!zw9RQocTdz3HRjZK' @ 0 : 0; will stop at (end)
2025/09/09-20:14:01.170691 7f811a7fc6c0 Manual compaction at level-1 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!zw9RQocTdz3HRjZK' @ 0 : 0; will stop at (end) 2026/01/19-22:34:04.092309 7f1243fff6c0 Manual compaction at level-1 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!zw9RQocTdz3HRjZK' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/09/09-20:04:40.803409 7f8120dfa6c0 Recovering log #402 2026/01/19-20:23:50.466717 7f14da7fc6c0 Recovering log #521
2025/09/09-20:04:40.813179 7f8120dfa6c0 Delete type=3 #400 2026/01/19-20:23:50.476745 7f14da7fc6c0 Delete type=3 #519
2025/09/09-20:04:40.813231 7f8120dfa6c0 Delete type=0 #402 2026/01/19-20:23:50.476798 7f14da7fc6c0 Delete type=0 #521
2025/09/09-20:04:50.082984 7f811a7fc6c0 Level-0 table #407: started 2026/01/19-20:32:02.937243 7f1243fff6c0 Level-0 table #526: started
2025/09/09-20:04:50.083011 7f811a7fc6c0 Level-0 table #407: 0 bytes OK 2026/01/19-20:32:02.937265 7f1243fff6c0 Level-0 table #526: 0 bytes OK
2025/09/09-20:04:50.088948 7f811a7fc6c0 Delete type=0 #405 2026/01/19-20:32:02.943907 7f1243fff6c0 Delete type=0 #524
2025/09/09-20:04:50.102813 7f811a7fc6c0 Manual compaction at level-0 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!zw9RQocTdz3HRjZK' @ 0 : 0; will stop at (end) 2026/01/19-20:32:02.956456 7f1243fff6c0 Manual compaction at level-0 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!zw9RQocTdz3HRjZK' @ 0 : 0; will stop at (end)
2025/09/09-20:04:50.102846 7f811a7fc6c0 Manual compaction at level-1 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!zw9RQocTdz3HRjZK' @ 0 : 0; will stop at (end) 2026/01/19-20:32:02.956498 7f1243fff6c0 Manual compaction at level-1 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!zw9RQocTdz3HRjZK' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000407 MANIFEST-000524

View File

@@ -1,15 +1,8 @@
2025/09/09-20:04:59.843330 7f811bfff6c0 Recovering log #405 2026/01/19-20:32:07.362345 7f14d9ffb6c0 Recovering log #522
2025/09/09-20:04:59.853031 7f811bfff6c0 Delete type=3 #403 2026/01/19-20:32:07.372537 7f14d9ffb6c0 Delete type=3 #520
2025/09/09-20:04:59.853123 7f811bfff6c0 Delete type=0 #405 2026/01/19-20:32:07.372596 7f14d9ffb6c0 Delete type=0 #522
2025/09/09-20:14:01.039360 7f811a7fc6c0 Level-0 table #410: started 2026/01/19-22:34:04.165591 7f1243fff6c0 Level-0 table #527: started
2025/09/09-20:14:01.042968 7f811a7fc6c0 Level-0 table #410: 15906 bytes OK 2026/01/19-22:34:04.165637 7f1243fff6c0 Level-0 table #527: 0 bytes OK
2025/09/09-20:14:01.050037 7f811a7fc6c0 Delete type=0 #408 2026/01/19-22:34:04.175509 7f1243fff6c0 Delete type=0 #525
2025/09/09-20:14:01.083045 7f811a7fc6c0 Manual compaction at level-0 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end) 2026/01/19-22:34:04.175734 7f1243fff6c0 Manual compaction at level-0 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!x5gLtqlW4sdDmHTd' @ 0 : 0; will stop at (end)
2025/09/09-20:14:01.083115 7f811a7fc6c0 Manual compaction at level-1 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at '!items!zjvGljrLk5SshC9D' @ 152 : 0 2026/01/19-22:34:04.175767 7f1243fff6c0 Manual compaction at level-1 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!x5gLtqlW4sdDmHTd' @ 0 : 0; will stop at (end)
2025/09/09-20:14:01.083123 7f811a7fc6c0 Compacting 1@1 + 1@2 files
2025/09/09-20:14:01.086586 7f811a7fc6c0 Generated table #411@1: 32 keys, 15258 bytes
2025/09/09-20:14:01.086603 7f811a7fc6c0 Compacted 1@1 + 1@2 files => 15258 bytes
2025/09/09-20:14:01.094593 7f811a7fc6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2025/09/09-20:14:01.094890 7f811a7fc6c0 Delete type=2 #322
2025/09/09-20:14:01.095210 7f811a7fc6c0 Delete type=2 #410
2025/09/09-20:14:01.122172 7f811a7fc6c0 Manual compaction at level-1 from '!items!zjvGljrLk5SshC9D' @ 152 : 0 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/09/09-20:04:40.816586 7f811affd6c0 Recovering log #401 2026/01/19-20:23:50.482057 7f14d8ff96c0 Recovering log #518
2025/09/09-20:04:40.826708 7f811affd6c0 Delete type=3 #399 2026/01/19-20:23:50.491682 7f14d8ff96c0 Delete type=3 #516
2025/09/09-20:04:40.826778 7f811affd6c0 Delete type=0 #401 2026/01/19-20:23:50.491756 7f14d8ff96c0 Delete type=0 #518
2025/09/09-20:04:50.089056 7f811a7fc6c0 Level-0 table #406: started 2026/01/19-20:32:02.944004 7f1243fff6c0 Level-0 table #523: started
2025/09/09-20:04:50.089094 7f811a7fc6c0 Level-0 table #406: 0 bytes OK 2026/01/19-20:32:02.944026 7f1243fff6c0 Level-0 table #523: 0 bytes OK
2025/09/09-20:04:50.095242 7f811a7fc6c0 Delete type=0 #404 2026/01/19-20:32:02.950204 7f1243fff6c0 Delete type=0 #521
2025/09/09-20:04:50.102822 7f811a7fc6c0 Manual compaction at level-0 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end) 2026/01/19-20:32:02.956469 7f1243fff6c0 Manual compaction at level-0 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!x5gLtqlW4sdDmHTd' @ 0 : 0; will stop at (end)
2025/09/09-20:04:50.102852 7f811a7fc6c0 Manual compaction at level-1 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end) 2026/01/19-20:32:02.956510 7f1243fff6c0 Manual compaction at level-1 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!x5gLtqlW4sdDmHTd' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000407 MANIFEST-000529

View File

@@ -1,15 +1,8 @@
2025/09/09-20:04:59.816534 7f8120dfa6c0 Recovering log #405 2026/01/19-20:32:07.335896 7f14d97fa6c0 Recovering log #527
2025/09/09-20:04:59.826062 7f8120dfa6c0 Delete type=3 #403 2026/01/19-20:32:07.346053 7f14d97fa6c0 Delete type=3 #525
2025/09/09-20:04:59.826148 7f8120dfa6c0 Delete type=0 #405 2026/01/19-20:32:07.346120 7f14d97fa6c0 Delete type=0 #527
2025/09/09-20:14:01.064807 7f811a7fc6c0 Level-0 table #410: started 2026/01/19-22:34:04.092377 7f1243fff6c0 Level-0 table #532: started
2025/09/09-20:14:01.070719 7f811a7fc6c0 Level-0 table #410: 149145 bytes OK 2026/01/19-22:34:04.092412 7f1243fff6c0 Level-0 table #532: 0 bytes OK
2025/09/09-20:14:01.076657 7f811a7fc6c0 Delete type=0 #408 2026/01/19-22:34:04.101864 7f1243fff6c0 Delete type=0 #530
2025/09/09-20:14:01.083085 7f811a7fc6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2026/01/19-22:34:04.133480 7f1243fff6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)
2025/09/09-20:14:01.111093 7f811a7fc6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at '!items!zt8s7564ep1La4XQ' @ 612 : 0 2026/01/19-22:34:04.133550 7f1243fff6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)
2025/09/09-20:14:01.111103 7f811a7fc6c0 Compacting 1@1 + 1@2 files
2025/09/09-20:14:01.115854 7f811a7fc6c0 Generated table #411@1: 88 keys, 116881 bytes
2025/09/09-20:14:01.115886 7f811a7fc6c0 Compacted 1@1 + 1@2 files => 116881 bytes
2025/09/09-20:14:01.121744 7f811a7fc6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2025/09/09-20:14:01.121874 7f811a7fc6c0 Delete type=2 #322
2025/09/09-20:14:01.122036 7f811a7fc6c0 Delete type=2 #410
2025/09/09-20:14:01.122196 7f811a7fc6c0 Manual compaction at level-1 from '!items!zt8s7564ep1La4XQ' @ 612 : 0 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/09/09-20:04:40.790103 7f811b7fe6c0 Recovering log #401 2026/01/19-20:23:50.451679 7f14d9ffb6c0 Recovering log #523
2025/09/09-20:04:40.799986 7f811b7fe6c0 Delete type=3 #399 2026/01/19-20:23:50.462356 7f14d9ffb6c0 Delete type=3 #521
2025/09/09-20:04:40.800061 7f811b7fe6c0 Delete type=0 #401 2026/01/19-20:23:50.462416 7f14d9ffb6c0 Delete type=0 #523
2025/09/09-20:04:50.076859 7f811a7fc6c0 Level-0 table #406: started 2026/01/19-20:32:02.930994 7f1243fff6c0 Level-0 table #528: started
2025/09/09-20:04:50.076908 7f811a7fc6c0 Level-0 table #406: 0 bytes OK 2026/01/19-20:32:02.931037 7f1243fff6c0 Level-0 table #528: 0 bytes OK
2025/09/09-20:04:50.082849 7f811a7fc6c0 Delete type=0 #404 2026/01/19-20:32:02.937118 7f1243fff6c0 Delete type=0 #526
2025/09/09-20:04:50.102802 7f811a7fc6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2026/01/19-20:32:02.956440 7f1243fff6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)
2025/09/09-20:04:50.102835 7f811a7fc6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2026/01/19-20:32:02.956479 7f1243fff6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000107 MANIFEST-000224

View File

@@ -1,15 +1,8 @@
2025/09/09-20:04:59.867080 7f811b7fe6c0 Recovering log #105 2026/01/19-20:32:07.386680 7f14d97fa6c0 Recovering log #222
2025/09/09-20:04:59.877643 7f811b7fe6c0 Delete type=3 #103 2026/01/19-20:32:07.397058 7f14d97fa6c0 Delete type=3 #220
2025/09/09-20:04:59.877696 7f811b7fe6c0 Delete type=0 #105 2026/01/19-20:32:07.397159 7f14d97fa6c0 Delete type=0 #222
2025/09/09-20:14:01.050289 7f811a7fc6c0 Level-0 table #110: started 2026/01/19-22:34:04.082756 7f1243fff6c0 Level-0 table #227: started
2025/09/09-20:14:01.058207 7f811a7fc6c0 Level-0 table #110: 430019 bytes OK 2026/01/19-22:34:04.082796 7f1243fff6c0 Level-0 table #227: 0 bytes OK
2025/09/09-20:14:01.064385 7f811a7fc6c0 Delete type=0 #108 2026/01/19-22:34:04.092156 7f1243fff6c0 Delete type=0 #225
2025/09/09-20:14:01.083065 7f811a7fc6c0 Manual compaction at level-0 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end) 2026/01/19-22:34:04.092321 7f1243fff6c0 Manual compaction at level-0 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end)
2025/09/09-20:14:01.095323 7f811a7fc6c0 Manual compaction at level-1 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at '!items!zKOpU34oLziGJW6y' @ 639 : 0 2026/01/19-22:34:04.092355 7f1243fff6c0 Manual compaction at level-1 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end)
2025/09/09-20:14:01.095336 7f811a7fc6c0 Compacting 1@1 + 1@2 files
2025/09/09-20:14:01.103834 7f811a7fc6c0 Generated table #111@1: 260 keys, 421724 bytes
2025/09/09-20:14:01.103860 7f811a7fc6c0 Compacted 1@1 + 1@2 files => 421724 bytes
2025/09/09-20:14:01.110575 7f811a7fc6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2025/09/09-20:14:01.110756 7f811a7fc6c0 Delete type=2 #22
2025/09/09-20:14:01.110964 7f811a7fc6c0 Delete type=2 #110
2025/09/09-20:14:01.122184 7f811a7fc6c0 Manual compaction at level-1 from '!items!zKOpU34oLziGJW6y' @ 639 : 0 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/09/09-20:04:40.841664 7f8120dfa6c0 Recovering log #101 2026/01/19-20:23:50.510934 7f14d97fa6c0 Recovering log #218
2025/09/09-20:04:40.852508 7f8120dfa6c0 Delete type=3 #99 2026/01/19-20:23:50.520501 7f14d97fa6c0 Delete type=3 #216
2025/09/09-20:04:40.852585 7f8120dfa6c0 Delete type=0 #101 2026/01/19-20:23:50.520578 7f14d97fa6c0 Delete type=0 #218
2025/09/09-20:04:50.121356 7f811a7fc6c0 Level-0 table #106: started 2026/01/19-20:32:02.976313 7f1243fff6c0 Level-0 table #223: started
2025/09/09-20:04:50.121387 7f811a7fc6c0 Level-0 table #106: 0 bytes OK 2026/01/19-20:32:02.976359 7f1243fff6c0 Level-0 table #223: 0 bytes OK
2025/09/09-20:04:50.127905 7f811a7fc6c0 Delete type=0 #104 2026/01/19-20:32:02.982272 7f1243fff6c0 Delete type=0 #221
2025/09/09-20:04:50.128027 7f811a7fc6c0 Manual compaction at level-0 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end) 2026/01/19-20:32:02.982509 7f1243fff6c0 Manual compaction at level-0 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end)
2025/09/09-20:04:50.128057 7f811a7fc6c0 Manual compaction at level-1 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end) 2026/01/19-20:32:02.982604 7f1243fff6c0 Manual compaction at level-1 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000407 MANIFEST-000523

View File

@@ -1,8 +1,8 @@
2025/09/09-20:04:59.855289 7f8120dfa6c0 Recovering log #405 2026/01/19-20:32:07.374730 7f14d8ff96c0 Recovering log #521
2025/09/09-20:04:59.864565 7f8120dfa6c0 Delete type=3 #403 2026/01/19-20:32:07.384387 7f14d8ff96c0 Delete type=3 #519
2025/09/09-20:04:59.864636 7f8120dfa6c0 Delete type=0 #405 2026/01/19-20:32:07.384466 7f14d8ff96c0 Delete type=0 #521
2025/09/09-20:14:01.076905 7f811a7fc6c0 Level-0 table #410: started 2026/01/19-22:34:04.031975 7f1243fff6c0 Level-0 table #526: started
2025/09/09-20:14:01.076943 7f811a7fc6c0 Level-0 table #410: 0 bytes OK 2026/01/19-22:34:04.032019 7f1243fff6c0 Level-0 table #526: 0 bytes OK
2025/09/09-20:14:01.082931 7f811a7fc6c0 Delete type=0 #408 2026/01/19-22:34:04.042831 7f1243fff6c0 Delete type=0 #524
2025/09/09-20:14:01.083098 7f811a7fc6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2026/01/19-22:34:04.082725 7f1243fff6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)
2025/09/09-20:14:01.122157 7f811a7fc6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2026/01/19-22:34:04.092294 7f1243fff6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/09/09-20:04:40.829012 7f811b7fe6c0 Recovering log #401 2026/01/19-20:23:50.495445 7f14d9ffb6c0 Recovering log #517
2025/09/09-20:04:40.838454 7f811b7fe6c0 Delete type=3 #399 2026/01/19-20:23:50.506507 7f14d9ffb6c0 Delete type=3 #515
2025/09/09-20:04:40.838506 7f811b7fe6c0 Delete type=0 #401 2026/01/19-20:23:50.506583 7f14d9ffb6c0 Delete type=0 #517
2025/09/09-20:04:50.095335 7f811a7fc6c0 Level-0 table #406: started 2026/01/19-20:32:02.950374 7f1243fff6c0 Level-0 table #522: started
2025/09/09-20:04:50.095357 7f811a7fc6c0 Level-0 table #406: 0 bytes OK 2026/01/19-20:32:02.950415 7f1243fff6c0 Level-0 table #522: 0 bytes OK
2025/09/09-20:04:50.102580 7f811a7fc6c0 Delete type=0 #404 2026/01/19-20:32:02.956303 7f1243fff6c0 Delete type=0 #520
2025/09/09-20:04:50.102829 7f811a7fc6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2026/01/19-20:32:02.956488 7f1243fff6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)
2025/09/09-20:04:50.102841 7f811a7fc6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2026/01/19-20:32:02.956546 7f1243fff6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)

View File

@@ -375,6 +375,9 @@
min-width: 2.5rem; min-width: 2.5rem;
max-width: 2.5rem; max-width: 2.5rem;
} }
.ranged-attack-button {
font-size: 0.8rem;
}
button { button {
min-width: 9rem; min-width: 9rem;
} }

View File

@@ -1,33 +1,34 @@
&.fortune { &.fortune {
img { img {
border: 0px; border: 0px;
} }
.intro-chat { .intro-chat {
border-radius: 20px; border-radius: 20px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
.intro-img { .intro-img {
padding: 5px; padding: 5px;
width: 80px; width: 80px;
align-self: center; align-self: center;
} }
.intro-right { .intro-right {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.introText { .introText {
font-family: var(--font-secondary); font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2); font-size: calc(var(--font-size-standard) * 1.2);
width: 210px; width: 210px;
text-align: center; text-align: center;
}
}
}
.button.control, .fortune-accepted {
display: flex;
justify-content: center;
align-items: center;
font-size: calc(var(--font-size-standard) * 1.3);
} }
}
}
.button.control,
.fortune-accepted {
display: flex;
justify-content: center;
align-items: center;
font-size: calc(var(--font-size-standard) * 1.3);
}
} }
&.ask-roll { &.ask-roll {
@@ -37,4 +38,279 @@
justify-content: center; justify-content: center;
font-family: var(--font-secondary); font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2); font-size: calc(var(--font-size-standard) * 1.2);
}
.defense-request {
padding: 12px;
background: linear-gradient(to bottom, #3a3930 0%, #2a2920 100%);
border: 2px solid #d4af37;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
h3 {
margin: 0 0 10px 0;
color: #d4af37;
font-size: calc(var(--font-size-standard) * 1.1);
font-weight: 700;
text-align: center;
text-transform: uppercase;
letter-spacing: 1px;
i {
margin-right: 6px;
}
}
p {
margin: 8px 0;
color: #f0e6d2;
font-size: calc(var(--font-size-standard) * 0.95);
line-height: 1.4;
strong {
color: #d4af37;
font-weight: 600;
}
}
.defense-prompt {
margin-top: 12px;
padding: 8px;
background: rgba(212, 175, 55, 0.1);
border-left: 3px solid #d4af37;
border-radius: 4px;
font-weight: 600;
color: #d4af37;
text-align: center;
}
}
.defense-request-dialog {
.attack-info {
padding: 12px;
background: linear-gradient(to bottom, rgba(42, 41, 32, 0.8) 0%, rgba(26, 25, 16, 0.9) 100%);
border: 1px solid rgba(212, 175, 55, 0.5);
border-radius: 6px;
margin-bottom: 16px;
p {
margin: 6px 0;
color: #f0e6d2;
font-size: calc(var(--font-size-standard) * 0.95);
strong {
color: #d4af37;
font-weight: 600;
}
}
}
.weapon-selection {
label {
display: block;
margin-bottom: 8px;
color: #d4af37;
font-weight: 600;
font-size: calc(var(--font-size-standard) * 0.95);
}
select {
width: 100%;
padding: 8px 12px;
background: #3a3930 !important;
border: 1px solid #d4af37;
border-radius: 4px;
color: #ffffff !important;
font-size: calc(var(--font-size-standard) * 0.95);
cursor: pointer;
&:focus {
outline: none;
border-color: #f0e6d2;
box-shadow: 0 0 0 2px rgba(212, 175, 55, 0.3);
}
option {
background: #3a3930 !important;
color: #ffffff !important;
padding: 6px;
&:checked,
&:hover {
background: #4a4940 !important;
color: #ffffff !important;
}
}
}
}
}
.grit-luck-dialog {
.combat-status {
padding: 12px;
background: linear-gradient(to bottom, rgba(42, 41, 32, 0.8) 0%, rgba(26, 25, 16, 0.9) 100%);
border: 1px solid rgba(212, 175, 55, 0.5);
border-radius: 6px;
margin-bottom: 16px;
p {
margin: 6px 0;
color: #f0e6d2;
font-size: calc(var(--font-size-standard) * 0.95);
strong {
color: #d4af37;
font-weight: 600;
}
}
.bonus-info {
color: #90EE90;
font-style: italic;
margin-top: 8px;
}
}
.offer-text {
color: #f0e6d2;
font-size: calc(var(--font-size-standard) * 1);
text-align: center;
font-weight: 600;
margin: 0;
}
}
.attack-result {
padding: 16px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
margin: 8px 0;
&.attack-success {
background: linear-gradient(to bottom, rgba(139, 0, 0, 0.2) 0%, rgba(100, 0, 0, 0.3) 100%);
border: 2px solid rgba(220, 20, 60, 0.6);
}
&.attack-failure {
background: linear-gradient(to bottom, rgba(0, 100, 139, 0.2) 0%, rgba(0, 70, 100, 0.3) 100%);
border: 2px solid rgba(70, 130, 180, 0.6);
}
h3 {
margin: 0 0 16px 0;
color: #d4af37;
font-size: calc(var(--font-size-standard) * 1.2);
font-weight: 700;
text-align: center;
text-transform: uppercase;
letter-spacing: 1px;
i {
margin-right: 8px;
}
}
.combat-comparison {
display: flex;
align-items: center;
justify-content: space-around;
margin-bottom: 16px;
padding: 12px;
background: rgba(0, 0, 0, 0.3);
border-radius: 6px;
.combat-side {
flex: 1;
text-align: center;
padding: 12px;
border-radius: 6px;
&.winner {
background: rgba(0, 255, 0, 0.1);
border: 2px solid rgba(0, 255, 0, 0.4);
}
&.loser {
background: rgba(255, 0, 0, 0.1);
border: 2px solid rgba(255, 0, 0, 0.3);
}
.side-label {
font-size: calc(var(--font-size-standard) * 0.8);
color: #aaa;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 4px;
}
.side-name {
font-size: calc(var(--font-size-standard) * 1);
color: #f0e6d2;
font-weight: 600;
margin-bottom: 8px;
}
.side-roll {
font-size: calc(var(--font-size-standard) * 1.5);
color: #d4af37;
font-weight: 700;
}
}
.combat-vs {
font-size: calc(var(--font-size-standard) * 1.2);
color: #d4af37;
font-weight: 700;
padding: 0 16px;
}
}
.combat-result-text {
text-align: center;
font-size: calc(var(--font-size-standard) * 1.1);
color: #f0e6d2;
margin-bottom: 16px;
padding: 12px;
background: rgba(0, 0, 0, 0.2);
border-radius: 6px;
i {
margin-right: 8px;
}
strong {
color: #d4af37;
}
}
.attack-result-damage {
display: flex;
gap: 8px;
justify-content: center;
.roll-damage-btn {
padding: 10px 16px;
background: linear-gradient(to bottom, #8b0000 0%, #660000 100%);
border: 1px solid #ff0000;
border-radius: 6px;
color: #f0e6d2;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
&:hover {
background: linear-gradient(to bottom, #a00000 0%, #7b0000 100%);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
transform: translateY(-2px);
}
&:active {
transform: translateY(0);
}
i {
margin-right: 6px;
}
}
}
} }

View File

@@ -47,7 +47,7 @@ i.lethalfantasy {
.application.dialog.lethalfantasy { .application.dialog.lethalfantasy {
font-family: var(--font-primary); font-family: var(--font-primary);
font-size: calc(var(--font-size-standard) * 1.0); font-size: calc(var(--font-size-standard) * 1);
background-image: var(--background-image-base); background-image: var(--background-image-base);
button:hover { button:hover {
background: var(--color-dark-6); background: var(--color-dark-6);
@@ -70,13 +70,13 @@ i.lethalfantasy {
background-size: 100% 100%; background-size: 100% 100%;
} }
.combat-sidebar li.combatant .token-initiative .initiative{ .combat-sidebar li.combatant .token-initiative .initiative {
margin-right: 16px; margin-right: 16px;
} }
.combat-sidebar li.combatant .token-initiative { .combat-sidebar li.combatant .token-initiative {
flex:none; flex: none;
} }
.initiative-minus { .initiative-minus {
margin-right: 8px; margin-right: 8px;
} }

View File

@@ -25,7 +25,6 @@
gap: 10px; gap: 10px;
flex: 1; flex: 1;
.monster-hp { .monster-hp {
display: flex; display: flex;
gap: 4px; gap: 4px;
@@ -46,7 +45,7 @@
min-width: 2.2rem; min-width: 2.2rem;
max-width: 2.2rem; max-width: 2.2rem;
margin-left: 4px; margin-left: 4px;
font-size: calc(var(--font-size-standard) * 1.0); font-size: calc(var(--font-size-standard) * 1);
} }
.character-hp-max { .character-hp-max {
clear: both; clear: both;
@@ -58,7 +57,7 @@
input { input {
width: 3.2rem; width: 3.2rem;
text-align: center; text-align: center;
font-size: calc(var(--font-size-standard) * 1.0); font-size: calc(var(--font-size-standard) * 1);
} }
} }
} }
@@ -121,9 +120,11 @@
flex: 1; flex: 1;
.monster-skill { .monster-skill {
display: flex; display: grid;
grid-template-columns: repeat(3, auto 2.5rem);
gap: 0.5rem;
align-items: center; align-items: center;
margin-right: 0.5rem;
.rollable:hover, .rollable:hover,
.rollable:focus { .rollable:focus {
text-shadow: 0 0 8px var(--color-shadow-primary); text-shadow: 0 0 8px var(--color-shadow-primary);
@@ -134,12 +135,9 @@
max-width: 2.5rem; max-width: 2.5rem;
} }
.name { .name {
flex: 1;
min-width: 3.2rem;
margin-left: 4px; margin-left: 4px;
} }
.form-group { .form-group {
flex: 1;
padding-left: 4px; padding-left: 4px;
.form-fields { .form-fields {
flex: none; flex: none;
@@ -157,8 +155,9 @@
.monster-movement { .monster-movement {
display: flex; display: flex;
align-items: center; flex-direction: column;
margin-right: 0.5rem; gap: 4px;
.rollable:hover, .rollable:hover,
.rollable:focus { .rollable:focus {
text-shadow: 0 0 8px var(--color-shadow-primary); text-shadow: 0 0 8px var(--color-shadow-primary);
@@ -252,7 +251,7 @@
} }
} }
.tab.monster-skills .main-div{ .tab.monster-skills .main-div {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
legend { legend {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
<div class="apply-damage-dialog">
<div class="dialog-content">
<div class="header">
<strong>{{targetName}}</strong>
-
{{weaponName}}
</div>
<div class="damage-summary">
<span class="label">{{localize
"LETHALFANTASY.Dialog.totalDamage"
}}:</span>
<span class="damage-value">{{damageTotal}}</span>
</div>
<div class="dr-summary">
<span>{{localize "LETHALFANTASY.Dialog.armorDR"}}: {{armorDR}}</span>
<span>{{localize "LETHALFANTASY.Dialog.shieldDR"}}: {{shieldDR}}</span>
<span class="total">DR: <strong>{{totalDR}}</strong></span>
</div>
<div class="damage-options">
<div class="option-line">
<span>{{localize "LETHALFANTASY.Dialog.noDR"}}</span>
<strong>{{damageNoDR}}</strong>
</div>
<div class="option-line">
<span>{{localize "LETHALFANTASY.Dialog.withArmor"}}</span>
<strong>{{damageWithArmor}}</strong>
</div>
<div class="option-line">
<span>{{localize "LETHALFANTASY.Dialog.withAll"}}</span>
<strong>{{damageWithAll}}</strong>
</div>
</div>
</div>
</div>

View File

@@ -1,97 +1,180 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="{{tab.id}}" data-group="{{tab.group}}"> <section
class="tab character-{{tab.id}} {{tab.cssClass}}"
data-tab="{{tab.id}}"
data-group="{{tab.group}}"
>
<div class="main-div"> <div class="main-div">
<fieldset> <fieldset>
<legend>{{localize "LETHALFANTASY.Label.biodata"}}</legend> <legend>{{localize "LETHALFANTASY.Label.biodata"}}</legend>
<div class="biodata"> <div class="biodata">
<div class="biodata-elem"> <div class="biodata-elem">
<span class="name">Class</span> <span class="name">Class</span>
{{formInput systemFields.biodata.fields.class value=system.biodata.class }} {{formInput
</div> systemFields.biodata.fields.class
<div class="biodata-elem"> value=system.biodata.class
<span class="name">Level</span> }}
{{formInput systemFields.biodata.fields.level value=system.biodata.level }} </div>
</div> <div class="biodata-elem">
<div class="biodata-elem"> <span class="name">Level</span>
<span class="name">Mortal</span> {{formInput
{{formInput systemFields.biodata.fields.mortal value=system.biodata.mortal }} systemFields.biodata.fields.level
</div> value=system.biodata.level
<div class="biodata-elem"> }}
<span class="name">Alignment</span> </div>
{{formInput systemFields.biodata.fields.alignment value=system.biodata.alignment }} <div class="biodata-elem">
</div> <span class="name">Mortal</span>
<div class="biodata-elem"> {{formInput
<span class="name">Age</span> systemFields.biodata.fields.mortal
{{formInput systemFields.biodata.fields.age value=system.biodata.age }} value=system.biodata.mortal
</div> }}
<div class="biodata-elem"> </div>
<span class="name">Height</span> <div class="biodata-elem">
{{formInput systemFields.biodata.fields.height value=system.biodata.height }} <span class="name">Alignment</span>
</div> {{formInput
<div class="biodata-elem"> systemFields.biodata.fields.alignment
<span class="name">Weight</span> value=system.biodata.alignment
{{formInput systemFields.biodata.fields.weight value=system.biodata.weight }} }}
</div> </div>
<div class="biodata-elem"> <div class="biodata-elem">
<span class="name">Eyes</span> <span class="name">Age</span>
{{formInput systemFields.biodata.fields.eyes value=system.biodata.eyes }} {{formInput systemFields.biodata.fields.age value=system.biodata.age}}
</div> </div>
<div class="biodata-elem"> <div class="biodata-elem">
<span class="name">Hair</span> <span class="name">Height</span>
{{formInput systemFields.biodata.fields.hair value=system.biodata.hair }} {{formInput
</div> systemFields.biodata.fields.height
<div class="biodata-elem"> value=system.biodata.height
<span class="name">Dev. Points (Total)</span> }}
{{formInput systemFields.developmentPoints.fields.total value=system.developmentPoints.total }} </div>
</div> <div class="biodata-elem">
<div class="biodata-elem"> <span class="name">Weight</span>
<span class="name">Dev. Points (Rem.)</span> {{formInput
{{formInput systemFields.developmentPoints.fields.remaining value=system.developmentPoints.remaining }} systemFields.biodata.fields.weight
</div> value=system.biodata.weight
<div class="biodata-elem"> }}
<span class="name">Magic User</span> </div>
{{formInput systemFields.biodata.fields.magicUser value=system.biodata.magicUser }} <div class="biodata-elem">
</div> <span class="name">Eyes</span>
<div class="biodata-elem"> {{formInput
<span class="name">Cleric User</span> systemFields.biodata.fields.eyes
{{formInput systemFields.biodata.fields.clericUser value=system.biodata.clericUser }} value=system.biodata.eyes
</div> }}
<div class="biodata-elem"> </div>
<span class="name">Save bonus (1/5levels)</span> <div class="biodata-elem">
{{formInput systemFields.modifiers.fields.saveModifier value=system.modifiers.saveModifier disabled=true}} <span class="name">Hair</span>
</div> {{formInput
systemFields.biodata.fields.hair
value=system.biodata.hair
}}
</div>
<div class="biodata-elem">
<span class="name">Dev. Points (Total)</span>
{{formInput
systemFields.developmentPoints.fields.total
value=system.developmentPoints.total
}}
</div>
<div class="biodata-elem">
<span class="name">Dev. Points (Rem.)</span>
{{formInput
systemFields.developmentPoints.fields.remaining
value=system.developmentPoints.remaining
}}
</div>
<div class="biodata-elem">
<span class="name">Magic User</span>
{{formInput
systemFields.biodata.fields.magicUser
value=system.biodata.magicUser
}}
</div>
<div class="biodata-elem">
<span class="name">Cleric User</span>
{{formInput
systemFields.biodata.fields.clericUser
value=system.biodata.clericUser
}}
</div>
<div class="biodata-elem">
<span class="name">Save bonus (1/5levels)</span>
{{formInput
systemFields.modifiers.fields.saveModifier
value=system.modifiers.saveModifier
disabled=true
}}
</div>
{{#if system.biodata.magicUser}} {{#if system.biodata.magicUser}}
<div class="biodata-elem"> <div class="biodata-elem">
<span class="name">Spell bonus (1/5levels)</span> <span class="name">Spell bonus (1/5levels)</span>
{{formInput systemFields.modifiers.fields.levelSpellModifier value=system.modifiers.levelSpellModifier disabled=true}} {{formInput
</div> systemFields.modifiers.fields.levelSpellModifier
{{/if}} value=system.modifiers.levelSpellModifier
{{#if system.biodata.clericUser}} disabled=true
<div class="biodata-elem"> }}
<span class="name">Miracle bonus (1/5levels)</span> </div>
{{formInput systemFields.modifiers.fields.levelMiracleModifier value=system.modifiers.levelMiracleModifier disabled=true}} {{/if}}
</div> {{#if system.biodata.clericUser}}
{{/if}} <div class="biodata-elem">
<span class="name">Miracle bonus (1/5levels)</span>
{{formInput
systemFields.modifiers.fields.levelMiracleModifier
value=system.modifiers.levelMiracleModifier
disabled=true
}}
</div>
{{/if}}
<div class="biodata-elem"> <div class="biodata-elem">
<span class="name">Last HD roll</span> <span class="name">Last HD roll</span>
{{formInput systemFields.biodata.fields.hpPerLevel value=system.biodata.hpPerLevel disabled=true}} {{formInput
</div> systemFields.biodata.fields.hpPerLevel
value=system.biodata.hpPerLevel
disabled=isPlayMode
}}
</div>
<div class="biodata-elem">
<span class="name">Natural DR</span>
{{formInput
systemFields.biodata.fields.naturalDR
value=system.biodata.naturalDR
disabled=isPlayMode
}}
</div>
</div> <div class="biodata-elem">
</fieldset> <span class="name">Magical DR</span>
{{formInput
systemFields.biodata.fields.magicDR
value=system.biodata.magicDR
disabled=isPlayMode
}}
</div>
<fieldset> </fieldset>
<legend>{{localize "LETHALFANTASY.Label.description"}}</legend>
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description"
toggled=true}}
</fieldset>
<fieldset> <fieldset>
<legend>{{localize "LETHALFANTASY.Label.notes"}}</legend> <legend>{{localize "LETHALFANTASY.Label.description"}}</legend>
{{formInput systemFields.notes enriched=enrichedNotes value=system.notes name="system.notes" toggled=true}} {{formInput
</fieldset> systemFields.description
enriched=enrichedDescription
value=system.description
name="system.description"
toggled=true
}}
</fieldset>
<fieldset>
<legend>{{localize "LETHALFANTASY.Label.notes"}}</legend>
{{formInput
systemFields.notes
enriched=enrichedNotes
value=system.notes
name="system.notes"
toggled=true
}}
</fieldset>
</div> </div>
</section> </section>

View File

@@ -1,161 +1,165 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="combat" data-group="sheet"> <section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="combat" data-group="sheet">
<div class="main-div"> <div class="main-div">
<fieldset> <fieldset>
<legend>{{localize "LETHALFANTASY.Label.combatDetails"}}</legend> <legend>{{localize "LETHALFANTASY.Label.combatDetails"}}</legend>
<div class="combat-details"> <div class="combat-details">
<div class="combat-detail"> <div class="combat-detail">
<button class="action" data-action="rangedAttackDefense"> <button class="action ranged-attack-button" data-action="rangedAttackDefense">
{{localize "LETHALFANTASY.Label.rangedAttackDefense"}} {{localize "LETHALFANTASY.Label.rangedAttackDefense"}}
</button> </button>
<button class="action" data-action="rollInitiative"> <button class="action ranged-attack-button" data-action="rollInitiative">
{{localize "LETHALFANTASY.Label.rollInitiative"}} {{localize "LETHALFANTASY.Label.rollInitiative"}}
</button> </button>
<div class="flexrow armor-hp">
<span class="name">{{localize "LETHALFANTASY.Label.armorHitPoints"}}</span>
{{formInput systemFields.combat.fields.armorHitPoints value=system.combat.armorHitPoints localize=true }}
<a data-action="armorHitPointsPlus"><i class="fa-solid fa-hexagon-plus"></i></a>
<a data-action="armorHitPointsMinus"><i class="fa-solid fa-hexagon-minus"></i></a>
</div>
<div class="flexrow granted">
<span class="">{{localize
"LETHALFANTASY.Label.grantedAttackDice"}}</a></span>
{{formInput systemFields.granted.fields.attackDice value=system.granted.attackDice disabled=isPlayMode }}
</div>
<div class="flexrow granted ">
<span class="">{{localize
"LETHALFANTASY.Label.grantedDefenseDice"}}</a></span>
{{formInput systemFields.granted.fields.defenseDice value=system.granted.defenseDice disabled=isPlayMode }}
</div>
<div class="flexrow granted">
<span class="">{{localize
"LETHALFANTASY.Label.grantedDamageDice"}}</a></span>
{{formInput systemFields.granted.fields.damageDice value=system.granted.damageDice disabled=isPlayMode }}
</div>
<div class="flexrow armor-hp">
<span class="name">{{localize "LETHALFANTASY.Label.armorHitPoints"}}</span>
{{formInput systemFields.combat.fields.armorHitPoints value=system.combat.armorHitPoints localize=true }}
<a data-action="armorHitPointsPlus"><i class="fa-solid fa-hexagon-plus"></i></a>
<a data-action="armorHitPointsMinus"><i class="fa-solid fa-hexagon-minus"></i></a>
</div> </div>
<div class="flexrow granted">
<span class=""><a class="rollable" data-roll-type="granted" data-roll-key="attackDice"><i
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize "LETHALFANTASY.Label.grantedAttackDice"}}</a></span>
{{formInput systemFields.granted.fields.attackDice value=system.granted.attackDice disabled=isPlayMode }}
</div>
<div class="flexrow granted ">
<span class=""><a class="rollable" data-roll-type="granted" data-roll-key="defenseDice"><i
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize "LETHALFANTASY.Label.grantedDefenseDice"}}</a></span>
{{formInput systemFields.granted.fields.defenseDice value=system.granted.defenseDice disabled=isPlayMode }}
</div>
<div class="flexrow granted">
<span class=""><a class="rollable" data-roll-type="granted" data-roll-key="damageDice"><i
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize "LETHALFANTASY.Label.grantedDamageDice"}}</a></span>
{{formInput systemFields.granted.fields.damageDice value=system.granted.damageDice disabled=isPlayMode }}
</div>
</div> </div>
</div> </fieldset>
</fieldset>
<fieldset> <fieldset>
<legend>{{localize "LETHALFANTASY.Label.wounds"}}</legend> <legend>{{localize "LETHALFANTASY.Label.wounds"}}</legend>
<div class="wounds"> <div class="wounds">
{{#each system.hp.wounds as |wound idx|}} {{#each system.hp.wounds as |wound idx|}}
<div class="wound"> <div class="wound">
Name:<input class="wound-description wound-data" type="text" data-type="String" data-index="{{@index}}" value="{{wound.description}}" data-name="description" > Name:<input class="wound-description wound-data" type="text" data-type="String" data-index="{{@index}}"
Duration:<input class="wound-duration wound-data" type="text" data-type="Number" data-index="{{@index}}" value="{{wound.duration}}" data-name="duration" > value="{{wound.description}}" data-name="description">
HP:<input class="wound-value wound-data" type="text" data-type="Number" data-index="{{@index}}" value="{{wound.value}}" data-name="value" > Duration:<input class="wound-duration wound-data" type="text" data-type="Number" data-index="{{@index}}"
value="{{wound.duration}}" data-name="duration">
HP:<input class="wound-value wound-data" type="text" data-type="Number" data-index="{{@index}}"
value="{{wound.value}}" data-name="value">
</div> </div>
{{/each}} {{/each}}
</div>
</fieldset>
<fieldset>
<legend>{{localize "LETHALFANTASY.Label.weapons"}}</legend>
<div class="weapons">
{{#each weapons as |item|}}
<div class="weapon" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true"
data-drag-type="damage">
{{#if (ne item.img "icons/svg/item-bag.svg")}}
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
{{/if}}
<div class="name">
{{item.name}}
</div>
<div class="attack-icons">
<a class="rollable" data-roll-type="weapon-attack" data-roll-key="{{item.id}}" data-tooltip="Roll Attack">
<i class="lf-roll-small fa-solid fa-swords" data-roll-type="weapon-attack" data-roll-key="{{item.id}}"></i>
</a>
<a class="rollable" data-roll-type="weapon-defense" data-roll-key="{{item.id}}" data-tooltip="Roll Defense">
<i class="fa-solid fa-shield-halved" data-roll-type="weapon-defense" data-roll-key="{{item.id}}"></i>
</a>
<a class="rollable" data-roll-type="weapon-damage-small" data-roll-key="{{item.id}}"
data-tooltip="Roll Damage (Small)">
<i class="fa-regular fa-face-head-bandage" data-roll-type="weapon-damage-small"
data-roll-key="{{item.id}}"></i>S
</a>
<a class="rollable" data-roll-type="weapon-damage-medium" data-roll-key="{{item.id}}"
data-tooltip="Roll Damage (Medium)">
<i class="fa-regular fa-face-head-bandage" data-roll-type="weapon-damage-medium"
data-roll-key="{{item.id}}"></i>M
</a>
</div>
<div class="controls">
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'LETHALFANTASY.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div> </div>
{{/each}} </fieldset>
</div>
</fieldset>
<fieldset> <fieldset>
<legend>{{localize "LETHALFANTASY.Label.armors"}}</legend> <legend>{{localize "LETHALFANTASY.Label.weapons"}}</legend>
<div class="armors"> <div class="weapons">
{{#each armors as |item|}} {{#each weapons as |item|}}
<div class="armor" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"> <div class="weapon" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true"
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" /> data-drag-type="damage">
<div class="name" data-tooltip="{{{item.system.description}}}"> {{#if (ne item.img "icons/svg/item-bag.svg")}}
{{item.name}} <img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
</div> {{/if}}
<div class="item-detail" data-tooltip="Defense">{{item.system.defense}}</div> <div class="name" data-tooltip="{{item.system.description}}">
<div class="item-detail" data-tooltip="Maximum movement">{{item.system.maximumMovement}}</div> {{item.name}}
<div class="item-detail" data-tooltip="HP">{{item.system.hp}}</div> </div>
<div class="item-detail" data-tooltip="Damage Reduction">{{item.system.damageReduction}}</div> <div class="attack-icons">
<div class="controls">
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}" <a class="rollable" data-roll-type="weapon-attack" data-roll-key="{{item.id}}" data-tooltip="Roll Attack">
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a> <i class="lf-roll-small fa-solid fa-swords" data-roll-type="weapon-attack"
<a data-tooltip="{{localize 'LETHALFANTASY.Delete'}}" data-action="delete" data-item-id="{{item.id}}" data-roll-key="{{item.id}}"></i>
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a> </a>
<a class="rollable" data-roll-type="weapon-defense" data-roll-key="{{item.id}}" data-tooltip="Roll Defense">
<i class="fa-solid fa-shield-halved" data-roll-type="weapon-defense" data-roll-key="{{item.id}}"></i>
</a>
<a class="rollable" data-roll-type="weapon-damage-small" data-roll-key="{{item.id}}"
data-tooltip="Roll Damage (Small)">
<i class="fa-regular fa-face-head-bandage" data-roll-type="weapon-damage-small"
data-roll-key="{{item.id}}"></i>S
</a>
<a class="rollable" data-roll-type="weapon-damage-medium" data-roll-key="{{item.id}}"
data-tooltip="Roll Damage (Medium)">
<i class="fa-regular fa-face-head-bandage" data-roll-type="weapon-damage-medium"
data-roll-key="{{item.id}}"></i>M
</a>
</div>
<div class="controls">
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'LETHALFANTASY.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div> </div>
{{/each}}
</div> </div>
{{/each}} </fieldset>
</div>
</fieldset>
<fieldset> <fieldset>
<legend>{{localize "LETHALFANTASY.Label.shields"}}</legend> <legend>{{localize "LETHALFANTASY.Label.armors"}}</legend>
<div class="shields"> <div class="armors">
{{#each shields as |item|}} {{#each armors as |item|}}
<div class="shield" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"> <div class="armor" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" /> <img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div class="name" data-tooltip="{{{item.system.description}}}"> <div class="name" data-tooltip="{{item.system.description}}">
{{item.name}} {{item.name}}
</div> </div>
<div class="item-detail" data-tooltip="Defense"> <div class="item-detail" data-tooltip="Defense">{{item.system.defense}}</div>
<a class="rollable" data-roll-type="shield-roll" data-roll-key="{{item.id}}" data-tooltip="Shield Defense"> <div class="item-detail" data-tooltip="Maximum movement">{{item.system.maximumMovement}}</div>
<i class="lf-roll-small fa-solid fa-shield" data-roll-type="shield-roll" data-roll-key="{{item.id}}"></i> <div class="item-detail" data-tooltip="HP">{{item.system.hp}}</div>
{{upperFirst item.system.defense}} <div class="item-detail" data-tooltip="Damage Reduction">{{item.system.damageReduction}}</div>
</a> <div class="item-detail" data-tooltip={{#if item.system.equipped}}"Equipped"{{else}}"Not Equipped"{{/if}}>{{#if item.system.equipped}}<i class="fas fa-check"></i>{{else}}<i class="fas fa-times"></i>{{/if}}</div>
<div class="controls">
</div> <a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
<div class="item-detail" data-tooltip="Movement reduction">{{item.system.movementreduction}}</div> data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<div class="item-detail" data-tooltip="Has cover">{{#if item.system.hascover}}Cover{{/if}}</div> <a data-tooltip="{{localize 'LETHALFANTASY.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
<div class="controls"> </div>
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'LETHALFANTASY.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
</div> </div>
{{/each}}
</div> </div>
{{/each}} </fieldset>
</div>
</fieldset>
<div>
<fieldset>
<legend>{{localize "LETHALFANTASY.Label.shields"}}</legend>
<div class="shields">
{{#each shields as |item|}}
<div class="shield" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div class="name" data-tooltip="{{item.system.description}}">
{{item.name}}
</div>
<div class="item-detail" data-tooltip="Defense">
<a class="rollable" data-roll-type="shield-roll" data-roll-key="{{item.id}}" data-tooltip="Shield Defense">
<i class="lf-roll-small fa-solid fa-shield" data-roll-type="shield-roll" data-roll-key="{{item.id}}"></i>
{{upperFirst item.system.defense}}
</a>
</div>
<div class="item-detail" data-tooltip="Movement reduction">{{item.system.movementreduction}}</div>
<div class="item-detail" data-tooltip="Has cover">{{#if item.system.hascover}}Cover{{/if}}</div>
<div class="item-detail" data-tooltip={{#if item.system.equipped}}"Equipped"{{else}}"Not Equipped"{{/if}}>{{#if item.system.equipped}}<i class="fas fa-check"></i>{{else}}<i class="fas fa-times"></i>{{/if}}</div>
<div class="controls">
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'LETHALFANTASY.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{/each}}
</div>
</fieldset>
</div>
</section> </section>

View File

@@ -1,4 +1,6 @@
<section class="character-main character-main-{{ifThen isPlayMode 'play' 'edit'}}"> <section
class="character-main character-main-{{ifThen isPlayMode 'play' 'edit'}}"
>
{{log "character-main" this}} {{log "character-main" this}}
<fieldset> <fieldset>
@@ -6,99 +8,189 @@
<div class="character-pc character-pc-{{ifThen isPlayMode 'play' 'edit'}}"> <div class="character-pc character-pc-{{ifThen isPlayMode 'play' 'edit'}}">
<div class="character-left"> <div class="character-left">
<div class="character-left-image"> <div class="character-left-image">
<img class="character-img" src="{{actor.img}}" data-edit="img" data-action="editImage" <img
data-tooltip="{{actor.name}}" /> class="character-img"
src="{{actor.img}}"
data-edit="img"
data-action="editImage"
data-tooltip="{{actor.name}}"
/>
</div> </div>
<fieldset class=""> <fieldset class="">
<div class="flexrow character-hp"> <div class="flexrow character-hp">
<span class="name">{{localize "LETHALFANTASY.Label.HP"}}</span> <span class="name">{{localize "LETHALFANTASY.Label.HP"}}</span>
{{formInput systemFields.hp.fields.value value=system.hp.value disabled=isPlayMode {{formInput
classes="character-hp-value"}} systemFields.hp.fields.value
value=system.hp.value
disabled=isPlayMode
classes="character-hp-value"
}}
&nbsp;/&nbsp; &nbsp;/&nbsp;
{{formInput systemFields.hp.fields.max value=system.hp.max disabled=isPlayMode {{formInput
classes="character-hp-value"}} systemFields.hp.fields.max
value=system.hp.max
disabled=isPlayMode
classes="character-hp-value"
}}
</div> </div>
<div class="flexrow character-hp"> <div class="flexrow character-hp">
<span class="name">{{localize "LETHALFANTASY.Label.grit"}}</span> <span class="name">{{localize "LETHALFANTASY.Label.grit"}}</span>
{{formInput systemFields.grit.fields.current value=system.grit.current disabled=isPlayMode {{formInput
classes="character-hp"}} systemFields.grit.fields.current
value=system.grit.current
disabled=isPlayMode
classes="character-hp"
}}
<span class="name">{{localize "LETHALFANTASY.Label.earned"}}</span> <span class="name">{{localize "LETHALFANTASY.Label.earned"}}</span>
{{formInput systemFields.grit.fields.earned value=system.grit.earned disabled=isPlayMode {{formInput
classes="character-hp"}} systemFields.grit.fields.earned
value=system.grit.earned
disabled=isPlayMode
classes="character-hp"
}}
</div> </div>
<div class="flexrow character-hp"> <div class="flexrow character-hp">
<span class="name">{{localize "LETHALFANTASY.Label.luck"}}</span> <span class="name">{{localize "LETHALFANTASY.Label.luck"}}</span>
{{formInput systemFields.luck.fields.current value=system.luck.current disabled=isPlayMode {{formInput
classes="character-hp"}} systemFields.luck.fields.current
value=system.luck.current
disabled=isPlayMode
classes="character-hp"
}}
<span class="name">{{localize "LETHALFANTASY.Label.earned"}}</span> <span class="name">{{localize "LETHALFANTASY.Label.earned"}}</span>
{{formInput systemFields.luck.fields.earned value=system.luck.earned disabled=isPlayMode {{formInput
classes="character-hp"}} systemFields.luck.fields.earned
value=system.luck.earned
disabled=isPlayMode
classes="character-hp"
}}
</div> </div>
<div class="flexrow "> <div class="flexrow">
<span class="">{{localize "LETHALFANTASY.Label.damageResistanceShort"}}</span> <span class="" data-tooltip="Damage reduction">{{localize
{{formInput systemFields.hp.fields.damageResistance value=system.hp.fields.damageResistance "LETHALFANTASY.Label.damageResistanceShort"
disabled=isPlayMode classes="character-hp"}} }}
</div> </span>
<input
type="text"
class="character-damage-reduction"
data-tooltip="DR (armor+natural+magical)"
disabled
value={{damageReduction}}
/>
<input
type="text"
class="character-damage-reduction"
data-tooltip="DR (shield)"
disabled
value={{damageReductionShield}}
/>
</div>
</fieldset> </fieldset>
</div> </div>
<div class="character-right"> <div class="character-right">
<div class="character-name"> <div class="character-name">
{{formInput fields.name value=source.name rootId=partId disabled=isPlayMode}} {{formInput
<a class="control" data-action="toggleSheet" data-tooltip="LETHALFANTASY.ToggleSheet" fields.name
data-tooltip-direction="UP"> value=source.name
rootId=partId
disabled=isPlayMode
}}
<a
class="control"
data-action="toggleSheet"
data-tooltip="LETHALFANTASY.ToggleSheet"
data-tooltip-direction="UP"
>
<i class="fa-solid fa-user-{{ifThen isPlayMode 'lock' 'pen'}}"></i> <i class="fa-solid fa-user-{{ifThen isPlayMode 'lock' 'pen'}}"></i>
</a> </a>
</div> </div>
<fieldset class="character-characteristics character-characteristics-{{ifThen isPlayMode 'play' 'edit'}}"> <fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "LETHALFANTASY.Label.Saves"}}</legend> <legend>{{localize "LETHALFANTASY.Label.Saves"}}</legend>
<div class="character-saves"> <div class="character-saves">
<div class="character-save"> <div class="character-save">
<span class="name"><a class="rollable" data-roll-type="save" data-roll-key="will"><i <span class="name"><a
class="lf-roll-small fa-solid fa-dice-d20"></i> class="rollable"
data-roll-type="save"
data-roll-key="will"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.will"}} {{localize "LETHALFANTASY.Label.saves.will"}}
</a></span> </a></span>
{{formField systemFields.saves.fields.will.fields.value value=system.saves.will.value disabled=true}} {{formField
systemFields.saves.fields.will.fields.value
value=system.saves.will.value
disabled=true
}}
<span class="name"> <span class="name">
<a class="rollable" data-roll-type="save" data-roll-key="dodge"><i <a
class="lf-roll-small fa-solid fa-dice-d20"></i> class="rollable"
data-roll-type="save"
data-roll-key="dodge"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.dodge"}} {{localize "LETHALFANTASY.Label.saves.dodge"}}
</a> </a>
</span> </span>
{{formField systemFields.saves.fields.dodge.fields.value value=system.saves.dodge.value {{formField
disabled=true}} systemFields.saves.fields.dodge.fields.value
value=system.saves.dodge.value
disabled=true
}}
<span class="name"> <span class="name">
<a class="rollable" data-roll-type="save" data-roll-key="toughness"><i <a
class="lf-roll-small fa-solid fa-dice-d20"></i> class="rollable"
data-roll-type="save"
data-roll-key="toughness"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.toughness"}} {{localize "LETHALFANTASY.Label.saves.toughness"}}
</a> </a>
</span> </span>
{{formField systemFields.saves.fields.toughness.fields.value value=system.saves.toughness.value {{formField
disabled=true}} systemFields.saves.fields.toughness.fields.value
value=system.saves.toughness.value
disabled=true
}}
</div> </div>
<div class="character-save"> <div class="character-save">
<span class="name"> <span class="name">
<a class="rollable" data-roll-type="save" data-roll-key="contagion"><i <a
class="lf-roll-small fa-solid fa-dice-d20"></i> class="rollable"
data-roll-type="save"
data-roll-key="contagion"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.contagion"}} {{localize "LETHALFANTASY.Label.saves.contagion"}}
</a> </a>
</span> </span>
{{formField systemFields.saves.fields.contagion.fields.value value=system.saves.contagion.value {{formField
disabled=true}} systemFields.saves.fields.contagion.fields.value
value=system.saves.contagion.value
disabled=true
}}
<span class="name"> <span class="name">
<a class="rollable" data-roll-type="save" data-roll-key="poison"><i <a
class="lf-roll-small fa-solid fa-dice-d20"></i> class="rollable"
data-roll-type="save"
data-roll-key="poison"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.poison"}} {{localize "LETHALFANTASY.Label.saves.poison"}}
</a> </a>
</span> </span>
{{formField systemFields.saves.fields.poison.fields.value value=system.saves.poison.value {{formField
disabled=true }} systemFields.saves.fields.poison.fields.value
value=system.saves.poison.value
disabled=true
}}
<!-- <!--
<span class="name-pain"> <span class="name-pain">
@@ -125,51 +217,124 @@
</div> </div>
</fieldset> </fieldset>
<fieldset class="character-characteristics character-characteristics-{{ifThen isPlayMode 'play' 'edit'}}"> <fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "LETHALFANTASY.Label.Challenges"}}</legend> <legend>{{localize "LETHALFANTASY.Label.Challenges"}}</legend>
<div class="character-challenges"> <div class="character-challenges">
<div class="character-challenge"> <div class="character-challenge">
<span class="name"><a class="rollable" data-roll-type="challenge" data-roll-key="str"><i <span class="name"><a
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize class="rollable"
"LETHALFANTASY.Label.challenges.strength"}}</a></span> data-roll-type="challenge"
{{formField systemFields.challenges.fields.str.fields.value value=system.challenges.str.value data-roll-key="str"
disabled=true ><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.challenges.strength"
}}</a></span>
{{formField
systemFields.challenges.fields.str.fields.value
value=system.challenges.str.value
disabled=true
}} }}
<span class="name"><a class="rollable" data-roll-type="challenge" data-roll-key="agility"><i <span class="name"><a
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize class="rollable"
"LETHALFANTASY.Label.challenges.agility"}}</a></span> data-roll-type="challenge"
{{formField systemFields.challenges.fields.agility.fields.value value=system.challenges.agility.value data-roll-key="agility"
disabled=true ><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.challenges.agility"
}}</a></span>
{{formField
systemFields.challenges.fields.agility.fields.value
value=system.challenges.agility.value
disabled=true
}}
<span class="name"><a
class="rollable"
data-roll-type="challenge"
data-roll-key="dying"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.challenges.dying"
}}</a></span>
{{formField
systemFields.challenges.fields.dying.fields.value
value=system.challenges.dying.value
disabled=true
}} }}
<span class="name"><a class="rollable" data-roll-type="challenge" data-roll-key="dying"><i
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.challenges.dying"}}</a></span>
{{formField systemFields.challenges.fields.dying.fields.value value=system.challenges.dying.value
disabled=true }}
</div> </div>
</div> </div>
</fieldset> </fieldset>
<fieldset class="character-characteristics character-characteristics-{{ifThen isPlayMode 'play' 'edit'}}"> <fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "LETHALFANTASY.Label.Movement"}}</legend> <legend>{{localize "LETHALFANTASY.Label.Movement"}}</legend>
<div class="character-movements"> <div class="character-movements">
<div class="character-movement"> <div class="character-movement">
<span class="name">{{localize "LETHALFANTASY.Label.movement.walk"}}</span> <span class="name">{{localize
{{formField systemFields.movement.fields.walk value=system.movement.walk disabled=isPlayMode}} "LETHALFANTASY.Label.movement.walk"
<span class="name">{{localize "LETHALFANTASY.Label.movement.jog"}}</span> }}</span>
{{formField systemFields.movement.fields.jog value=system.movement.jog disabled=isPlayMode}} {{formField
<span class="name">{{localize "LETHALFANTASY.Label.movement.run"}}</span> systemFields.movement.fields.walk
{{formField systemFields.movement.fields.run value=system.movement.run disabled=isPlayMode}} value=system.movement.walk
<span class="name">{{localize "LETHALFANTASY.Label.movement.sprint"}}</span> disabled=isPlayMode
{{formField systemFields.movement.fields.sprint value=system.movement.sprint disabled=isPlayMode}} }}
<span class="name">{{localize
"LETHALFANTASY.Label.movement.jog"
}}</span>
{{formField
systemFields.movement.fields.jog
value=system.movement.jog
disabled=isPlayMode
}}
<span class="name">{{localize
"LETHALFANTASY.Label.movement.run"
}}</span>
{{formField
systemFields.movement.fields.run
value=system.movement.run
disabled=isPlayMode
}}
<span class="name">{{localize
"LETHALFANTASY.Label.movement.sprint"
}}</span>
{{formField
systemFields.movement.fields.sprint
value=system.movement.sprint
disabled=isPlayMode
}}
</div> </div>
<div class="character-movement"> <div class="character-movement">
<span class="name">{{localize "LETHALFANTASY.Label.movement.jumpBroad"}}</span> <span class="name">{{localize
{{formField systemFields.jump.fields.broad value=system.jump.broad disabled=isPlayMode}} "LETHALFANTASY.Label.movement.jumpBroad"
<span class="name">{{localize "LETHALFANTASY.Label.movement.jumpRunning"}}</span> }}</span>
{{formField systemFields.jump.fields.running value=system.jump.running disabled=isPlayMode}} {{formField
<span class="name">{{localize "LETHALFANTASY.Label.movement.jumpVertical"}}</span> systemFields.jump.fields.broad
{{formField systemFields.jump.fields.vertical value=system.jump.vertical disabled=isPlayMode}} value=system.jump.broad
disabled=isPlayMode
}}
<span class="name">{{localize
"LETHALFANTASY.Label.movement.jumpRunning"
}}</span>
{{formField
systemFields.jump.fields.running
value=system.jump.running
disabled=isPlayMode
}}
<span class="name">{{localize
"LETHALFANTASY.Label.movement.jumpVertical"
}}</span>
{{formField
systemFields.jump.fields.vertical
value=system.jump.vertical
disabled=isPlayMode
}}
</div> </div>
</div> </div>
</fieldset> </fieldset>
@@ -178,62 +343,124 @@
</div> </div>
</fieldset> </fieldset>
<fieldset class="character-characteristics character-characteristics-{{ifThen isPlayMode 'play' 'edit'}}"> <fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "LETHALFANTASY.Label.characteristics"}}</legend> <legend>{{localize "LETHALFANTASY.Label.characteristics"}}</legend>
<div class="character-characteristic"> <div class="character-characteristic">
<span>{{localize "LETHALFANTASY.Label.str"}}</span> <span>{{localize "LETHALFANTASY.Label.str"}}</span>
{{formField systemFields.characteristics.fields.str.fields.value value=system.characteristics.str.value {{formField
disabled=isPlayMode data-char-id="str" }} systemFields.characteristics.fields.str.fields.value
{{formField systemFields.characteristics.fields.str.fields.percent value=system.characteristics.str.percent value=system.characteristics.str.value
disabled=isPlayMode type="number"}} disabled=isPlayMode
data-char-id="str"
}}
{{formField
systemFields.characteristics.fields.str.fields.percent
value=system.characteristics.str.percent
disabled=isPlayMode
type="number"
}}
</div> </div>
<div class="character-characteristic"> <div class="character-characteristic">
<span>{{localize "LETHALFANTASY.Label.int"}}</span> <span>{{localize "LETHALFANTASY.Label.int"}}</span>
{{formField systemFields.characteristics.fields.int.fields.value value=system.characteristics.int.value {{formField
disabled=isPlayMode data-char-id="int" }} systemFields.characteristics.fields.int.fields.value
value=system.characteristics.int.value
disabled=isPlayMode
data-char-id="int"
}}
{{formField systemFields.characteristics.fields.int.fields.percent value=system.characteristics.int.percent {{formField
disabled=isPlayMode type="number" }} systemFields.characteristics.fields.int.fields.percent
value=system.characteristics.int.percent
disabled=isPlayMode
type="number"
}}
</div> </div>
<div class="character-characteristic"> <div class="character-characteristic">
<span>{{localize "LETHALFANTASY.Label.wis"}}</span> <span>{{localize "LETHALFANTASY.Label.wis"}}</span>
{{formField systemFields.characteristics.fields.wis.fields.value value=system.characteristics.wis.value {{formField
disabled=isPlayMode data-char-id="wis" }} systemFields.characteristics.fields.wis.fields.value
value=system.characteristics.wis.value
disabled=isPlayMode
data-char-id="wis"
}}
{{formField systemFields.characteristics.fields.wis.fields.percent value=system.characteristics.wis.percent {{formField
disabled=isPlayMode type="number"}} systemFields.characteristics.fields.wis.fields.percent
value=system.characteristics.wis.percent
disabled=isPlayMode
type="number"
}}
</div> </div>
<div class="character-characteristic"> <div class="character-characteristic">
<span>{{localize "LETHALFANTASY.Label.dex"}}</span> <span>{{localize "LETHALFANTASY.Label.dex"}}</span>
{{formField systemFields.characteristics.fields.dex.fields.value value=system.characteristics.dex.value {{formField
disabled=isPlayMode data-char-id="wis" }} systemFields.characteristics.fields.dex.fields.value
value=system.characteristics.dex.value
disabled=isPlayMode
data-char-id="wis"
}}
{{formField systemFields.characteristics.fields.dex.fields.percent value=system.characteristics.dex.percent {{formField
disabled=isPlayMode type="number" }} systemFields.characteristics.fields.dex.fields.percent
value=system.characteristics.dex.percent
disabled=isPlayMode
type="number"
}}
</div> </div>
<div class="character-characteristic"> <div class="character-characteristic">
<span>{{localize "LETHALFANTASY.Label.con"}}</span> <span>{{localize "LETHALFANTASY.Label.con"}}</span>
{{formField systemFields.characteristics.fields.con.fields.value value=system.characteristics.con.value {{formField
disabled=isPlayMode data-char-id="con" }} systemFields.characteristics.fields.con.fields.value
value=system.characteristics.con.value
disabled=isPlayMode
data-char-id="con"
}}
{{formField systemFields.characteristics.fields.con.fields.percent value=system.characteristics.con.percent {{formField
disabled=isPlayMode type="number"}} systemFields.characteristics.fields.con.fields.percent
value=system.characteristics.con.percent
disabled=isPlayMode
type="number"
}}
</div> </div>
<div class="character-characteristic"> <div class="character-characteristic">
<span>{{localize "LETHALFANTASY.Label.cha"}}</span> <span>{{localize "LETHALFANTASY.Label.cha"}}</span>
{{formField systemFields.characteristics.fields.cha.fields.value value=system.characteristics.cha.value {{formField
disabled=isPlayMode data-char-id="cha" }} systemFields.characteristics.fields.cha.fields.value
value=system.characteristics.cha.value
disabled=isPlayMode
data-char-id="cha"
}}
{{formField systemFields.characteristics.fields.cha.fields.percent value=system.characteristics.cha.percent {{formField
disabled=isPlayMode type="number"}} systemFields.characteristics.fields.cha.fields.percent
value=system.characteristics.cha.percent
disabled=isPlayMode
type="number"
}}
</div> </div>
<div class="character-characteristic"> <div class="character-characteristic">
<span>{{localize "LETHALFANTASY.Label.luc"}}</span> <span>{{localize "LETHALFANTASY.Label.luc"}}</span>
{{formField systemFields.characteristics.fields.luc.fields.value value=system.characteristics.luc.value {{formField
disabled=isPlayMode data-char-id="luc" }} systemFields.characteristics.fields.luc.fields.value
value=system.characteristics.luc.value
disabled=isPlayMode
data-char-id="luc"
}}
{{formField systemFields.characteristics.fields.luc.fields.percent value=system.characteristics.luc.percent {{formField
disabled=isPlayMode type="number"}} systemFields.characteristics.fields.luc.fields.percent
value=system.characteristics.luc.percent
disabled=isPlayMode
type="number"
}}
</div> </div>
</fieldset> </fieldset>

View File

@@ -1,73 +1,145 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="skills" data-group="sheet"> <section
class="tab character-{{tab.id}} {{tab.cssClass}}"
data-tab="skills"
data-group="sheet"
>
<div class="main-div"> <div class="main-div">
<fieldset> {{#each skillsByCategory as |categoryData|}}
<legend data-tooltip="{{localize " LETHALFANTASY.Tooltip.skills"}}" data-tooltip-direction="UP">{{localize <fieldset>
"LETHALFANTASY.Label.skills"}}</legend> <legend
<div class="skills"> data-tooltip="{{localize ' LETHALFANTASY.Tooltip.skills'}}"
{{#each skills as |item|}} data-tooltip-direction="UP"
<div class="skill " data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"> >{{localize categoryData.label}}</legend>
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" /> <div class="skills">
<div class="name"> {{#each categoryData.skills as |item|}}
<a class="rollable" data-roll-type="skill" data-roll-key="{{item.id}}"> <div
<i class="lf-roll-small fa-duotone fa-solid fa-dice-d10"></i> class="skill"
{{item.name}} data-item-id="{{item.id}}"
</a> data-item-uuid="{{item.uuid}}"
>
<img
class="item-img"
src="{{item.img}}"
data-tooltip="{{item.name}}"
/>
<div class="name">
<a
class="rollable"
data-roll-type="skill"
data-roll-key="{{item.id}}"
>
<i class="lf-roll-small fa-duotone fa-solid fa-dice-d10"></i>
{{item.name}}
</a>
</div>
<div class="score">
+{{item.system.skillTotal}}
</div>
<div class="controls">
<a
data-tooltip="{{localize 'LETHALFANTASY.Edit'}}"
data-action="edit"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
><i class="fas fa-edit"></i></a>
<a
data-tooltip="{{localize 'LETHALFANTASY.Delete'}}"
data-action="delete"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
><i class="fas fa-trash"></i></a>
</div>
</div>
{{/each}}
</div> </div>
<div class="score"> </fieldset>
+{{item.system.skillTotal}} {{/each}}
</div>
<div class="controls">
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'LETHALFANTASY.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{/each}}
</div>
</fieldset>
<fieldset> <fieldset>
<legend data-tooltip="{{localize " LETHALFANTASY.Tooltip.gifts"}}" data-tooltip-direction="UP">{{localize <legend
"LETHALFANTASY.Label.gifts"}}</legend> data-tooltip="{{localize ' LETHALFANTASY.Tooltip.gifts'}}"
<div class="gifts"> data-tooltip-direction="UP"
{{#each gifts as |item|}} >{{localize "LETHALFANTASY.Label.gifts"}}</legend>
<div class="gift " data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"> <div class="gifts">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" /> {{#each gifts as |item|}}
<div class="name" data-tooltip="{{{item.description}}}<br><br>{{item.path}}" data-tooltip-direction="UP"> <div
{{item.name}} class="gift"
</div> data-item-id="{{item.id}}"
<div class="controls"> data-item-uuid="{{item.uuid}}"
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}" >
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a> <img
<a data-tooltip="{{localize 'LETHALFANTASY.Delete'}}" data-action="delete" data-item-id="{{item.id}}" class="item-img"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a> src="{{item.img}}"
</div> data-tooltip="{{item.name}}"
/>
<div
class="name"
data-tooltip="{{{item.description}}}<br><br>{{item.path}}"
data-tooltip-direction="UP"
>
{{item.name}}
</div>
<div class="controls">
<a
data-tooltip="{{localize 'LETHALFANTASY.Edit'}}"
data-action="edit"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
><i class="fas fa-edit"></i></a>
<a
data-tooltip="{{localize 'LETHALFANTASY.Delete'}}"
data-action="delete"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
><i class="fas fa-trash"></i></a>
</div>
</div>
{{/each}}
</div> </div>
{{/each}} </fieldset>
</div>
</fieldset>
<fieldset> <fieldset>
<legend data-tooltip="{{localize " LETHALFANTASY.Tooltip.vulnerabilities"}}" data-tooltip-direction="UP">{{localize <legend
"LETHALFANTASY.Label.vulnerabilities"}}</legend> data-tooltip="{{localize ' LETHALFANTASY.Tooltip.vulnerabilities'}}"
<div class="vulnerabilities"> data-tooltip-direction="UP"
{{#each vulnerabilities as |item|}} >{{localize "LETHALFANTASY.Label.vulnerabilities"}}</legend>
<div class="vulnerability " data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"> <div class="vulnerabilities">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" /> {{#each vulnerabilities as |item|}}
<div class="name" data-tooltip="{{{item.description}}}<br><br>{{item.path}}" data-tooltip-direction="UP"> <div
{{item.name}} class="vulnerability"
</div> data-item-id="{{item.id}}"
<div class="controls"> data-item-uuid="{{item.uuid}}"
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}" >
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a> <img
<a data-tooltip="{{localize 'LETHALFANTASY.Delete'}}" data-action="delete" data-item-id="{{item.id}}" class="item-img"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a> src="{{item.img}}"
</div> data-tooltip="{{item.name}}"
/>
<div
class="name"
data-tooltip="{{{item.description}}}<br><br>{{item.path}}"
data-tooltip-direction="UP"
>
{{item.name}}
</div>
<div class="controls">
<a
data-tooltip="{{localize 'LETHALFANTASY.Edit'}}"
data-action="edit"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
><i class="fas fa-edit"></i></a>
<a
data-tooltip="{{localize 'LETHALFANTASY.Delete'}}"
data-action="delete"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
><i class="fas fa-trash"></i></a>
</div>
</div>
{{/each}}
</div> </div>
{{/each}} </fieldset>
</div> </div>
</fieldset>
</div>
</section> </section>

View File

@@ -1,82 +1,291 @@
{{!log 'chat-message' this}} {{!log 'chat-message' this}}
{{!log 'weaponDamageOptions' weaponDamageOptions}}
<div class="{{cssClass}}"> <div class="{{cssClass}}">
<div class="intro-chat"> <!-- Header with character info -->
<div class="intro-img"> <div class="chat-header">
<img src="{{actingCharImg}}" data-tooltip="{{actingCharName}}" /> <div class="character-info">
</div> <div class="character-avatar">
<img src="{{actingCharImg}}" data-tooltip="{{actingCharName}}" />
<div class="intro-right"> </div>
<span>{{actingCharName}} - {{upperFirst rollName}}</span> <div class="character-details">
<div class="character-name">{{actingCharName}}</div>
{{#if (match rollType "attack")}} <div class="roll-type-badge">
<span>Attack roll !</span> {{#if (match rollType "attack")}}
{{/if}} <i class="fa-solid fa-swords"></i>
{{#if (match rollType "defense")}} {{/if}}
<span>Defense roll !</span> {{#if (match rollType "defense")}}
{{/if}} <i class="fa-solid fa-shield"></i>
{{/if}}
{{#if badResult}} {{#if rollData.isDamage}}
<span>{{localize "LETHALFANTASY.Label.otherResult"}} : {{badResult}}</span> <i class="fa-solid fa-burst"></i>
{{/if}} {{/if}}
{{#if (eq rollType "skill")}}
{{#if rollTarget.weapon}} <i class="fa-solid fa-hand-sparkles"></i>
<span>{{rollTarget.weapon.name}}</span> {{/if}}
{{/if}} {{upperFirst rollName}}
</div>
{{#if rollData.letItFly}} </div>
<span>Let It Fly attack ! </span>
{{/if}}
{{#if rollData.pointBlank}}
<span>Point Blank Range Attack !</span>
{{/if}}
{{#if rollData.beyondSkill}}
<span>Beyond Skill Range Attack !</span>
{{/if}}
<span>Formula : {{titleFormula}}</span>
{{#each diceResults as |result|}}
<span>{{result.dice}} : {{result.value}}</span>
{{/each}}
</div> </div>
</div> </div>
{{#if isSave}}
<div class="result">
{{#if (eq resultType "success")}}
{{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.success"}}{{/if}}
{{else}}
{{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.failure"}}{{/if}}
{{/if}}
</div>
{{/if}}
{{#if isResource}}
<div class="result">
{{#if (eq resultType "success")}}
{{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.success"}}{{/if}}
{{else}}
{{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.failure"}}{{#if isFailure}} ({{localize
"LETHALFANTASY.Roll.resourceLost"}}){{/if}}{{/if}}
{{/if}}
</div>
{{/if}}
{{#if isDamage}}
<div>
{{#if (and isGM hasTarget)}}
{{{localize "LETHALFANTASY.Roll.displayArmor" targetName=targetName targetArmor=targetArmor realDamage=realDamage}}}
{{/if}}
</div>
{{/if}}
<!-- Roll details and modifiers -->
<div class="roll-details">
{{#if rollTarget.weapon}}
<div class="detail-item weapon-name">
<i class="fa-solid fa-sword"></i>
<span>{{rollTarget.weapon.name}}</span>
</div>
{{/if}}
{{#if (eq rollData.favor "favor")}}
<div class="detail-badge favor-badge">
<i class="fa-solid fa-sparkles"></i>
<span>Favor</span>
</div>
{{/if}}
{{#if (eq rollData.favor "disfavor")}}
<div class="detail-badge disfavor-badge">
<i class="fa-solid fa-skull"></i>
<span>Disfavor</span>
</div>
{{/if}}
{{#if rollData.letItFly}}
<div class="detail-badge special-badge">
<i class="fa-solid fa-bow-arrow"></i>
<span>Let It Fly!</span>
</div>
{{/if}}
{{#if rollData.pointBlank}}
<div class="detail-badge special-badge">
<i class="fa-solid fa-bullseye-arrow"></i>
<span>Point Blank</span>
</div>
{{/if}}
{{#if rollData.beyondSkill}}
<div class="detail-badge special-badge">
<i class="fa-solid fa-target-lock"></i>
<span>Beyond Skill</span>
</div>
{{/if}}
{{#if rollData.damageSmall}}
<div class="detail-badge damage-badge">
<i class="fa-solid fa-dice-d6"></i>
<span>{{localize "LETHALFANTASY.Label.weapon-damage-small"}}</span>
</div>
{{/if}}
{{#if rollData.damageMedium}}
<div class="detail-badge damage-badge">
<i class="fa-solid fa-dice-d20"></i>
<span>{{localize "LETHALFANTASY.Label.weapon-damage-medium"}}</span>
</div>
{{/if}}
{{#if badResult}}
<div class="detail-item other-result">
<strong>{{localize "LETHALFANTASY.Label.otherResult"}}</strong>: {{badResult}}
</div>
{{/if}}
</div>
<!-- Formula and dice results -->
<div class="formula-section">
<div class="formula-display">
<i class="fa-solid fa-dice-d20"></i>
<span class="formula-text">{{titleFormula}}</span>
</div>
{{#if diceResults}}
<div class="dice-breakdown">
{{#each diceResults as |result|}}
<div class="dice-item">
<span class="dice-type">{{result.dice}}</span>
<span class="dice-separator">→</span>
<span class="dice-value">{{result.value}}</span>
</div>
{{/each}}
</div>
{{/if}}
</div>
<!-- Result section -->
{{#unless isPrivate}} {{#unless isPrivate}}
<div class="dice-result"> <div class="result-section">
<h4 class="dice-total">{{total}}</h4> <div class="main-result">
</div> <div class="result-label">Total</div>
{{#if D30result}} <div class="result-value {{#if (eq resultType 'success')}}success{{else}}failure{{/if}}">
<div class="dice-result"> {{total}}
<h4 class="dice-total">D30 result: {{D30result}}</h4> </div>
</div> </div>
{{#if D30result}}
<div class="d30-result">
<div class="d30-header">
<i class="fa-solid fa-dice-d20"></i>
<span class="d30-label">D30 Special</span>
<span class="d30-value">{{D30result}}</span>
</div>
{{#if D30message}}
<div class="d30-message">
<i class="fa-solid fa-wand-magic-sparkles"></i>
<span>{{D30message}}</span>
</div>
{{/if}}
</div>
{{/if}}
</div>
{{#if isSave}}
<div class="outcome-badge {{#if (eq resultType 'success')}}success-outcome{{else}}failure-outcome{{/if}}">
{{#if (eq resultType "success")}}
<i class="fa-solid fa-circle-check"></i>
{{localize "LETHALFANTASY.Roll.success"}}
{{else}}
<i class="fa-solid fa-circle-xmark"></i>
{{localize "LETHALFANTASY.Roll.failure"}}
{{/if}}
</div>
{{/if}}
{{#if isResource}}
<div class="outcome-badge {{#if (eq resultType 'success')}}success-outcome{{else}}failure-outcome{{/if}}">
{{#if (eq resultType "success")}}
<i class="fa-solid fa-circle-check"></i>
{{localize "LETHALFANTASY.Roll.success"}}
{{else}}
<i class="fa-solid fa-circle-xmark"></i>
{{localize "LETHALFANTASY.Roll.failure"}}
{{#if isFailure}}
<span class="resource-lost">({{localize "LETHALFANTASY.Roll.resourceLost"}})</span>
{{/if}}
{{/if}}
</div>
{{/if}}
{{#if isDamage}}
{{#if (and isGM hasTarget)}}
<div class="target-info">
<i class="fa-solid fa-crosshairs"></i>
{{{localize
"LETHALFANTASY.Roll.displayArmor"
targetName=targetName
targetArmor=targetArmor
realDamage=realDamage
}}}
</div>
{{/if}}
{{/if}}
{{else}}
<div class="private-result">
<i class="fa-solid fa-eye-slash"></i>
<span>Private Roll</span>
</div>
{{/unless}}
{{#if weaponDamageOptions}}
{{#if canSelectTarget}}
<div class="damage-buttons">
<div class="damage-buttons-grid {{#if weaponDamageOptions.isMonster}}monster-damage{{/if}}">
{{#if weaponDamageOptions.isMonster}}
<button
class="damage-roll-btn"
data-weapon-id="{{weaponDamageOptions.weaponId}}"
data-damage-type="monster"
data-damage-formula="{{weaponDamageOptions.damageFormula}}"
data-damage-modifier="{{weaponDamageOptions.damageModifier}}"
data-is-monster="true"
title="{{localize 'LETHALFANTASY.Label.rollDamage'}} - {{weaponDamageOptions.weaponName}}"
>
<i class="fa-solid fa-dice"></i>
Damage:
{{weaponDamageOptions.damageFormula}}{{#if
weaponDamageOptions.damageModifier
}}+{{weaponDamageOptions.damageModifier}}{{/if}}
</button>
{{else}}
{{#if weaponDamageOptions.damageS}}
<button
class="damage-roll-btn"
data-weapon-id="{{weaponDamageOptions.weaponId}}"
data-damage-type="small"
data-damage-formula="{{weaponDamageOptions.damageS}}"
data-is-monster="false"
title="{{localize 'LETHALFANTASY.Label.rollDamage'}}"
>
<i class="fa-solid fa-dice-d6"></i>
Damage Small
</button>
{{/if}}
{{#if weaponDamageOptions.damageM}}
<button
class="damage-roll-btn"
data-weapon-id="{{weaponDamageOptions.weaponId}}"
data-damage-type="medium"
data-damage-formula="{{weaponDamageOptions.damageM}}"
data-is-monster="false"
title="{{localize 'LETHALFANTASY.Label.rollDamage'}}"
>
<i class="fa-solid fa-dice-d20"></i>
Damage Medium
</button>
{{/if}}
{{/if}}
</div>
</div>
{{/if}}
{{/if}}
{{#if rollData.isDamage}}
{{#if defenderId}}
<div class="damage-result auto-applied">
<div class="auto-damage-notice">
<i class="fa-solid fa-check-circle"></i>
<span>Damage automatically applied to defender (with Armor DR)</span>
</div>
</div>
{{else}}
<div class="damage-result">
<ul>
<li class="li-apply-wounds">
<div>{{localize "LETHALFANTASY.Label.applyDamage"}}</div>
<div class="combatants-grid">
{{#each combatants}}
<button
class="apply-wounds-btn"
data-combatant-id="{{this.id}}"
title="{{this.name}}"
>
{{this.name}}
</button>
{{/each}}
</div>
</li>
</ul>
</div>
{{/if}}
{{/if}}
{{#if isAttack}}
{{#if canSelectTarget}}
<div class="attack-targets">
<ul>
<li class="li-select-target">
<div>{{localize "LETHALFANTASY.Label.selectTarget"}}</div>
<div class="combatants-grid">
{{#each combatants}}
<button
class="request-defense-btn"
data-combatant-id="{{this.id}}"
data-token-id="{{this.tokenId}}"
title="{{this.name}}"
>
{{this.name}}
</button>
{{/each}}
</div>
</li>
</ul>
</div>
{{/if}}
{{/if}} {{/if}}
{{/unless}}
</div> </div>

View File

@@ -0,0 +1,36 @@
<div class="fvtt-lethal-fantasy damage-applied-message">
<div class="damage-header">
<div class="damage-icon">
<i class="fa-solid fa-heart-crack"></i>
</div>
<div class="damage-info">
<div class="target-name">{{targetName}}</div>
<div class="damage-subtitle">{{localize "LETHALFANTASY.DamageApplied.subtitle"}}</div>
</div>
</div>
<div class="damage-details">
<div class="damage-amount">
<span class="damage-label">{{localize "LETHALFANTASY.DamageApplied.damageDealt"}}</span>
<span class="damage-value">{{damage}}</span>
{{#if rawDamage}}
<span class="damage-raw">({{rawDamage}} raw)</span>
{{/if}}
</div>
{{#if drText}}
<div class="damage-reduction">
<i class="fa-solid fa-shield"></i>
<span>{{drText}}</span>
</div>
{{/if}}
<div class="damage-source">
<i class="fa-solid fa-crosshairs"></i>
<span>{{localize "LETHALFANTASY.DamageApplied.from"}}
{{#if attackerName}}<strong>{{attackerName}}</strong> with {{/if}}
<strong>{{weaponName}}</strong>
</span>
</div>
</div>
</div>

View File

@@ -22,7 +22,7 @@
{{#each system.attacks as |item key|}} {{#each system.attacks as |item key|}}
<div class="attack" data-attack-key="{{key}}" > <div class="attack" data-attack-key="{{key}}" >
<div class=""> <div class="">
<input type="checkbox" name="system.attacks.{{item.key}}.enabled" value="{{item.enabled}}" data-tooltip="Attack enabled/disabled" /> <input type="checkbox" name="system.attacks.{{item.key}}.enabled" {{checked item.enabled}} data-tooltip="Attack enabled/disabled" />
</div> </div>
<div class="name"> <div class="name">
<input type="text" name="system.attacks.{{item.key}}.name" value="{{item.name}}" data-tooltip="Attack name" /> <input type="text" name="system.attacks.{{item.key}}.name" value="{{item.name}}" data-tooltip="Attack name" />
@@ -65,4 +65,54 @@
{{/each}} {{/each}}
</div> </div>
</fieldset> </fieldset>
<fieldset>
<legend>Hand To Hand Attacks</legend>
<div class="attacks">
{{#each system.combatHTH as |item key|}}
<div class="attack" data-attack-key="{{key}}" >
<div class="">
<input type="checkbox" name="system.combatHTH.{{item.key}}.enabled" {{checked item.enabled}} data-tooltip="HTH Attack enabled/disabled" />
</div>
<div class="name">
<input type="text" name="system.combatHTH.{{item.key}}.name" value="{{item.name}}" data-tooltip="HTH Attack name" />
</div>
<div class="numeric">
<input type="number" name="system.combatHTH.{{item.key}}.attackScore" value="{{item.attackScore}}" data-tooltip="Progression number" />
</div>
<div class="numeric">
<input type="number" name="system.combatHTH.{{item.key}}.attackModifier" value="{{item.attackModifier}}" data-tooltip="Attack modifier" />
</div>
<div class="numeric">
<input type="number" name="system.combatHTH.{{item.key}}.defenseModifier" value="{{item.defenseModifier}}" data-tooltip="Defense modifier"/>
</div>
<div class="damage-dice">
<input type="text" name="system.combatHTH.{{item.key}}.damageDice" value="{{item.damageDice}}" data-tooltip="Damage dice"/>
</div>
<div class="numeric">
<input type="number" name="system.combatHTH.{{item.key}}.damageModifier" value="{{item.damageModifier}}" data-tooltip="Damage modifier"/>
</div>
<div class="attack-icons">
<a class="rollable" data-roll-type="monster-attack-hth" data-roll-key="{{item.key}}" data-tooltip="Roll HTH Attack">
<i class="lf-roll-small fa-solid fa-hand-fist" data-roll-type="monster-attack-hth" data-roll-key="{{item.key}}"></i>
</a>
<a class="rollable" data-roll-type="monster-defense-hth" data-roll-key="{{item.key}}" data-tooltip="Roll HTH Defense">
<i class="fa-solid fa-shield-halved" data-roll-type="monster-defense-hth" data-roll-key="{{item.key}}"></i>
</a>
<a class="rollable" data-roll-type="monster-damage-hth" data-roll-key="{{item.key}}"
data-tooltip="Roll HTH Damage">
<i class="fa-regular fa-face-head-bandage" data-roll-type="monster-damage-hth"
data-roll-key="{{item.key}}"></i>
</a>
</div>
</div>
{{/each}}
</div>
</fieldset>
</section> </section>

View File

@@ -6,79 +6,177 @@
<div class="monster-pc monster-pc-{{ifThen isPlayMode 'play' 'edit'}}"> <div class="monster-pc monster-pc-{{ifThen isPlayMode 'play' 'edit'}}">
<div class="monster-left"> <div class="monster-left">
<div class="monster-left-image"> <div class="monster-left-image">
<img class="monster-img" src="{{actor.img}}" data-edit="img" data-action="editImage" <img
data-tooltip="{{actor.name}}" /> class="monster-img"
src="{{actor.img}}"
data-edit="img"
data-action="editImage"
data-tooltip="{{actor.name}}"
/>
</div> </div>
<fieldset class="monster-characteristics monster-characteristics-{{ifThen isPlayMode 'play' 'edit'}}"> <fieldset
class="monster-characteristics monster-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<div class="flexrow monster-hp"> <div class="flexrow monster-hp">
<span class="name">{{localize "LETHALFANTASY.Label.HP"}}</span> <span class="name">{{localize "LETHALFANTASY.Label.HP"}}</span>
{{formInput systemFields.hp.fields.value value=system.hp.value disabled=isPlayMode classes="monster-hp-value"}} {{formInput
systemFields.hp.fields.value
value=system.hp.value
disabled=isPlayMode
classes="monster-hp-value"
}}
&nbsp;/&nbsp; &nbsp;/&nbsp;
{{formInput systemFields.hp.fields.max value=system.hp.max disabled=isPlayMode classes="monster-hp-value"}} {{formInput
systemFields.hp.fields.max
value=system.hp.max
disabled=isPlayMode
classes="monster-hp-value"
}}
</div> </div>
<div class="flexrow monster-hp"> <div class="flexrow monster-hp">
<span class="damage-resistance">{{localize "LETHALFANTASY.Label.damageResistance"}}</span> <span class="name">{{localize
{{formInput systemFields.hp.fields.damageResistance value=system.hp.damageResistance disabled=isPlayMode classes="monster-hp-value"}} "LETHALFANTASY.Label.damageResistance"
}}</span>
{{formInput
systemFields.hp.fields.damageResistance
value=system.hp.damageResistance
disabled=isPlayMode
classes="monster-hp-value"
}}
<span class="name">{{localize
"LETHALFANTASY.Label.shieldDamageReduction"
}}</span>
{{formInput
systemFields.combat.fields.shieldDamageReduction
value=system.combat.shieldDamageReduction
disabled=isPlayMode
classes="monster-hp-value"
}}
</div>
<div class="flexrow monster-hp">
<span class="name"><a
class="rollable"
data-roll-type="shield-defense"
data-roll-dice={{system.combat.shieldDefenseDice}}
><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.shieldDefenseDice"
}}</a></span>
{{formInput
systemFields.combat.fields.shieldDefenseDice
value=system.combat.shieldDefenseDice
disabled=isPlayMode
classes="monster-hp-value"
}}
</div> </div>
</fieldset> </fieldset>
</div> </div>
<div class="monster-right"> <div class="monster-right">
<div class="monster-name"> <div class="monster-name">
{{formInput fields.name value=source.name rootId=partId disabled=isPlayMode}} {{formInput
<a class="control" data-action="toggleSheet" data-tooltip="LETHALFANTASY.ToggleSheet" fields.name
data-tooltip-direction="UP"> value=source.name
rootId=partId
disabled=isPlayMode
}}
<a
class="control"
data-action="toggleSheet"
data-tooltip="LETHALFANTASY.ToggleSheet"
data-tooltip-direction="UP"
>
<i class="fa-solid fa-user-{{ifThen isPlayMode 'lock' 'pen'}}"></i> <i class="fa-solid fa-user-{{ifThen isPlayMode 'lock' 'pen'}}"></i>
</a> </a>
</div> </div>
<fieldset class="monster-characteristics monster-characteristics-{{ifThen isPlayMode 'play' 'edit'}}"> <fieldset
class="monster-characteristics monster-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "LETHALFANTASY.Label.Saves"}}</legend> <legend>{{localize "LETHALFANTASY.Label.Saves"}}</legend>
<div class="monster-saves"> <div class="monster-saves">
<div class="monster-save"> <div class="monster-save">
<span class="name"><a class="rollable" data-roll-type="save" data-roll-key="will"><i <span class="name"><a
class="lf-roll-small fa-solid fa-dice-d20"></i> class="rollable"
data-roll-type="save"
data-roll-key="will"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.will"}} {{localize "LETHALFANTASY.Label.saves.will"}}
</a></span> </a></span>
{{formField systemFields.saves.fields.will.fields.value value=system.saves.will.value disabled=isPlayMode }} {{formField
systemFields.saves.fields.will.fields.value
value=system.saves.will.value
disabled=isPlayMode
}}
<span class="name"> <span class="name">
<a class="rollable" data-roll-type="save" data-roll-key="dodge"><i <a
class="lf-roll-small fa-solid fa-dice-d20"></i> class="rollable"
data-roll-type="save"
data-roll-key="dodge"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.dodge"}} {{localize "LETHALFANTASY.Label.saves.dodge"}}
</a> </a>
</span> </span>
{{formField systemFields.saves.fields.dodge.fields.value value=system.saves.dodge.value {{formField
disabled=isPlayMode}} systemFields.saves.fields.dodge.fields.value
value=system.saves.dodge.value
disabled=isPlayMode
}}
<span class="name"> <span class="name">
<a class="rollable" data-roll-type="save" data-roll-key="toughness"><i <a
class="lf-roll-small fa-solid fa-dice-d20"></i> class="rollable"
data-roll-type="save"
data-roll-key="toughness"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.toughness"}} {{localize "LETHALFANTASY.Label.saves.toughness"}}
</a> </a>
</span> </span>
{{formField systemFields.saves.fields.toughness.fields.value value=system.saves.toughness.value {{formField
disabled=isPlayMode}} systemFields.saves.fields.toughness.fields.value
value=system.saves.toughness.value
disabled=isPlayMode
}}
</div> </div>
<div class="monster-save"> <div class="monster-save">
<span class="name"> <span class="name">
<a class="rollable" data-roll-type="save" data-roll-key="contagion"><i <a
class="lf-roll-small fa-solid fa-dice-d20"></i> class="rollable"
data-roll-type="save"
data-roll-key="contagion"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.contagion"}} {{localize "LETHALFANTASY.Label.saves.contagion"}}
</a> </a>
</span> </span>
{{formField systemFields.saves.fields.contagion.fields.value value=system.saves.contagion.value {{formField
disabled=isPlayMode}} systemFields.saves.fields.contagion.fields.value
value=system.saves.contagion.value
disabled=isPlayMode
}}
<span class="name"> <span class="name">
<a class="rollable" data-roll-type="save" data-roll-key="poison"><i <a
class="lf-roll-small fa-solid fa-dice-d20"></i> class="rollable"
{{localize "LETHALFANTASY.Label.saves.poison"}} data-roll-type="save"
data-roll-key="poison"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.poison"}}
</a> </a>
</span> </span>
{{formField systemFields.saves.fields.poison.fields.value value=system.saves.poison.value {{formField
disabled=isPlayMode }} systemFields.saves.fields.poison.fields.value
value=system.saves.poison.value
disabled=isPlayMode
}}
<span class="name"> <!-- <span class="name">
<a class="rollable" data-roll-type="save" data-roll-key="paincourage" data-roll-dice="D20" data-tooltip="Pain/Courage check on wound of..."><i <a class="rollable" data-roll-type="save" data-roll-key="paincourage" data-roll-dice="D20" data-tooltip="Pain/Courage check on wound of..."><i
class="lf-roll-small fa-solid fa-dice-d20"></i> class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "LETHALFANTASY.Label.saves.paincourage"}} {{localize "LETHALFANTASY.Label.saves.paincourage"}}
@@ -88,65 +186,88 @@
<span data-tooltip="Pain save if wound exceeds"> <span data-tooltip="Pain save if wound exceeds">
{{formField systemFields.hp.fields.painDamage value=system.hp.painDamage disabled=isPlayMode tooltip="Pain Damage"}} {{formField systemFields.hp.fields.painDamage value=system.hp.painDamage disabled=isPlayMode tooltip="Pain Damage"}}
</span> </span>-->
</div> </div>
</div> </div>
</fieldset> </fieldset>
<fieldset class="monster-characteristics monster-characteristics-{{ifThen isPlayMode 'play' 'edit'}}"> <fieldset
class="monster-characteristics monster-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "LETHALFANTASY.Label.skills"}}</legend> <legend>{{localize "LETHALFANTASY.Label.skills"}}</legend>
<div class="monster-skills"> <div class="monster-skills">
<div class="monster-skill"> <div class="monster-skill">
<span class="name"><a class="rollable" data-roll-type="monster-skill" data-roll-key="resistTorture"><i <span class="name"><a
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize class="rollable"
"LETHALFANTASY.Label.resistTorture"}}</a></span> data-roll-type="monster-skill"
{{formField systemFields.resists.fields.resistTorture.fields.value value=system.resists.resistTorture.value data-roll-key="resistTorture"
disabled=isPlayMode ><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.resistTorture"
}}</a></span>
{{formField
systemFields.resists.fields.resistTorture.fields.value
value=system.resists.resistTorture.value
disabled=isPlayMode
}} }}
<span class="name"><a class="rollable" data-roll-type="monster-skill" data-roll-key="resistPerformance"><i <span class="name"><a
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize class="rollable"
"LETHALFANTASY.Label.resistPerformance"}}</a></span> data-roll-type="monster-skill"
{{formField systemFields.resists.fields.resistPerformance.fields.value value=system.resists.resistPerformance.value data-roll-key="resistPerformance"
disabled=isPlayMode ><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.resistPerformance"
}}</a></span>
{{formField
systemFields.resists.fields.resistPerformance.fields.value
value=system.resists.resistPerformance.value
disabled=isPlayMode
}}
<span class="name"><a
class="rollable"
data-roll-type="monster-skill"
data-roll-key="resistIntimidation"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.resistIntimidation"
}}</a></span>
{{formField
systemFields.resists.fields.resistIntimidation.fields.value
value=system.resists.resistIntimidation.value
disabled=isPlayMode
}} }}
<span class="name"><a class="rollable" data-roll-type="monster-skill" data-roll-key="resistIntimidation"><i
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.resistIntimidation"}}</a></span>
{{formField systemFields.resists.fields.resistIntimidation.fields.value value=system.resists.resistIntimidation.value
disabled=isPlayMode }}
<span class="name"><a class="rollable" data-roll-type="monster-skill" data-roll-key="perception"><i
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.perception"}}</a></span>
{{formField systemFields.resists.fields.perception.fields.value value=system.resists.perception.value
disabled=isPlayMode }}
<span class="name"><a class="rollable" data-roll-type="monster-skill" data-roll-key="stealth"><i
class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.stealth"}}</a></span>
{{formField systemFields.resists.fields.stealth.fields.value value=system.resists.stealth.value
disabled=isPlayMode }}
</div> </div>
</div> <div class="monster-skill">
</fieldset> <span class="name"><a
class="rollable"
data-roll-type="monster-skill"
data-roll-key="perception"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"LETHALFANTASY.Label.perception"
}}</a></span>
{{formField
systemFields.resists.fields.perception.fields.value
value=system.resists.perception.value
disabled=isPlayMode
}}
<span class="name"><a
<fieldset class="monster-characteristics monster-characteristics-{{ifThen isPlayMode 'play' 'edit'}}"> class="rollable"
<legend>{{localize "LETHALFANTASY.Label.Movement"}}</legend> data-roll-type="monster-skill"
<div class="monster-movements"> data-roll-key="stealth"
<div class="monster-movement"> ><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
<span class="name">{{localize "LETHALFANTASY.Label.movement.walk"}}</span> "LETHALFANTASY.Label.stealth"
{{formField systemFields.movement.fields.walk value=system.movement.walk disabled=isPlayMode}} }}</a></span>
<span class="name">{{localize "LETHALFANTASY.Label.movement.jog"}}</span> {{formField
{{formField systemFields.movement.fields.jog value=system.challenges.movement.jog disabled=isPlayMode}} systemFields.resists.fields.stealth.fields.value
<span class="name">{{localize "LETHALFANTASY.Label.movement.run"}}</span> value=system.resists.stealth.value
{{formField systemFields.movement.fields.run value=system.movement.run disabled=isPlayMode}} disabled=isPlayMode
<span class="name">{{localize "LETHALFANTASY.Label.movement.sprint"}}</span> }}
{{formField systemFields.movement.fields.sprint value=system.movement.sprint disabled=isPlayMode}}
</div> </div>
</div> </div>
</fieldset> </fieldset>
@@ -154,23 +275,77 @@
</div> </div>
</fieldset> </fieldset>
<fieldset class="monster-characteristics monster-characteristics-{{ifThen isPlayMode 'play' 'edit'}}"> <fieldset
class="monster-characteristics monster-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "LETHALFANTASY.Label.characteristics"}}</legend> <legend>{{localize "LETHALFANTASY.Label.characteristics"}}</legend>
<div class="monster-characteristic"> <div class="monster-characteristic">
<span>x{{localize "LETHALFANTASY.Label.int"}}</span> <span>{{localize "LETHALFANTASY.Label.int"}}</span>
{{formField systemFields.characteristics.fields.int.fields.value value=system.characteristics.int.value {{formField
disabled=isPlayMode data-char-id="int" }} systemFields.characteristics.fields.int.fields.value
value=system.characteristics.int.value
{{formField systemFields.characteristics.fields.int.fields.percent value=system.characteristics.int.percent disabled=isPlayMode
disabled=isPlayMode type="number" }} data-char-id="int"
}}
</div> </div>
<div class="monster-characteristic"> <div class="monster-characteristic">
<span>{{localize "LETHALFANTASY.Label.dex"}}</span> <span>{{localize "LETHALFANTASY.Label.dex"}}</span>
{{formField systemFields.characteristics.fields.dex.fields.value value=system.characteristics.dex.value {{formField
disabled=isPlayMode data-char-id="wis" }} systemFields.characteristics.fields.dex.fields.value
value=system.characteristics.dex.value
disabled=isPlayMode
data-char-id="wis"
}}
</div>
{{formField systemFields.characteristics.fields.dex.fields.percent value=system.characteristics.dex.percent <legend>{{localize "LETHALFANTASY.Label.Movement"}}</legend>
disabled=isPlayMode type="number" }} <div class="monster-movements">
<div class="monster-movement">
<div class="flexrow monster-hp">
<span class="name">{{localize
"LETHALFANTASY.Label.movement.walk"
}}</span>
{{formField
systemFields.movement.fields.walk
value=system.movement.walk
disabled=isPlayMode
}}
</div>
<div class="flexrow monster-hp">
<span class="name">{{localize
"LETHALFANTASY.Label.movement.jog"
}}</span>
{{formField
systemFields.movement.fields.jog
value=system.movement.jog
disabled=isPlayMode
}}
</div>
<div class="flexrow monster-hp">
<span class="name">{{localize
"LETHALFANTASY.Label.movement.run"
}}</span>
{{formField
systemFields.movement.fields.run
value=system.movement.run
disabled=isPlayMode
}}
</div>
<div class="flexrow monster-hp">
<span class="name">{{localize
"LETHALFANTASY.Label.movement.sprint"
}}</span>
{{formField
systemFields.movement.fields.sprint
value=system.movement.sprint
disabled=isPlayMode
}}
</div>
</div>
</div> </div>
</fieldset> </fieldset>

View File

@@ -0,0 +1,44 @@
<div class="fvtt-lethal-fantasy progression-message {{#if success}}progression-success{{else}}progression-failure{{/if}}">
<div class="progression-header">
<div class="progression-icon">
{{#if success}}
<i class="fa-solid fa-check-circle"></i>
{{else}}
<i class="fa-solid fa-hourglass-half"></i>
{{/if}}
</div>
<div class="progression-info">
<div class="actor-name">{{actorName}}</div>
<div class="progression-subtitle">
{{#if success}}
{{localize "LETHALFANTASY.ProgressionMessage.canAct"}}
{{else}}
{{localize "LETHALFANTASY.ProgressionMessage.cannotAct"}}
{{/if}}
</div>
</div>
</div>
<div class="progression-details">
{{#if weaponName}}
<div class="progression-weapon">
<i class="fa-solid fa-sword"></i>
<span><strong>{{weaponName}}</strong></span>
</div>
{{/if}}
{{#if rollResult}}
<div class="progression-roll">
<span class="roll-label">{{localize "LETHALFANTASY.ProgressionMessage.diceResult"}}</span>
<span class="roll-value">{{rollResult}}</span>
</div>
{{/if}}
{{#if progressionCount}}
<div class="progression-count">
<i class="fa-solid fa-dice-d20"></i>
<span>{{localize "LETHALFANTASY.ProgressionMessage.progressionCount"}} <strong>{{progressionCount}}</strong></span>
</div>
{{/if}}
</div>
</div>

View File

@@ -36,9 +36,11 @@
<fieldSet> <fieldSet>
<legend>{{localize "LETHALFANTASY.Roll.visibility"}}</legend> <legend>{{localize "LETHALFANTASY.Roll.visibility"}}</legend>
<select name="visibility"> <span class="fieldset-centered">
{{selectOptions rollModes selected=visibility localize=true}} <select name="visibility">
</select> {{selectOptions rollModes selected=visibility localize=true}}
</select>
</span>
</fieldSet> </fieldSet>
</div> </div>

View File

@@ -1,111 +1,148 @@
<div class="lethalfantasy-roll-dialog"> <div class="lethalfantasy-roll-dialog">
<fieldSet class=""> <fieldSet class="">
<legend>{{localize (concat "LETHALFANTASY.Label." rollType)}} - {{actorName}}</legend> <legend>{{localize (concat "LETHALFANTASY.Label." rollType)}}
-
{{actorName}}</legend>
{{#if rollTarget.tokenId}} {{#if rollTarget.tokenId}}
<div class="dialog-save"> <div class="dialog-save">
<a class="goto-token-button" data-action="gotoToken" data-token-id="{{rollTarget.tokenId}}">{{localize <a
"LETHALFANTASY.Label.gotoToken"}} </a> class="goto-token-button"
</div> data-action="gotoToken"
data-token-id="{{rollTarget.tokenId}}"
>{{localize "LETHALFANTASY.Label.gotoToken"}} </a>
</div>
{{/if}} {{/if}}
{{#if (match rollType "attack")}} {{#if (match rollType "attack")}}
<div class="dialog-save">Attack roll ! - {{rollTarget.name}}</div> <div class="dialog-save">Attack roll ! - {{rollTarget.name}}</div>
{{/if}} {{/if}}
{{#if (match rollType "defense")}} {{#if (match rollType "defense")}}
<div class="dialog-save">Defense roll ! - {{rollTarget.name}}</div> <div class="dialog-save">Defense roll ! - {{rollTarget.name}}</div>
{{/if}} {{/if}}
{{#if hasModifier}} {{#if hasModifier}}
<div class="dialog-save">{{upperFirst rollName}} : {{baseFormula}} + {{baseValue}}</div> <div class="dialog-save">{{upperFirst rollName}}
:
{{baseFormula}}
+
{{baseValue}}</div>
{{else}} {{else}}
<div class="dialog-save">{{upperFirst rollName}} : {{baseFormula}}</div> <div class="dialog-save">{{upperFirst rollName}} : {{baseFormula}}</div>
{{/if}} {{/if}}
{{#if rollTarget.weapon}} {{#if rollTarget.weapon}}
<div class="dialog-save">{{localize "LETHALFANTASY.Label.baseModifier"}} : {{rollTarget.charModifier}}</div> <div class="dialog-save">{{localize "LETHALFANTASY.Label.baseModifier"}}
<div class="dialog-save">{{localize "LETHALFANTASY.Label.weapon"}} : {{rollTarget.weapon.name}}</div> :
<div class="dialog-save">{{localize "LETHALFANTASY.Label.skill"}} : {{rollTarget.name}}</div> {{rollTarget.charModifier}}</div>
<div class="dialog-save">{{localize "LETHALFANTASY.Label.skillBonus"}} : {{rollTarget.weaponSkillModifier}}</div> {{#if (match rollType "defense")}}
<div class="dialog-save">Armor defense modifier :
{{rollTarget.armorDefense}}</div>
{{/if}}
<div class="dialog-save">{{localize "LETHALFANTASY.Label.weapon"}}
:
{{rollTarget.weapon.name}}</div>
<div class="dialog-save">{{localize "LETHALFANTASY.Label.skill"}}
:
{{rollTarget.name}}</div>
<div class="dialog-save">{{localize "LETHALFANTASY.Label.skillBonus"}}
:
{{rollTarget.weaponSkillModifier}}</div>
{{/if}} {{/if}}
{{#if (match rollType "attack")}} {{#if (match rollType "attack")}}
<div class="dialog-save">Add Granted Attack Dice <div class="dialog-save">Add Granted Attack Dice
<input type="checkbox" data-action="selectGranted" name="granted"> <input type="checkbox" data-action="selectGranted" name="granted" />
</div> </div>
{{#if rollTarget.weapon}} {{#if rollTarget.weapon}}
{{#if (eq rollTarget.weapon.system.weaponType "melee")}} {{#if (eq rollTarget.weapon.system.weaponType "melee")}}{{else}}
{{else}} <div class="dialog-save">Point Blank Range Attack
<div class="dialog-save">Point Blank Range Attack <input
<input type="checkbox" data-action="selectPointBlank" name="pointBlankV"> type="checkbox"
</div> data-action="selectPointBlank"
<div class="dialog-save">Beyond Skill Range Attack name="pointBlankV"
<input type="checkbox" data-action="selectBeyondSkill" name="beyondSkillV"> />
</div> </div>
<div class="dialog-save">Let it Fly (Pure D20E) <div class="dialog-save">Beyond Skill Range Attack
<input type="checkbox" data-action="selectLetItFly" name="letItFlyV"> <input
</div> type="checkbox"
<div class="dialog-save">Aiming data-action="selectBeyondSkill"
<select name="attackerAim" data-tooltip-direction="UP"> name="beyondSkillV"
{{selectOptions attackerAimChoices selected=attackerAim}} />
</select> </div>
</div> <div class="dialog-save">Let it Fly (Pure D20E)
{{/if}} <input
{{/if}} type="checkbox"
data-action="selectLetItFly"
name="letItFlyV"
/>
</div>
<div class="dialog-save">Aiming
<select name="attackerAim" data-tooltip-direction="UP">
{{selectOptions attackerAimChoices selected=attackerAim}}
</select>
</div>
{{/if}}
{{/if}}
{{/if}} {{/if}}
{{#if (match rollType "defense")}} {{#if (match rollType "defense")}}
<div class="dialog-save">Add Granted Defense Dice <div class="dialog-save">Add Granted Defense Dice
<input type="checkbox" data-action="selectGranted" name="granted"> <input type="checkbox" data-action="selectGranted" name="granted" />
</div> </div>
{{/if}} {{/if}}
{{#if (match rollType "damage")}} {{#if (match rollType "damage")}}
<div class="dialog-save">Add Granted Damage Dice <div class="dialog-save">Add Granted Damage Dice
<input type="checkbox" data-action="selectGranted" name="granted"> <input type="checkbox" data-action="selectGranted" name="granted" />
</div> </div>
{{/if}} {{/if}}
{{#if rollTarget.staticModifier}} {{#if rollTarget.staticModifier}}
<div class="dialog-save">Static modifier : +{{rollTarget.staticModifier}}</div> <div class="dialog-save">Static modifier : +{{rollTarget.staticModifier}}</div>
{{/if}} {{/if}}
</fieldSet> </fieldSet>
{{#if hasFavor}} {{#if hasFavor}}
<fieldSet class="dialog-favor"> <fieldSet class="dialog-favor">
<legend>{{localize "LETHALFANTASY.Roll.favorDisfavor"}}</legend> <legend>{{localize "LETHALFANTASY.Roll.favorDisfavor"}}</legend>
<select name="favor" class="favor-choice" data-tooltip-direction="UP"> <select name="favor" class="favor-choice" data-tooltip-direction="UP">
{{selectOptions choiceFavor selected=favor}} {{selectOptions choiceFavor selected=favor}}
</select> </select>
</fieldSet> </fieldSet>
{{/if}} {{/if}}
{{#if hasModifier}} {{#if hasModifier}}
<fieldSet class="dialog-modifier"> <fieldSet class="dialog-modifier">
<legend>{{localize "LETHALFANTASY.Roll.modifierBonusMalus"}}</legend> <legend>{{localize "LETHALFANTASY.Roll.modifierBonusMalus"}}</legend>
<select name="modifier" data-tooltip-direction="UP"> <select name="modifier" data-tooltip-direction="UP">
{{selectOptions choiceModifier selected=modifier}} {{selectOptions choiceModifier selected=modifier}}
</select> </select>
{{#if (eq rollType "save")}} {{#if (eq rollType "save")}}
{{#if rollTarget.magicUser}} {{#if rollTarget.magicUser}}
<div> <div>
<span>Save against spell (+{{rollTarget.actorModifiers.saveModifier}}) ?</span> <span>Save against spell (+{{rollTarget.actorModifiers.saveModifier}})
<input type="checkbox" name="saveSpell" value="saveSpell"> ?</span>
</div> <input
{{/if}} type="checkbox"
{{/if}} name="saveSpellCheck"
</fieldSet> data-action="saveSpellCheck"
/>
</div>
{{/if}}
{{/if}}
</fieldSet>
{{/if}} {{/if}}
{{#if hasChangeDice}} {{#if hasChangeDice}}
<fieldSet class="dialog-modifier"> <fieldSet class="dialog-modifier">
<legend>{{localize "LETHALFANTASY.Roll.changeDice"}}</legend> <legend>{{localize "LETHALFANTASY.Roll.changeDice"}}</legend>
<select name="changeDice" data-tooltip-direction="UP"> <select name="changeDice" data-tooltip-direction="UP">
{{selectOptions choiceDice selected=changeDice}} {{selectOptions choiceDice selected=changeDice}}
</select> </select>
</fieldSet> </fieldSet>
{{/if}} {{/if}}
<fieldSet> <fieldSet>
@@ -115,5 +152,4 @@
</select> </select>
</fieldSet> </fieldSet>
</div> </div>

View File

@@ -1,6 +1,12 @@
<section> <section>
<div class="header"> <div class="header">
<img class="item-img" src="{{item.img}}" data-edit="img" data-action="editImage" data-tooltip="{{item.name}}" /> <img
class="item-img"
src="{{item.img}}"
data-edit="img"
data-action="editImage"
data-tooltip="{{item.name}}"
/>
{{formInput fields.name value=source.name}} {{formInput fields.name value=source.name}}
</div> </div>
@@ -8,23 +14,32 @@
<div class="align-top"> <div class="align-top">
{{formField systemFields.defense value=system.defense}} {{formField systemFields.defense value=system.defense}}
{{formField systemFields.damageReduction value=system.damageReduction}}
<div class="shift-right">&nbsp; <div class="shift-right">&nbsp;
</div> </div>
{{formField systemFields.hascover value=system.hascover}} {{formField systemFields.hascover value=system.hascover}}
{{#if system.hascover}} {{#if system.hascover}}
<label>Cover vs ranged attacks</label> <label>Cover vs ranged attacks</label>
<div class="shift-right"> <div class="shift-right">
<label>Standing </label> <label>Standing </label>
<div class="flexrow">{{formField systemFields.standing.fields.min value=system.standing.min}} &nbsp;...&nbsp;30 <div class="flexrow">{{formField
systemFields.standing.fields.min
value=system.standing.min
}}
&nbsp;...&nbsp;30
</div>
</div>
<div class="shift-right">
<label>Crouching</label>
<div class="flexrow">{{formField
systemFields.crouching.fields.min
value=system.crouching.min
}}
&nbsp;...&nbsp;30</div>
</div> </div>
</div>
<div class="shift-right">
<label>Crouching</label>
<div class="flexrow">{{formField systemFields.crouching.fields.min value=system.crouching.min}}
&nbsp;...&nbsp;30</div>
</div>
{{/if}} {{/if}}
</div> </div>
@@ -53,15 +68,14 @@
</div> </div>
</div> </div>
<fieldset> <fieldset>
<legend>{{localize "LETHALFANTASY.Label.description"}}</legend> <legend>{{localize "LETHALFANTASY.Label.description"}}</legend>
{{formInput {{formInput
systemFields.description systemFields.description
enriched=enrichedDescription enriched=enrichedDescription
value=system.description value=system.description
name="system.description" name="system.description"
toggled="false" toggled="false"
}} }}
</fieldset> </fieldset>
</section> </section>