Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| caef2c85d2 | |||
| e43487b727 | |||
| 709e69fac8 | |||
| e26db56585 | |||
| bb6a6248f2 | |||
| 7dc2492c96 | |||
| 2c25820152 | |||
| 6ad8226265 | |||
| 68a0d03740 | |||
| 8d9cc1045c | |||
| 4af277d8a2 | |||
| bc9f397755 |
@@ -0,0 +1 @@
|
|||||||
|
packs/** filter=lfs diff=lfs merge=lfs -text
|
||||||
@@ -59,5 +59,5 @@ jobs:
|
|||||||
version: ${{github.event.release.tag_name}}
|
version: ${{github.event.release.tag_name}}
|
||||||
manifest: 'https://www.uberwald.me/gitea/public/fvtt-cthulhu-eternal/releases/download/latest/system.json'
|
manifest: 'https://www.uberwald.me/gitea/public/fvtt-cthulhu-eternal/releases/download/latest/system.json'
|
||||||
notes: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-cthulhu-eternal.zip'
|
notes: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-cthulhu-eternal.zip'
|
||||||
compatibility-minimum: '13'
|
compatibility-minimum: '14'
|
||||||
compatibility-verified: '13'
|
compatibility-verified: '14'
|
||||||
@@ -5,4 +5,6 @@ styles/*.css
|
|||||||
|
|
||||||
# Node Modules
|
# Node Modules
|
||||||
node_modules/
|
node_modules/
|
||||||
|
.github/
|
||||||
|
.history/
|
||||||
|
|
||||||
|
|||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"CTHULHUETERNAL"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
Code license :
|
||||||
|
|
||||||
|
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
||||||
|
|
||||||
|
This license requires that reusers give credit to the creator. It allows reusers to distribute, remix, adapt, and build upon the material in any medium or format, for noncommercial purposes only. If others modify or adapt the material, they must license the modified material under identical terms.
|
||||||
|
|
||||||
|
|||||||
+265
-15
@@ -232,6 +232,32 @@ i.fvtt-cthulhu-eternal {
|
|||||||
font-family: var(--font-primary);
|
font-family: var(--font-primary);
|
||||||
font-size: calc(var(--font-size-standard) * 1.02);
|
font-size: calc(var(--font-size-standard) * 1.02);
|
||||||
}
|
}
|
||||||
|
.chat-san-request ul .combatants-grid,
|
||||||
|
.chat-lethal-damage ul .combatants-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
gap: 4px;
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
.chat-san-request ul .combatants-grid button.apply-wounds-btn,
|
||||||
|
.chat-lethal-damage ul .combatants-grid button.apply-wounds-btn {
|
||||||
|
font-family: var(--font-primary);
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.7);
|
||||||
|
border: 1px solid #4b4a44;
|
||||||
|
padding: 4px 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
background-color: #f0f0e0;
|
||||||
|
color: #1c1c1c;
|
||||||
|
}
|
||||||
|
.chat-san-request ul .combatants-grid button.apply-wounds-btn:hover,
|
||||||
|
.chat-lethal-damage ul .combatants-grid button.apply-wounds-btn:hover {
|
||||||
|
background-color: #d5d5c5;
|
||||||
|
border-color: #2d2d2a;
|
||||||
|
}
|
||||||
.fvtt-cthulhu-eternal .protagonist-sheet-common label {
|
.fvtt-cthulhu-eternal .protagonist-sheet-common label {
|
||||||
font-family: var(--font-secondary);
|
font-family: var(--font-secondary);
|
||||||
font-size: calc(var(--font-size-standard) * 1);
|
font-size: calc(var(--font-size-standard) * 1);
|
||||||
@@ -651,6 +677,12 @@ i.fvtt-cthulhu-eternal {
|
|||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
.fvtt-cthulhu-eternal .empty-state {
|
||||||
|
color: var(--color-text-light-6);
|
||||||
|
font-style: italic;
|
||||||
|
padding: 0.5rem 0.25rem;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
.fvtt-cthulhu-eternal .tab.protagonist-skills .main-div .skills .skill {
|
.fvtt-cthulhu-eternal .tab.protagonist-skills .main-div .skills .skill {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -892,6 +924,20 @@ i.fvtt-cthulhu-eternal {
|
|||||||
min-width: 1.8rem;
|
min-width: 1.8rem;
|
||||||
max-width: 1.8rem;
|
max-width: 1.8rem;
|
||||||
}
|
}
|
||||||
|
.fvtt-cthulhu-eternal .tab.protagonist-equipment .main-div .armors .armor .equipped-toggle {
|
||||||
|
font-size: 1rem;
|
||||||
|
min-width: 1.4rem;
|
||||||
|
max-width: 1.4rem;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #888;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.fvtt-cthulhu-eternal .tab.protagonist-equipment .main-div .armors .armor .equipped-toggle.active {
|
||||||
|
color: #4caf50;
|
||||||
|
}
|
||||||
|
.fvtt-cthulhu-eternal .tab.protagonist-equipment .main-div .armors .armor .equipped-toggle:hover {
|
||||||
|
color: #2196f3;
|
||||||
|
}
|
||||||
.fvtt-cthulhu-eternal .tab.protagonist-equipment .main-div .armors .armor .protection {
|
.fvtt-cthulhu-eternal .tab.protagonist-equipment .main-div .armors .armor .protection {
|
||||||
min-width: 5rem;
|
min-width: 5rem;
|
||||||
max-width: 5rem;
|
max-width: 5rem;
|
||||||
@@ -3041,6 +3087,9 @@ i.fvtt-cthulhu-eternal {
|
|||||||
font-size: calc(var(--font-size-standard) * 2);
|
font-size: calc(var(--font-size-standard) * 2);
|
||||||
color: var(--color-dark-1);
|
color: var(--color-dark-1);
|
||||||
}
|
}
|
||||||
|
.li-apply-wounds {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.dice-roll {
|
.dice-roll {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
@@ -3084,21 +3133,6 @@ i.fvtt-cthulhu-eternal {
|
|||||||
font-family: var(--font-primary);
|
font-family: var(--font-primary);
|
||||||
font-size: calc(var(--font-size-standard) * 1);
|
font-size: calc(var(--font-size-standard) * 1);
|
||||||
}
|
}
|
||||||
.dice-roll .intro-chat .intro-right ul .nudge-roll {
|
|
||||||
font-size: calc(var(--font-size-standard) * 1);
|
|
||||||
margin-left: 2rem;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.dice-roll .intro-chat .intro-right ul .healing-roll {
|
|
||||||
font-size: calc(var(--font-size-standard) * 1);
|
|
||||||
margin-left: 2rem;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.dice-roll .intro-chat .intro-right ul .roll-damage {
|
|
||||||
font-size: calc(var(--font-size-standard) * 1);
|
|
||||||
margin-left: 2rem;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.dice-roll .intro-chat .intro-right ul .result-success {
|
.dice-roll .intro-chat .intro-right ul .result-success {
|
||||||
color: var(--color-success);
|
color: var(--color-success);
|
||||||
font-family: var(--font-title);
|
font-family: var(--font-title);
|
||||||
@@ -3133,3 +3167,219 @@ i.fvtt-cthulhu-eternal {
|
|||||||
font-size: calc(var(--font-size-standard) * 1.2);
|
font-size: calc(var(--font-size-standard) * 1.2);
|
||||||
text-shadow: 0 0 10px var(--color-shadow-primary);
|
text-shadow: 0 0 10px var(--color-shadow-primary);
|
||||||
}
|
}
|
||||||
|
.dice-roll .skill-progress-notice {
|
||||||
|
margin: 0.3rem 0 0;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--color-level-warning);
|
||||||
|
border-left: 3px solid var(--color-level-warning);
|
||||||
|
background: rgba(var(--color-level-warning-rgb, 180, 130, 0), 0.08);
|
||||||
|
}
|
||||||
|
.dice-roll .chat-actions { display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.375rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
border-top: 1px solid var(--color-border-light-primary);
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 0 0 5px 5px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.dice-roll .chat-actions .chat-action-button {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
background: var(--color-dark-6);
|
||||||
|
color: var(--color-light-1);
|
||||||
|
border: 1px solid var(--color-border-light-primary);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.dice-roll .chat-actions .chat-action-button:hover {
|
||||||
|
background: var(--color-dark-5);
|
||||||
|
border-color: var(--color-border-dark);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.dice-roll .chat-actions .chat-action-button i {
|
||||||
|
font-size: 1rem !important;
|
||||||
|
line-height: 1;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.dice-roll .chat-actions .nudge-roll,
|
||||||
|
.dice-roll .chat-actions .damage-roll,
|
||||||
|
.dice-roll .chat-actions .healing-roll,
|
||||||
|
.dice-roll .chat-actions .opposed-roll,
|
||||||
|
.dice-roll .chat-actions .nudge-to-success {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.dice-roll .chat-actions .nudge-to-success {
|
||||||
|
width: auto;
|
||||||
|
padding: 0 0.5rem !important;
|
||||||
|
gap: 0.3rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: bold;
|
||||||
|
background: var(--color-level-success);
|
||||||
|
color: var(--color-light-1);
|
||||||
|
border-color: var(--color-level-success);
|
||||||
|
}
|
||||||
|
.dice-roll .chat-actions .nudge-to-success:hover {
|
||||||
|
filter: brightness(1.15);
|
||||||
|
}
|
||||||
|
.opposed-roll-result {
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 5px;
|
||||||
|
font-family: var(--font-primary);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .opposed-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
padding-bottom: 0.2rem;
|
||||||
|
border-bottom: 1px solid var(--color-border-light-primary);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .opposed-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--font-title);
|
||||||
|
font-size: calc(var(--font-size-standard) * 1);
|
||||||
|
color: var(--color-dark-1);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .opposed-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.15rem;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
.opposed-roll-result .opposed-winner,
|
||||||
|
.opposed-roll-result .opposed-loser {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.3rem 0.4rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.opposed-roll-result .opposed-winner {
|
||||||
|
background: rgba(34, 139, 34, 0.1);
|
||||||
|
border: 2px solid var(--color-success);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .opposed-loser {
|
||||||
|
background: rgba(220, 20, 60, 0.1);
|
||||||
|
border: 2px solid var(--color-failure);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .character-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.opposed-roll-result .character-info img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid var(--color-border-light-primary);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .character-info .character-name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.15rem;
|
||||||
|
}
|
||||||
|
.opposed-roll-result .character-info .character-name strong {
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.75);
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-dark-2);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .character-info .character-name .winner-name {
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.95);
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .character-info .character-name .loser-name {
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.95);
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-failure);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .roll-result {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.3rem;
|
||||||
|
}
|
||||||
|
.opposed-roll-result .roll-result .roll-value {
|
||||||
|
font-size: calc(var(--font-size-standard) * 1.2);
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: var(--font-title);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .roll-result .critical-badge {
|
||||||
|
padding: 0.15rem 0.3rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.7);
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background: var(--color-critical-success);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.opposed-roll-result .winner-result .roll-value {
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .loser-result .roll-value {
|
||||||
|
color: var(--color-failure);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .versus-separator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.3rem;
|
||||||
|
padding: 0;
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.85);
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-dark-2);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .versus-separator i {
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.9);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .chat-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.375rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
border-top: 1px solid var(--color-border-light-primary);
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 0 0 5px 5px;
|
||||||
|
}
|
||||||
|
.opposed-roll-result .chat-actions .chat-action-button {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
background: var(--color-dark-6);
|
||||||
|
color: var(--color-light-1);
|
||||||
|
border: 1px solid var(--color-border-light-primary);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.opposed-roll-result .chat-actions .chat-action-button:hover {
|
||||||
|
background: var(--color-dark-5);
|
||||||
|
border-color: var(--color-border-dark);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.opposed-roll-result .chat-actions .chat-action-button i {
|
||||||
|
font-size: 1rem !important;
|
||||||
|
line-height: 1;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|||||||
+119
-16
@@ -14,6 +14,9 @@ import * as applications from "./module/applications/_module.mjs"
|
|||||||
import { handleSocketEvent } from "./module/socket.mjs"
|
import { handleSocketEvent } from "./module/socket.mjs"
|
||||||
import CthulhuEternalUtils from "./module/utils.mjs"
|
import CthulhuEternalUtils from "./module/utils.mjs"
|
||||||
|
|
||||||
|
import { SystemManager } from './module/applications/hud/system-manager.js'
|
||||||
|
import { MODULE, REQUIRED_CORE_MODULE_VERSION } from './module/applications/hud/constants.js'
|
||||||
|
|
||||||
export class ClassCounter { static printHello() { console.log("Hello") } static sendJsonPostRequest(e, s) { const t = { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify(s) }; return fetch(e, t).then((e => { if (!e.ok) throw new Error("La requête a échoué avec le statut " + e.status); return e.json() })).catch((e => { throw console.error("Erreur envoi de la requête:", e), e })) } static registerUsageCount(e = game.system.id, s = {}) { if (game.user.isGM) { game.settings.register(e, "world-key", { name: "Unique world key", scope: "world", config: !1, default: "", type: String }); let t = game.settings.get(e, "world-key"); null != t && "" != t && "NONE" != t && "none" != t.toLowerCase() || (t = foundry.utils.randomID(32), game.settings.set(e, "world-key", t)); let a = { name: e, system: game.system.id, worldKey: t, version: game.system.version, language: game.settings.get("core", "language"), remoteAddr: game.data.addresses.remote, nbInstalledModules: game.modules.size, nbActiveModules: game.modules.filter((e => e.active)).length, nbPacks: game.world.packs.size, nbUsers: game.users.size, nbScenes: game.scenes.size, nbActors: game.actors.size, nbPlaylist: game.playlists.size, nbTables: game.tables.size, nbCards: game.cards.size, optionsData: s, foundryVersion: `${game.release.generation}.${game.release.build}` }; this.sendJsonPostRequest("https://www.uberwald.me/fvtt_appcount/count_post.php", a) } } }
|
export class ClassCounter { static printHello() { console.log("Hello") } static sendJsonPostRequest(e, s) { const t = { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify(s) }; return fetch(e, t).then((e => { if (!e.ok) throw new Error("La requête a échoué avec le statut " + e.status); return e.json() })).catch((e => { throw console.error("Erreur envoi de la requête:", e), e })) } static registerUsageCount(e = game.system.id, s = {}) { if (game.user.isGM) { game.settings.register(e, "world-key", { name: "Unique world key", scope: "world", config: !1, default: "", type: String }); let t = game.settings.get(e, "world-key"); null != t && "" != t && "NONE" != t && "none" != t.toLowerCase() || (t = foundry.utils.randomID(32), game.settings.set(e, "world-key", t)); let a = { name: e, system: game.system.id, worldKey: t, version: game.system.version, language: game.settings.get("core", "language"), remoteAddr: game.data.addresses.remote, nbInstalledModules: game.modules.size, nbActiveModules: game.modules.filter((e => e.active)).length, nbPacks: game.world.packs.size, nbUsers: game.users.size, nbScenes: game.scenes.size, nbActors: game.actors.size, nbPlaylist: game.playlists.size, nbTables: game.tables.size, nbCards: game.cards.size, optionsData: s, foundryVersion: `${game.release.generation}.${game.release.build}` }; this.sendJsonPostRequest("https://www.uberwald.me/fvtt_appcount/count_post.php", a) } } }
|
||||||
|
|
||||||
Hooks.once("init", function () {
|
Hooks.once("init", function () {
|
||||||
@@ -29,6 +32,12 @@ Hooks.once("init", function () {
|
|||||||
models,
|
models,
|
||||||
documents,
|
documents,
|
||||||
}
|
}
|
||||||
|
// Set an initiative formula for the system
|
||||||
|
CONFIG.Combat.initiative = {
|
||||||
|
formula: "@characteristics.dex.value",
|
||||||
|
decimals: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
CONFIG.Actor.documentClass = documents.CthulhuEternalActor
|
CONFIG.Actor.documentClass = documents.CthulhuEternalActor
|
||||||
CONFIG.Actor.dataModels = {
|
CONFIG.Actor.dataModels = {
|
||||||
@@ -93,7 +102,7 @@ Hooks.once("init", function () {
|
|||||||
CthulhuEternalUtils.registerSettings()
|
CthulhuEternalUtils.registerSettings()
|
||||||
CthulhuEternalUtils.registerHandlebarsHelpers()
|
CthulhuEternalUtils.registerHandlebarsHelpers()
|
||||||
CthulhuEternalUtils.setupCSSRootVariables()
|
CthulhuEternalUtils.setupCSSRootVariables()
|
||||||
|
CONFIG.debug.hooks = false;
|
||||||
|
|
||||||
console.info("CTHULHU ETERNAL | System Initialized")
|
console.info("CTHULHU ETERNAL | System Initialized")
|
||||||
})
|
})
|
||||||
@@ -136,35 +145,129 @@ Hooks.once("ready", function () {
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
Hooks.on('tokenActionHudCoreApiReady', async () => {
|
||||||
|
/**
|
||||||
|
* Return the SystemManager and requiredCoreModuleVersion to Token Action HUD Core
|
||||||
|
*/
|
||||||
|
let module = {} // game.modules.get(MODULE.ID)
|
||||||
|
module.api = {
|
||||||
|
requiredCoreModuleVersion: REQUIRED_CORE_MODULE_VERSION,
|
||||||
|
SystemManager
|
||||||
|
}
|
||||||
|
Hooks.call('tokenActionHudSystemReady', module)
|
||||||
|
})
|
||||||
|
|
||||||
Hooks.on("renderChatMessageHTML", (message, html, data) => {
|
Hooks.on("renderChatMessageHTML", (message, html, data) => {
|
||||||
|
const alreadyUsedLabel = game.i18n.localize("CTHULHUETERNAL.Label.alreadyUsed")
|
||||||
|
|
||||||
|
// Helper: disable all action buttons in the message
|
||||||
|
function disableChatActions(container) {
|
||||||
|
$(container).find(".chat-action-button").each((i, btn) => {
|
||||||
|
btn.setAttribute("disabled", "disabled")
|
||||||
|
btn.setAttribute("data-tooltip", alreadyUsedLabel)
|
||||||
|
btn.style.opacity = "0.5"
|
||||||
|
btn.style.cursor = "not-allowed"
|
||||||
|
btn.style.pointerEvents = "none"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an action was already used on this message, disable all buttons immediately
|
||||||
|
if (message.getFlag("fvtt-cthulhu-eternal", "actionUsed")) {
|
||||||
|
disableChatActions(html)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper: click any action button → disable all + persist flag
|
||||||
|
function withActionLock(handler) {
|
||||||
|
return async (event) => {
|
||||||
|
const container = $(event.currentTarget).closest(".chat-message, .message-content, div[class]")[0] || html
|
||||||
|
disableChatActions(container)
|
||||||
|
message.setFlag("fvtt-cthulhu-eternal", "actionUsed", true).catch(() => { })
|
||||||
|
await handler(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Affichage des boutons de jet de dés uniquement pour les joueurs
|
// Affichage des boutons de jet de dés uniquement pour les joueurs
|
||||||
if (message.author.id === game.user.id) {
|
if (message.author.id === game.user.id || game.user.isGM) {
|
||||||
$(html).find(".nudge-roll").each((i, btn) => {
|
$(html).find(".nudge-roll").each((i, btn) => {
|
||||||
btn.style.display = "inline"
|
btn.style.display = "inline"
|
||||||
})
|
})
|
||||||
|
$(html).find(".nudge-to-success").each((i, btn) => {
|
||||||
|
btn.style.display = "inline-flex"
|
||||||
|
})
|
||||||
$(html).find(".damage-roll").each((i, btn) => {
|
$(html).find(".damage-roll").each((i, btn) => {
|
||||||
btn.style.display = "inline"
|
btn.style.display = "inline"
|
||||||
})
|
})
|
||||||
$(html).find(".healing-roll").each((i, btn) => {
|
$(html).find(".healing-roll").each((i, btn) => {
|
||||||
btn.style.display = "inline"
|
btn.style.display = "inline"
|
||||||
})
|
})
|
||||||
$(html).find(".nudge-roll").click((event) => {
|
$(html).find(".worn-weapon-check").each((i, btn) => {
|
||||||
CthulhuEternalUtils.nudgeRoll(message)
|
btn.style.display = "inline"
|
||||||
})
|
})
|
||||||
$(html).find(".damage-roll").click((event) => {
|
if (game.user.isGM) {
|
||||||
let formula = $(event.currentTarget).data("roll-value")
|
$(html).find(".opposed-roll").each((i, btn) => {
|
||||||
CthulhuEternalUtils.damageRoll(message, formula)
|
btn.style.display = "inline"
|
||||||
})
|
|
||||||
$(html).find(".healing-roll").click((event) => {
|
|
||||||
CthulhuEternalUtils.healingRoll(message)
|
|
||||||
})
|
|
||||||
$(html).find(".san-loose").click((event) => {
|
|
||||||
CthulhuEternalUtils.applySANLoss(message, event)
|
|
||||||
})
|
|
||||||
$(html).find(".san-type").click((event) => {
|
|
||||||
CthulhuEternalUtils.applySANType(message, event)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
$(html).find(".nudge-roll").click(withActionLock(() => {
|
||||||
|
CthulhuEternalUtils.nudgeRoll(message)
|
||||||
|
}))
|
||||||
|
$(html).find(".nudge-to-success").click(withActionLock(() => {
|
||||||
|
CthulhuEternalUtils.nudgeToSuccess(message)
|
||||||
|
}))
|
||||||
|
$(html).find(".damage-roll").click(withActionLock((event) => {
|
||||||
|
let formula = $(event.currentTarget).data("roll-value")
|
||||||
|
CthulhuEternalUtils.damageRoll(message, formula)
|
||||||
|
}))
|
||||||
|
$(html).find(".healing-roll").click(withActionLock(() => {
|
||||||
|
CthulhuEternalUtils.healingRoll(message)
|
||||||
|
}))
|
||||||
|
$(html).find(".worn-weapon-check").click(withActionLock(() => {
|
||||||
|
CthulhuEternalUtils.wornWeaponCheck(message)
|
||||||
|
}))
|
||||||
|
$(html).find(".san-loose").click(withActionLock((event) => {
|
||||||
|
CthulhuEternalUtils.applySANLoss(message, event)
|
||||||
|
}))
|
||||||
|
$(html).find(".san-type").click(withActionLock((event) => {
|
||||||
|
CthulhuEternalUtils.applySANType(message, event)
|
||||||
|
}))
|
||||||
|
$(html).find(".opposed-roll").click(withActionLock((event) => {
|
||||||
|
CthulhuEternalUtils.opposedRollManagement(message, event)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
if (game.user.isGM) {
|
||||||
|
$(html).find(".li-apply-wounds").each((i, btn) => {
|
||||||
|
btn.style.display = "block"
|
||||||
|
})
|
||||||
|
$(html).find(".apply-wounds-btn").click(withActionLock((event) => {
|
||||||
|
CthulhuEternalUtils.applyWounds(message, event)
|
||||||
|
}))
|
||||||
|
$(html).find(".apply-wounds-btn").hover(
|
||||||
|
function (event) {
|
||||||
|
// Mouse enter - highlight the token on the canvas
|
||||||
|
const tokenId = $(this).data("token-id")
|
||||||
|
if (tokenId) {
|
||||||
|
const token = canvas.tokens.get(tokenId)
|
||||||
|
if (token) token.control({ releaseOthers: true })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Legacy: resolve via combatant
|
||||||
|
const combatantId = $(this).data("combatant-id")
|
||||||
|
if (combatantId && game.combat) {
|
||||||
|
const combatant = game.combat.combatants.get(combatantId)
|
||||||
|
if (combatant?.token) {
|
||||||
|
const token = canvas.tokens.get(combatant.token.id)
|
||||||
|
if (token) token.control({ releaseOthers: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (event) {
|
||||||
|
// Mouse leave - release selection
|
||||||
|
canvas.tokens.releaseAll()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+81
-5
@@ -24,6 +24,9 @@
|
|||||||
"Settings": {
|
"Settings": {
|
||||||
"era": "Select the era of your game",
|
"era": "Select the era of your game",
|
||||||
"eraHint": "Select the era of your game",
|
"eraHint": "Select the era of your game",
|
||||||
|
"nudge": "Allow roll modification (WP spend)",
|
||||||
|
"nudgeHint": "When enabled, players can spend Willpower Points to modify failed rolls. Disable to remove this optional rule.",
|
||||||
|
"nudgeDisabled": "Roll modification via Willpower Points is currently disabled.",
|
||||||
"Common": "Common",
|
"Common": "Common",
|
||||||
"Classical": "Classical",
|
"Classical": "Classical",
|
||||||
"Medieval": "Medieval",
|
"Medieval": "Medieval",
|
||||||
@@ -59,6 +62,12 @@
|
|||||||
},
|
},
|
||||||
"hp": {
|
"hp": {
|
||||||
"label": "HP",
|
"label": "HP",
|
||||||
|
"value": {
|
||||||
|
"label": "Current"
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"label": "Max"
|
||||||
|
},
|
||||||
"stunned": {
|
"stunned": {
|
||||||
"label": "Stun."
|
"label": "Stun."
|
||||||
}
|
}
|
||||||
@@ -126,6 +135,18 @@
|
|||||||
"damageBonus": {
|
"damageBonus": {
|
||||||
"label": "Dmg.Bonus"
|
"label": "Dmg.Bonus"
|
||||||
},
|
},
|
||||||
|
"hp": {
|
||||||
|
"label": "HP",
|
||||||
|
"value": {
|
||||||
|
"label": "Current"
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"label": "Max"
|
||||||
|
},
|
||||||
|
"stunned": {
|
||||||
|
"label": "Stun."
|
||||||
|
}
|
||||||
|
},
|
||||||
"resources": {
|
"resources": {
|
||||||
"permanentRating": {
|
"permanentRating": {
|
||||||
"label": "Permanent Rating"
|
"label": "Permanent Rating"
|
||||||
@@ -205,6 +226,7 @@
|
|||||||
"Struggle": "Struggle"
|
"Struggle": "Struggle"
|
||||||
},
|
},
|
||||||
"Skill": {
|
"Skill": {
|
||||||
|
"DodgeName": "Dodge",
|
||||||
"Unnatural": "Unnatural",
|
"Unnatural": "Unnatural",
|
||||||
"Melee": "Melee Weapons",
|
"Melee": "Melee Weapons",
|
||||||
"Firearms": "Firearms",
|
"Firearms": "Firearms",
|
||||||
@@ -552,10 +574,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Label": {
|
"Label": {
|
||||||
|
"JunkWeapon": "This weapon is in 'Junk' state, -20% applied. Critical failure on a roll of 96-100",
|
||||||
|
"WornWeapon": "This weapon is 'Worn', a Luck roll will be performed.",
|
||||||
|
"noTarget": "No target selected",
|
||||||
|
"noDefenseRoll": "No defense roll available",
|
||||||
|
"opposedRoll": "Opposed Roll",
|
||||||
|
"Target": "Target",
|
||||||
|
"feet": "feet",
|
||||||
|
"feets": "feet",
|
||||||
|
"yard": "yard",
|
||||||
|
"Yard": "Yard",
|
||||||
|
"yards": "yards",
|
||||||
|
"Feet": "Feet",
|
||||||
|
"Yards": "Yards",
|
||||||
"sanLoss5": "You suffered a SAN loss of 5+ ({value}) : you suffer a temporary insanity (Flee, Submit, Struggle or understanding the Unnatural).",
|
"sanLoss5": "You suffered a SAN loss of 5+ ({value}) : you suffer a temporary insanity (Flee, Submit, Struggle or understanding the Unnatural).",
|
||||||
"sanViolenceReset": "The violence SAN loss count has been reset.",
|
"sanViolenceReset": "The violence SAN loss count has been reset.",
|
||||||
"sanHelplessnessReset": "The helplessness SAN loss count has been reset.",
|
"sanHelplessnessReset": "The helplessness SAN loss count has been reset.",
|
||||||
"sanLoss": "You suffer a SAN loss",
|
"sanLoss": "You suffer a SAN loss",
|
||||||
|
"noSanLoss": "No SAN loss",
|
||||||
|
"sanLossInfo": "Select the SAN loss using the buttons below.",
|
||||||
"selectSANType": "Select the type of SAN loss with the buttons below.",
|
"selectSANType": "Select the type of SAN loss with the buttons below.",
|
||||||
"Violence" : "Violence",
|
"Violence" : "Violence",
|
||||||
"Helplessness": "Helplessness",
|
"Helplessness": "Helplessness",
|
||||||
@@ -574,6 +611,10 @@
|
|||||||
"rollNudge": "Roll Nudge",
|
"rollNudge": "Roll Nudge",
|
||||||
"rollDamage": "Roll Damage",
|
"rollDamage": "Roll Damage",
|
||||||
"rollHealing": "Roll Healing",
|
"rollHealing": "Roll Healing",
|
||||||
|
"wornWeaponCheck": "Check Weapon Condition",
|
||||||
|
"wornWeaponCheckTitle": "Worn Weapon Condition Check",
|
||||||
|
"wornWeaponCheckSuccess": "{weapon}: Luck roll succeeded ({roll}/50) - Weapon remains worn.",
|
||||||
|
"wornWeaponCheckFailure": "{weapon}: Luck roll failed ({roll}/50) - Weapon became junk!",
|
||||||
"result": "Result",
|
"result": "Result",
|
||||||
"damageMessage": "Damage to apply",
|
"damageMessage": "Damage to apply",
|
||||||
"lethalityRoll": "Lethality Roll",
|
"lethalityRoll": "Lethality Roll",
|
||||||
@@ -584,7 +625,9 @@
|
|||||||
"Weapon": "Weapon",
|
"Weapon": "Weapon",
|
||||||
"ZeroWP": "Zero WP : Automatic failure (ie 0%)",
|
"ZeroWP": "Zero WP : Automatic failure (ie 0%)",
|
||||||
"LowWP": "Low WP",
|
"LowWP": "Low WP",
|
||||||
|
"notEnoughWP": "Not enough WP to perform this action",
|
||||||
"Exhausted": "Exhausted",
|
"Exhausted": "Exhausted",
|
||||||
|
"wornWeaponWarning": "Worn weapon: Don't forget to make the luck roll after the attack!",
|
||||||
"creature": "Creature",
|
"creature": "Creature",
|
||||||
"Rituals": "Rituals",
|
"Rituals": "Rituals",
|
||||||
"Tomes": "Tomes",
|
"Tomes": "Tomes",
|
||||||
@@ -695,6 +738,8 @@
|
|||||||
"newMentalDisorder": "New Mental Disorder",
|
"newMentalDisorder": "New Mental Disorder",
|
||||||
"newWeapon": "New Weapon",
|
"newWeapon": "New Weapon",
|
||||||
"newArmor": "New Armor",
|
"newArmor": "New Armor",
|
||||||
|
"equipped": "Equipped (click to unequip)",
|
||||||
|
"unequipped": "Not equipped (click to equip)",
|
||||||
"newInjury": "New Injury",
|
"newInjury": "New Injury",
|
||||||
"newGear": "New Gear",
|
"newGear": "New Gear",
|
||||||
"newArcane": "New Arcane",
|
"newArcane": "New Arcane",
|
||||||
@@ -711,9 +756,17 @@
|
|||||||
"skillProgress": "Skill Progress",
|
"skillProgress": "Skill Progress",
|
||||||
"unconscious": "Unconscious",
|
"unconscious": "Unconscious",
|
||||||
"dying": "Dying",
|
"dying": "Dying",
|
||||||
"stunnedWarning": "The Protagonist is stunned. He cannot act until he recovers by successfully rolling a CONx5 check.",
|
|
||||||
"deadWarning": "The Protagonist is dead. He cannot act until he is revived by a successful First Aid roll.",
|
|
||||||
"unconsciousWarning": "The Protagonist is unconscious. He cannot act until he has at least 3 HP.",
|
"unconsciousWarning": "The Protagonist is unconscious. He cannot act until he has at least 3 HP.",
|
||||||
|
"stunnedWarning": "The Protagonist is stunned. He cannot act until he succeeds at a CON × 5 test.",
|
||||||
|
"deadWarning": "The Protagonist is dying. He will die if not treated within {con} minutes.",
|
||||||
|
"noSkills": "No skills. Add skills to this character.",
|
||||||
|
"noWeapons": "No weapons. Add weapons in edit mode.",
|
||||||
|
"noArmors": "No armor. Add armor in edit mode.",
|
||||||
|
"noGears": "No gear. Add gear in edit mode.",
|
||||||
|
"luck": "Luck",
|
||||||
|
"Other": "Other",
|
||||||
|
"Skills": "Skills",
|
||||||
|
"WP": "WP",
|
||||||
"Luck": "Luck",
|
"Luck": "Luck",
|
||||||
"titleLuck": "Luck Roll",
|
"titleLuck": "Luck Roll",
|
||||||
"healingRoll": "Healing Roll succes, the target gains",
|
"healingRoll": "Healing Roll succes, the target gains",
|
||||||
@@ -724,13 +777,27 @@
|
|||||||
"lethalityLethal": "Lethal !!",
|
"lethalityLethal": "Lethal !!",
|
||||||
"lethalityNotLethal": "Non-Lethal",
|
"lethalityNotLethal": "Non-Lethal",
|
||||||
"WPSpent": "WP Spent",
|
"WPSpent": "WP Spent",
|
||||||
|
"alreadyUsed": "Already used",
|
||||||
"targetMove": "Target Move",
|
"targetMove": "Target Move",
|
||||||
"attackerState": "Attacker State",
|
"attackerState": "Attacker State",
|
||||||
"targetSize": "Target Size",
|
"targetSize": "Target Size",
|
||||||
"visibility": "Visibility",
|
"visibility": "Visibility",
|
||||||
"rangedRange": "Range",
|
"rangedRange": "Range",
|
||||||
"aimingLastRound": "Aiming Last Round (+20)",
|
"aimingLastRound": "Aiming Last Round (+20)",
|
||||||
"aimingWithSight": "Aiming with Sight (+20)"
|
"aimingWithSight": "Aiming with Sight (+20)",
|
||||||
|
"applyWounds": "Apply To",
|
||||||
|
"opposedRollWinner": "Opposed Roll Winner",
|
||||||
|
"opposedRollResult": "Opposed Roll Result",
|
||||||
|
"defeats": "defeats",
|
||||||
|
"critical": "Critical",
|
||||||
|
"other": "Other",
|
||||||
|
"str": "STR",
|
||||||
|
"dex": "DEX",
|
||||||
|
"int": "INT",
|
||||||
|
"pow": "POW",
|
||||||
|
"con": "CON",
|
||||||
|
"cha": "CHA",
|
||||||
|
"Damage": "Damage"
|
||||||
},
|
},
|
||||||
"ChatMessage": {
|
"ChatMessage": {
|
||||||
"exhausted": "Your protagonist is exhausted. He loses [[/r 1d6]] Willpower Points."
|
"exhausted": "Your protagonist is exhausted. He loses [[/r 1d6]] Willpower Points."
|
||||||
@@ -746,7 +813,8 @@
|
|||||||
"roll": "Roll",
|
"roll": "Roll",
|
||||||
"applyNudge": "Apply",
|
"applyNudge": "Apply",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"nudgeRoll": "Nudge Roll"
|
"nudgeRoll": "Nudge Roll",
|
||||||
|
"nudgeToSuccess": "Succeed at {score}% for {wp} WP"
|
||||||
},
|
},
|
||||||
"Tooltip": {
|
"Tooltip": {
|
||||||
"sanBP": ">5 SAN lost in one roll, temporary insanity. If SAN less reaches BP = a Disorder unconscious Breaking and AND reset BP.",
|
"sanBP": ">5 SAN lost in one roll, temporary insanity. If SAN less reaches BP = a Disorder unconscious Breaking and AND reset BP.",
|
||||||
@@ -755,17 +823,25 @@
|
|||||||
"rollDamage": "Roll Damage"
|
"rollDamage": "Roll Damage"
|
||||||
},
|
},
|
||||||
"Chat": {
|
"Chat": {
|
||||||
|
"noArmor": "No armor absorbed damage.",
|
||||||
|
"armorAbsorbed": "Armor absorbed {armor} damage.",
|
||||||
|
"woundsApplied": "Wounds applied to {name}: {effectiveWounds} (armor absorbed : {armorText})"
|
||||||
},
|
},
|
||||||
"Notifications": {
|
"Notifications": {
|
||||||
|
"AttackNoTarget": "No target selected for the attack, please select one target to get automations.",
|
||||||
"NoWeaponSkill": "No weapon skill found for this weapon. Check Weapon definition or available skills/era",
|
"NoWeaponSkill": "No weapon skill found for this weapon. Check Weapon definition or available skills/era",
|
||||||
"NoWeaponType": "No weapon type found for this weapon subtype. Check Weapon definition or available skills/era",
|
"NoWeaponType": "No weapon type found for this weapon subtype. Check Weapon definition or available skills/era",
|
||||||
"skillAlreadyExists": "Skill already exists",
|
"skillAlreadyExists": "Skill already exists",
|
||||||
"WrongEra": "The era of the item does not match the ear of the system",
|
"WrongEra": "The era of the item does not match the ear of the system",
|
||||||
"NoSelectiveFireChoices": "Not enough ammo fo Selective Fire choices for this weapon.",
|
"NoSelectiveFireChoices": "Not enough ammo fo Selective Fire choices for this weapon.",
|
||||||
"NoAmmo": "No more ammo for this weapon. ",
|
"NoAmmo": "No more ammo for this weapon. ",
|
||||||
|
"WeaponBecameJunk": "Weapon {weapon} became junk!",
|
||||||
|
"noWeaponFound": "No weapon found.",
|
||||||
"noRollDataFound": "No roll data found",
|
"noRollDataFound": "No roll data found",
|
||||||
"noActorFound": "No actor found for this item.",
|
"noActorFound": "No actor found for this item.",
|
||||||
"noSanLossFound": "No SAN loss value found."
|
"noSanLossFound": "No SAN loss value found.",
|
||||||
|
"opposedRollFirstStored": "First opposed roll stored. Perform and store the second roll to resolve the opposed roll.",
|
||||||
|
"opposedRollSecondStored": "Second opposed roll stored. The opposed roll is now being resolved."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-7
@@ -24,6 +24,9 @@
|
|||||||
"Settings": {
|
"Settings": {
|
||||||
"era": "Sélectionnez l'époque de votre jeu",
|
"era": "Sélectionnez l'époque de votre jeu",
|
||||||
"eraHint": "L'époque détermine les compétences, les armes et les équipements disponibles pour votre protagoniste.",
|
"eraHint": "L'époque détermine les compétences, les armes et les équipements disponibles pour votre protagoniste.",
|
||||||
|
"nudge": "Autoriser la modification des jets (dépense de PVO)",
|
||||||
|
"nudgeHint": "Si activé, les joueurs peuvent dépenser des Points de Volonté pour modifier leurs jets ratés. Désactivez pour supprimer cette règle optionnelle.",
|
||||||
|
"nudgeDisabled": "La modification des jets via les Points de Volonté est actuellement désactivée.",
|
||||||
"Common": "Commun",
|
"Common": "Commun",
|
||||||
"Classical": "Classique",
|
"Classical": "Classique",
|
||||||
"Medieval": "Médiéval",
|
"Medieval": "Médiéval",
|
||||||
@@ -217,6 +220,7 @@
|
|||||||
"Struggle": "Lutter"
|
"Struggle": "Lutter"
|
||||||
},
|
},
|
||||||
"Skill": {
|
"Skill": {
|
||||||
|
"DodgeName": "Esquive",
|
||||||
"Unnatural": "Inconcevable",
|
"Unnatural": "Inconcevable",
|
||||||
"Melee": "Armes de mêlée",
|
"Melee": "Armes de mêlée",
|
||||||
"Firearms": "Armes à feu",
|
"Firearms": "Armes à feu",
|
||||||
@@ -564,6 +568,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Label": {
|
"Label": {
|
||||||
|
"JunkWeapon": "Cette arme est 'Défectueuse'', -20% appliqué. Échec critique sur un jet de 96-100",
|
||||||
|
"WornWeapon": "Cette arme est 'Usée', un jet de Chance sera effectué.",
|
||||||
|
"noTarget": "Aucune cible sélectionnée",
|
||||||
|
"noDefenseRoll": "Aucun jet de défense existant",
|
||||||
|
"opposedRoll": "Jet opposé",
|
||||||
|
"Target": "Cible",
|
||||||
"feet": "pieds (ie 30cm)",
|
"feet": "pieds (ie 30cm)",
|
||||||
"feets": "pieds (ie 30cm)",
|
"feets": "pieds (ie 30cm)",
|
||||||
"yard": "mètres",
|
"yard": "mètres",
|
||||||
@@ -595,6 +605,10 @@
|
|||||||
"rollNudge": "Modifier le jet",
|
"rollNudge": "Modifier le jet",
|
||||||
"rollDamage": "Jet de dégâts",
|
"rollDamage": "Jet de dégâts",
|
||||||
"rollHealing": "Jet de soin",
|
"rollHealing": "Jet de soin",
|
||||||
|
"wornWeaponCheck": "Vérifier l'état de l'arme",
|
||||||
|
"wornWeaponCheckTitle": "Vérification d'état d'arme usée",
|
||||||
|
"wornWeaponCheckSuccess": "{weapon} : Jet de chance réussi ({roll}/50) - L'arme reste usée.",
|
||||||
|
"wornWeaponCheckFailure": "{weapon} : Jet de chance échoué ({roll}/50) - L'arme devient défectueuse !",
|
||||||
"result": "Resultat",
|
"result": "Resultat",
|
||||||
"damageMessage": "Dégâts à appliquer",
|
"damageMessage": "Dégâts à appliquer",
|
||||||
"lethalityRoll": "Jet de Létalité",
|
"lethalityRoll": "Jet de Létalité",
|
||||||
@@ -605,15 +619,15 @@
|
|||||||
"Weapon": "Arme",
|
"Weapon": "Arme",
|
||||||
"ZeroWP": "PVO à 0 : Echec automatique (ie 0%)",
|
"ZeroWP": "PVO à 0 : Echec automatique (ie 0%)",
|
||||||
"LowWP": "PVO faibles",
|
"LowWP": "PVO faibles",
|
||||||
"Exhausted": "Epuisé",
|
"notEnoughWP": "PVO insuffisants pour effectuer cette action",
|
||||||
"creature": "Créature",
|
"Exhausted": "Epuisé", "wornWeaponWarning": "Arme usée : N'oubliez pas de faire le jet de chance après l'attaque !", "creature": "Créature",
|
||||||
"Rituals": "Rituels",
|
"Rituals": "Rituels",
|
||||||
"Tomes": "Ouvrages",
|
"Tomes": "Ouvrages",
|
||||||
"otherBenefits": "Autres avantages",
|
"otherBenefits": "Autres avantages",
|
||||||
"Unarmed": "Désarmé",
|
"Unarmed": "Désarmé",
|
||||||
"Cured": "Soigné",
|
"Cured": "Soigné",
|
||||||
"Uncured": "Non soigné",
|
"Uncured": "Non soigné",
|
||||||
"nudgedRoll": "Modifier le jeu",
|
"nudgedRoll": "Jet modifié : ",
|
||||||
"selectNewValue": "Sélectionner une nouvelle valeur",
|
"selectNewValue": "Sélectionner une nouvelle valeur",
|
||||||
"wpCost": "Cout en PVO",
|
"wpCost": "Cout en PVO",
|
||||||
"Hand": "A portée de main",
|
"Hand": "A portée de main",
|
||||||
@@ -716,6 +730,8 @@
|
|||||||
"newMentalDisorder": "Nouveau Trouble mental",
|
"newMentalDisorder": "Nouveau Trouble mental",
|
||||||
"newWeapon": "Nouvelle Arme",
|
"newWeapon": "Nouvelle Arme",
|
||||||
"newArmor": "Nouvelle Armure",
|
"newArmor": "Nouvelle Armure",
|
||||||
|
"equipped": "Équipée (cliquer pour déséquiper)",
|
||||||
|
"unequipped": "Non équipée (cliquer pour équiper)",
|
||||||
"newInjury": "Nouvelle Blessure",
|
"newInjury": "Nouvelle Blessure",
|
||||||
"newGear": "Nouvel Equipement",
|
"newGear": "Nouvel Equipement",
|
||||||
"newArcane": "Nouvel Arcane",
|
"newArcane": "Nouvel Arcane",
|
||||||
@@ -735,7 +751,14 @@
|
|||||||
"stunnedWarning": "Votre protagoniste est étourdi. Il ne peut pas agir tant qu'il n'a pas réussi un test de CON x 5.",
|
"stunnedWarning": "Votre protagoniste est étourdi. Il ne peut pas agir tant qu'il n'a pas réussi un test de CON x 5.",
|
||||||
"deadWarning": "Votre protagoniste est mourrant. Il mourra s'il n'est pas soigné dans les {con} minutes",
|
"deadWarning": "Votre protagoniste est mourrant. Il mourra s'il n'est pas soigné dans les {con} minutes",
|
||||||
"unconsciousWarning": "Votre protagoniste est inconscient. Il ne peut pas agir tant qu'il n'a pas atteint 3 PV.",
|
"unconsciousWarning": "Votre protagoniste est inconscient. Il ne peut pas agir tant qu'il n'a pas atteint 3 PV.",
|
||||||
"Luck": "Chance",
|
"noSkills": "Aucune compétence. Ajoutez des compétences à ce personnage.",
|
||||||
|
"noWeapons": "Aucune arme. Ajoutez des armes en mode édition.",
|
||||||
|
"noArmors": "Aucune armure. Ajoutez une armure en mode édition.",
|
||||||
|
"noGears": "Aucun équipement. Ajoutez de l'équipement en mode édition.",
|
||||||
|
"luck": "Chance",
|
||||||
|
"Other": "Autre",
|
||||||
|
"Skills": "Compétences",
|
||||||
|
"WP": "PVO",
|
||||||
"titleLuck": "Jet de Chance",
|
"titleLuck": "Jet de Chance",
|
||||||
"healingRoll": "Jet de soin, PV soignés",
|
"healingRoll": "Jet de soin, PV soignés",
|
||||||
"healingRollFailure": "Jet de soin échoué critique, PV perdus",
|
"healingRollFailure": "Jet de soin échoué critique, PV perdus",
|
||||||
@@ -745,13 +768,28 @@
|
|||||||
"lethalityLethal": "Létal !!",
|
"lethalityLethal": "Létal !!",
|
||||||
"lethalityNotLethal": "Non létal",
|
"lethalityNotLethal": "Non létal",
|
||||||
"WPSpent": "PVO dépensés",
|
"WPSpent": "PVO dépensés",
|
||||||
|
"alreadyUsed": "Déjà utilisé",
|
||||||
"targetMove": "Mouvement de la cible",
|
"targetMove": "Mouvement de la cible",
|
||||||
"attackerState": "Etat de l'attaquant",
|
"attackerState": "Etat de l'attaquant",
|
||||||
"targetSize": "Taille de la cible",
|
"targetSize": "Taille de la cible",
|
||||||
"visibility": "Visibilité",
|
"visibility": "Visibilité",
|
||||||
"rangedRange": "Portée",
|
"rangedRange": "Portée",
|
||||||
"aimingLastRound": "Visée lors du dernier round (+20)",
|
"aimingLastRound": "Visée lors du dernier round (+20)",
|
||||||
"aimingWithSight": "Visée avec lunette (+20)"
|
"aimingWithSight": "Visée avec lunette (+20)",
|
||||||
|
"applyWounds": "Appliquer à",
|
||||||
|
"opposedRollWinner": "Gagnant du jet opposé",
|
||||||
|
"opposedRollResult": "Résultat du jet opposé",
|
||||||
|
"defeats": "Défait",
|
||||||
|
"critical": "Critique",
|
||||||
|
"other": "Autre",
|
||||||
|
"str": "FOR",
|
||||||
|
"dex": "DEX",
|
||||||
|
"int": "INT",
|
||||||
|
"pow": "POU",
|
||||||
|
"con": "CON",
|
||||||
|
"cha": "CHA",
|
||||||
|
"Luck": "Chance",
|
||||||
|
"Damage": "Dégâts"
|
||||||
},
|
},
|
||||||
"ChatMessage": {
|
"ChatMessage": {
|
||||||
"exhausted": "Votre protagoniste est épuisé. Il perd [[/r 1d6]] Points de Volonté."
|
"exhausted": "Votre protagoniste est épuisé. Il perd [[/r 1d6]] Points de Volonté."
|
||||||
@@ -767,7 +805,8 @@
|
|||||||
"roll": "Jet",
|
"roll": "Jet",
|
||||||
"applyNudge": "Lancer",
|
"applyNudge": "Lancer",
|
||||||
"cancel": "Annuler",
|
"cancel": "Annuler",
|
||||||
"nudgeRoll": "Modifier le jet"
|
"nudgeRoll": "Modifier le jet",
|
||||||
|
"nudgeToSuccess": "Réussir à {score}% pour {wp} PVO"
|
||||||
},
|
},
|
||||||
"Tooltip": {
|
"Tooltip": {
|
||||||
"sanBP": "Perte de 5+ SAN en 1 jet : folie temporaire. SI la SAN atteint le PR : trouble mental, perte de conscience et reset du PR.",
|
"sanBP": "Perte de 5+ SAN en 1 jet : folie temporaire. SI la SAN atteint le PR : trouble mental, perte de conscience et reset du PR.",
|
||||||
@@ -776,17 +815,25 @@
|
|||||||
"rollDamage": "Lancer les dégâts"
|
"rollDamage": "Lancer les dégâts"
|
||||||
},
|
},
|
||||||
"Chat": {
|
"Chat": {
|
||||||
|
"armorAbsorbed": "L'armure a absorbé {armor} points de dégâts.",
|
||||||
|
"noArmor": "Pas d'armure.",
|
||||||
|
"woundsApplied": "Blessures appliquées à {name} : {effectiveWounds} (armure absorbée : {armorText})"
|
||||||
},
|
},
|
||||||
"Notifications": {
|
"Notifications": {
|
||||||
|
"AttackNoTarget": "Aucune cible n'a été sélectionnée pour cette attaque, veuillez sélectionner une cible pour bénéficier des automatisations.",
|
||||||
"NoWeaponSkill": "Aucune compétence associée n'a été trouvé pour cette arme. Vérifier la définition de l'arme ainsi que l'époque configurée.",
|
"NoWeaponSkill": "Aucune compétence associée n'a été trouvé pour cette arme. Vérifier la définition de l'arme ainsi que l'époque configurée.",
|
||||||
"NoWeaponType": "Aucun type d'arme trouvé pour ce sous-type. Vérifier la définition de l'arme ainsi que l'époque configurée.",
|
"NoWeaponType": "Aucun type d'arme trouvé pour ce sous-type. Vérifier la définition de l'arme ainsi que l'époque configurée.",
|
||||||
"skillAlreadyExists": "La compétence existe déja",
|
"skillAlreadyExists": "La compétence existe déja",
|
||||||
"WrongEra": "L'époque de l'item ne correspond pas à celle du jeu en cours.",
|
"WrongEra": "L'époque de l'item ne correspond pas à celle du jeu en cours.",
|
||||||
"NoSelectiveFireChoices": "Aucune option de tir sélectif n'est disponible pour cette arme : pas assez de munitions.",
|
"NoSelectiveFireChoices": "Aucune option de tir sélectif n'est disponible pour cette arme : pas assez de munitions.",
|
||||||
"NoAmmo": "Aucune munition disponible pour cette arme.",
|
"NoAmmo": "Aucune munition disponible pour cette arme.",
|
||||||
|
"WeaponBecameJunk": "L'arme {weapon} est devenue défectueuse !",
|
||||||
|
"noWeaponFound": "Aucune arme trouvée.",
|
||||||
"noRollDataFound": "Aucune donnée de jet trouvée.",
|
"noRollDataFound": "Aucune donnée de jet trouvée.",
|
||||||
"noActorFound": "Aucun protagoniste trouvé.",
|
"noActorFound": "Aucun protagoniste trouvé.",
|
||||||
"noSanLossFound": "Aucune valeur de perte de SAN trouvée."
|
"noSanLossFound": "Aucune valeur de perte de SAN trouvée.",
|
||||||
|
"opposedRollFirstStored": "Premier jet opposé enregistré. Effectuez et enregistrez le deuxième jet pour résoudre le jet opposé.",
|
||||||
|
"opposedRollSecondStored": "Deuxième jet opposé enregistré. Le jet opposé est maintenant en cours de résolution."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// System Module Imports
|
// System Module Imports
|
||||||
import { Utils } from './utils.js'
|
import CthulhuEternalUtils from '../../utils.mjs'
|
||||||
import { SYSTEM } from "../../config/system.mjs"
|
|
||||||
export let ActionHandler = null
|
export let ActionHandler = null
|
||||||
|
|
||||||
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
||||||
@@ -15,8 +15,7 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
* @param {array} groupIds
|
* @param {array} groupIds
|
||||||
*/
|
*/
|
||||||
async buildSystemActions(groupIds) {
|
async buildSystemActions(groupIds) {
|
||||||
// Set actor and token variables
|
// this.actor and this.actors are provided by the base class (TAH Core v2+)
|
||||||
this.actors = (!this.actor) ? this._getActors() : [this.actor]
|
|
||||||
this.actorType = this.actor?.type
|
this.actorType = this.actor?.type
|
||||||
|
|
||||||
// Set items variable
|
// Set items variable
|
||||||
@@ -26,9 +25,11 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
this.items = items
|
this.items = items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.actor) {
|
||||||
if (this.actorType !== 'vehicle') {
|
if (this.actorType !== 'vehicle') {
|
||||||
this.#buildCharacterActions()
|
this.#buildCharacterActions()
|
||||||
} else if (!this.actor) {
|
}
|
||||||
|
} else {
|
||||||
this.#buildMultipleTokenActions()
|
this.#buildMultipleTokenActions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,7 +39,7 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
#buildCharacterActions() {
|
#buildCharacterActions() {
|
||||||
this.buildAttributes()
|
this.buildCharacteristics()
|
||||||
this.buildOther()
|
this.buildOther()
|
||||||
this.buildLuck()
|
this.buildLuck()
|
||||||
this.buildSkills()
|
this.buildSkills()
|
||||||
@@ -49,17 +50,17 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
return game.settings.get('token-action-hud-core', 'tooltips') === 'none'
|
return game.settings.get('token-action-hud-core', 'tooltips') === 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildAttributes() {
|
async buildCharacteristics() {
|
||||||
const actions = []
|
const actions = []
|
||||||
for (const key in this.actor.system.characteristics) {
|
for (const key in this.actor.system.characteristics) {
|
||||||
const encodedValue = [coreModule.api.Utils.i18n('attributes'), key].join(this.delimiter)
|
const encodedValue = ['characteristics', key].join(this.delimiter)
|
||||||
const tooltip = {
|
const tooltip = {
|
||||||
content: String(this.actor.system.characteristics[key].value * 5),
|
content: String(this.actor.system.characteristics[key].value * 5),
|
||||||
class: 'tah-system-tooltip',
|
class: 'tah-system-tooltip',
|
||||||
direction: 'LEFT'
|
direction: 'LEFT'
|
||||||
}
|
}
|
||||||
actions.push({
|
actions.push({
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.' + key),
|
name: coreModule.api.Utils.i18n(`CTHULHUETERNAL.Label.${key}`),
|
||||||
id: key,
|
id: key,
|
||||||
info1: this.#showValue() ? { text: tooltip.content } : null,
|
info1: this.#showValue() ? { text: tooltip.content } : null,
|
||||||
tooltip,
|
tooltip,
|
||||||
@@ -67,7 +68,7 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
await this.addActions(actions, {
|
await this.addActions(actions, {
|
||||||
id: 'attributes',
|
id: 'characteristics',
|
||||||
type: 'system'
|
type: 'system'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -80,79 +81,83 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
direction: 'LEFT'
|
direction: 'LEFT'
|
||||||
}
|
}
|
||||||
actions.push({
|
actions.push({
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.Luck'),
|
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.luck'),
|
||||||
id: 'luck',
|
id: 'luck',
|
||||||
info1: this.#showValue() ? { text: '50' } : null,
|
info1: this.#showValue() ? { text: '50' } : null,
|
||||||
tooltip,
|
tooltip,
|
||||||
encodedValue: ['attributes', 'luck'].join(this.delimiter)
|
encodedValue: ['characteristics', 'luck'].join(this.delimiter)
|
||||||
})
|
})
|
||||||
await this.addActions(actions, { id: 'luck', type: 'system' })
|
await this.addActions(actions, { id: 'luck', type: 'system' })
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildOther() {
|
async buildOther() {
|
||||||
if (typeof this.actor.system.sanity.value !== 'undefined') {
|
if (typeof this.actor.system?.san?.value !== 'undefined') {
|
||||||
const actions = []
|
const actions = []
|
||||||
const groupData = {
|
const groupData = {
|
||||||
id: 'other_sanity',
|
id: 'other_san',
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.SAN'),
|
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.SAN'),
|
||||||
type: 'system'
|
type: 'system'
|
||||||
}
|
}
|
||||||
this.addGroup(groupData, { id: 'other', type: 'system' }, true)
|
this.addGroup(groupData, { id: 'other', type: 'system' }, true)
|
||||||
const tooltip = {
|
const tooltip = {
|
||||||
content: String(this.actor.system.san.value + '/' + this.actor.system.san.max),
|
content: `${this.actor.system.san.value}/${this.actor.system.san.max}`,
|
||||||
class: 'tah-system-tooltip',
|
class: 'tah-system-tooltip',
|
||||||
direction: 'LEFT'
|
direction: 'LEFT'
|
||||||
}
|
}
|
||||||
actions.push({
|
actions.push({
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.SAN'),
|
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.SAN'),
|
||||||
id: 'sanity',
|
id: 'san',
|
||||||
info1: this.#showValue() ? { text: tooltip.content } : null,
|
info1: this.#showValue() ? { text: tooltip.content } : null,
|
||||||
tooltip,
|
tooltip,
|
||||||
encodedValue: ['attributes', 'sanity'].join(this.delimiter)
|
encodedValue: ['characteristics', 'san'].join(this.delimiter)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '+',
|
name: '+',
|
||||||
id: 'sanity_add',
|
id: 'san_add',
|
||||||
encodedValue: ['attributes', 'sanity_add'].join(this.delimiter)
|
tooltip,
|
||||||
|
encodedValue: ['characteristics', 'san_add'].join(this.delimiter)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '-',
|
name: '-',
|
||||||
id: 'sanity_subtract',
|
id: 'san_subtract',
|
||||||
encodedValue: ['attributes', 'sanity_subtract'].join(this.delimiter)
|
tooltip,
|
||||||
|
encodedValue: ['characteristics', 'san_subtract'].join(this.delimiter)
|
||||||
})
|
})
|
||||||
await this.addActions(actions, { id: 'other_sanity', type: 'system' })
|
await this.addActions(actions, { id: 'other_san', type: 'system' })
|
||||||
}
|
}
|
||||||
if (typeof this.actor.system.hp.value !== 'undefined') {
|
if (typeof this.actor.system.hp.value !== 'undefined') {
|
||||||
const actions = []
|
const actions = []
|
||||||
const groupData = {
|
const groupData = {
|
||||||
id: 'other_health',
|
id: 'other_hp',
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.HP'),
|
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.HP'),
|
||||||
type: 'system'
|
type: 'system'
|
||||||
}
|
}
|
||||||
this.addGroup(groupData, { id: 'other', type: 'system' }, true)
|
this.addGroup(groupData, { id: 'other', type: 'system' }, true)
|
||||||
const tooltip = {
|
const tooltip = {
|
||||||
content: String(this.actor.system.hp.value + '/' + this.actor.system.hp.max),
|
content: `${this.actor.system.hp.value}/${this.actor.system.hp.max}`,
|
||||||
class: 'tah-system-tooltip',
|
class: 'tah-system-tooltip',
|
||||||
direction: 'LEFT'
|
direction: 'LEFT'
|
||||||
}
|
}
|
||||||
actions.push({
|
actions.push({
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.HP'),
|
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.HP'),
|
||||||
id: 'health',
|
id: 'hp',
|
||||||
info1: this.#showValue() ? { text: tooltip.content } : null,
|
info1: this.#showValue() ? { text: tooltip.content } : null,
|
||||||
tooltip,
|
tooltip,
|
||||||
encodedValue: ['attributes', 'health'].join(this.delimiter)
|
encodedValue: ['characteristics', 'hp'].join(this.delimiter)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '+',
|
name: '+',
|
||||||
id: 'health_add',
|
id: 'hp_add',
|
||||||
encodedValue: ['attributes', 'health_add'].join(this.delimiter)
|
tooltip,
|
||||||
|
encodedValue: ['characteristics', 'hp_add'].join(this.delimiter)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '-',
|
name: '-',
|
||||||
id: 'health_subtract',
|
id: 'hp_subtract',
|
||||||
encodedValue: ['attributes', 'health_subtract'].join(this.delimiter)
|
tooltip,
|
||||||
|
encodedValue: ['characteristics', 'hp_subtract'].join(this.delimiter)
|
||||||
})
|
})
|
||||||
await this.addActions(actions, { id: 'other_health', type: 'system' })
|
await this.addActions(actions, { id: 'other_hp', type: 'system' })
|
||||||
}
|
}
|
||||||
if (typeof this.actor.system.wp.value !== 'undefined') {
|
if (typeof this.actor.system.wp.value !== 'undefined') {
|
||||||
const actions = []
|
const actions = []
|
||||||
@@ -163,7 +168,7 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
}
|
}
|
||||||
this.addGroup(groupData, { id: 'other', type: 'system' }, true)
|
this.addGroup(groupData, { id: 'other', type: 'system' }, true)
|
||||||
const tooltip = {
|
const tooltip = {
|
||||||
content: String(this.actor.system.wp.value + '/' + this.actor.system.wp.max),
|
content: `${this.actor.system.wp.value}/${this.actor.system.wp.max}`,
|
||||||
class: 'tah-system-tooltip',
|
class: 'tah-system-tooltip',
|
||||||
direction: 'LEFT'
|
direction: 'LEFT'
|
||||||
}
|
}
|
||||||
@@ -172,17 +177,19 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
id: 'wp',
|
id: 'wp',
|
||||||
info1: this.#showValue() ? { text: tooltip.content } : null,
|
info1: this.#showValue() ? { text: tooltip.content } : null,
|
||||||
tooltip,
|
tooltip,
|
||||||
encodedValue: ['attributes', 'wp'].join(this.delimiter)
|
encodedValue: ['characteristics', 'wp'].join(this.delimiter)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '+',
|
name: '+',
|
||||||
id: 'wp_add',
|
id: 'wp_add',
|
||||||
encodedValue: ['attributes', 'wp_add'].join(this.delimiter)
|
tooltip,
|
||||||
|
encodedValue: ['characteristics', 'wp_add'].join(this.delimiter)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '-',
|
name: '-',
|
||||||
id: 'wp_subtract',
|
id: 'wp_subtract',
|
||||||
encodedValue: ['attributes', 'wp_subtract'].join(this.delimiter)
|
tooltip,
|
||||||
|
encodedValue: ['characteristics', 'wp_subtract'].join(this.delimiter)
|
||||||
})
|
})
|
||||||
await this.addActions(actions, { id: 'other_wp', type: 'system' })
|
await this.addActions(actions, { id: 'other_wp', type: 'system' })
|
||||||
}
|
}
|
||||||
@@ -190,19 +197,23 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
|
|
||||||
async buildSkills() {
|
async buildSkills() {
|
||||||
const actions = []
|
const actions = []
|
||||||
let actorSkills = this.actor.items.filter(item => item.type === 'skill')
|
// Build alpha sorted skill list
|
||||||
for (const skill in actorSkills) {
|
let skills = this.actor.items.filter(i => i.type === 'skill')
|
||||||
if (skill.system.computeScore() > 0) {
|
skills = skills.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
|
||||||
|
for (const skill of skills) {
|
||||||
|
//console.log('Processing skill item:', skill)
|
||||||
|
if (skill.system.skillTotal > 0) {
|
||||||
const tooltip = {
|
const tooltip = {
|
||||||
content: String(skill.skill.system.computeScore()),
|
content: String(skill.system.skillTotal),
|
||||||
direction: 'LEFT'
|
direction: 'LEFT'
|
||||||
}
|
}
|
||||||
actions.push({
|
actions.push({
|
||||||
name: skill.name,
|
name: `${skill.name} (${skill.system.skillTotal})`,
|
||||||
id: skill.id,
|
id: skill.id,
|
||||||
info1: this.#showValue() ? { text: tooltip.content } : null,
|
info1: this.#showValue() ? { text: tooltip.content } : null,
|
||||||
tooltip,
|
tooltip,
|
||||||
encodedValue: ['skills', s].join(this.delimiter)
|
encodedValue: ['skills', skill.id].join(this.delimiter)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,76 +221,54 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async buildEquipment() {
|
async buildEquipment() {
|
||||||
let weapons = this.actor.items.filter(item => item.type === 'weapon')
|
let era = game.settings.get("fvtt-cthulhu-eternal", "settings-era")
|
||||||
let skills = this.actor.items.filter(item => item.type === 'skill')
|
|
||||||
|
const weapons = this.actor.items.filter(i => i.type === 'weapon')
|
||||||
for (const item of weapons) {
|
for (const item of weapons) {
|
||||||
// Push the weapon name as a new group
|
// Push the weapon name as a new group
|
||||||
const groupData = {
|
const groupData = {
|
||||||
id: 'weapons_' + item._id,
|
id: `weapons_${item._id}`,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
type: 'system'
|
type: 'system'
|
||||||
}
|
}
|
||||||
if (!SYSTEM.WEAPON_SKILL_MAPPING[era] || !SYSTEM.WEAPON_SKILL_MAPPING[era][options.rollItem.system.weaponType]) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
let skillName = game.i18n.localize(SYSTEM.WEAPON_SKILL_MAPPING[era][options.rollItem.system.weaponType])
|
|
||||||
let skill = skills.find(skill => skill.name.toLowerCase() === skillName.toLowerCase())
|
|
||||||
this.addGroup(groupData, { id: 'weapons', type: 'system' }, true)
|
this.addGroup(groupData, { id: 'weapons', type: 'system' }, true)
|
||||||
if (item.type === 'weapon') {
|
let skill = CthulhuEternalUtils.getWeaponSkill(this.actor, item, era)
|
||||||
const weapons = []
|
item.skillTotal = skill ? skill.system.skillTotal : 0
|
||||||
|
let weaponActions = []
|
||||||
const tooltip = {
|
const tooltip = {
|
||||||
content: String(skill.system.computeScore()),
|
content: String(item.skillTotal),
|
||||||
direction: 'LEFT'
|
direction: 'LEFT'
|
||||||
}
|
}
|
||||||
weapons.push({
|
weaponActions.push({
|
||||||
name: skill.name,
|
name: `${item.name} (${item.skillTotal})`,
|
||||||
id: skill._id,
|
id: `weapon_${item._id}`,
|
||||||
info1: this.#showValue() ? { text: tooltip.content } : null,
|
info1: this.#showValue() ? { text: tooltip.content } : null,
|
||||||
encodedValue: ['weapons', item._id].join(this.delimiter),
|
encodedValue: ['weapons', item._id].join(this.delimiter),
|
||||||
tooltip
|
tooltip
|
||||||
})
|
})
|
||||||
|
let damage = ''
|
||||||
|
if (item.system.lethality > 0) {
|
||||||
|
damage = `L:${item.system.lethality}%`
|
||||||
|
} else {
|
||||||
|
damage = item.system.damage
|
||||||
|
}
|
||||||
const damageTooltip = {
|
const damageTooltip = {
|
||||||
content: String(item.system.damage),
|
content: String(damage),
|
||||||
direction: 'LEFT'
|
direction: 'LEFT'
|
||||||
}
|
}
|
||||||
if (item.system.damage !== '') {
|
if (item.system.damage !== '') {
|
||||||
weapons.push({
|
weaponActions.push({
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.Damage'),
|
name: `${coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.Damage')} (${damage})`,
|
||||||
id: item._id,
|
id: `damage_${item._id}`,
|
||||||
info1: this.#showValue() ? { text: damageTooltip.content } : null,
|
info1: this.#showValue() ? { text: damageTooltip.content } : null,
|
||||||
encodedValue: ['damage', item._id].join(this.delimiter),
|
encodedValue: ['damage', item._id].join(this.delimiter),
|
||||||
tooltip: damageTooltip
|
tooltip: damageTooltip
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (item.system.isLethal) {
|
await this.addActions(weaponActions, {
|
||||||
const lethalityTooltip = {
|
id: `weapons_${item._id}`,
|
||||||
content: String(item.system.lethality),
|
|
||||||
direction: 'LEFT'
|
|
||||||
}
|
|
||||||
weapons.push({
|
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.Lethality'),
|
|
||||||
id: item._id,
|
|
||||||
info1: this.#showValue() ? { text: lethalityTooltip.content } : null,
|
|
||||||
encodedValue: ['lethality', item._id].join(this.delimiter),
|
|
||||||
tooltip: lethalityTooltip
|
|
||||||
})
|
|
||||||
}
|
|
||||||
await this.addActions(weapons, {
|
|
||||||
id: 'weapons_' + item._id,
|
|
||||||
type: 'system'
|
type: 'system'
|
||||||
})
|
})
|
||||||
}/* else if (item.type === 'ritual') {
|
|
||||||
rituals.push({
|
|
||||||
name: item.name,
|
|
||||||
id: item._id,
|
|
||||||
encodedValue: ['rituals', item.name].join(this.delimiter)
|
|
||||||
})
|
|
||||||
} */
|
|
||||||
|
|
||||||
/* await this.addActions(rituals, {
|
|
||||||
id: 'rituals',
|
|
||||||
type: 'system'
|
|
||||||
}) */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Module-based constants
|
* Module-based constants
|
||||||
*/
|
*/
|
||||||
export const SYSTEM = {
|
export const MODULE = {
|
||||||
ID: 'fvtt-cthulhu-eternal'
|
ID: 'token-action-hud-cthulhu-eternal'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,24 +15,25 @@ export const CORE_MODULE = {
|
|||||||
/**
|
/**
|
||||||
* Core module version required by the system module
|
* Core module version required by the system module
|
||||||
*/
|
*/
|
||||||
export const REQUIRED_CORE_MODULE_VERSION = '2.0'
|
export const REQUIRED_CORE_MODULE_VERSION = '2'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action types
|
* Action types
|
||||||
*/
|
*/
|
||||||
export const ACTION_TYPE = {
|
export const ACTION_TYPE = {
|
||||||
attributes: 'CTHULHUETERNAL.Label.Characteristics',
|
characteristics: 'CTHULHUETERNAL.Label.characteristics',
|
||||||
skills: 'CTHULHUETERNAL.Label.Skill',
|
skills: 'CTHULHUETERNAL.Label.Skills',
|
||||||
equipment: 'CTHULHUETERNAL.Label.Gear'
|
equipment: 'CTHULHUETERNAL.Label.gear'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Groups
|
* Groups
|
||||||
*/
|
*/
|
||||||
export const GROUP = {
|
export const GROUP = {
|
||||||
attributes: { id: 'attributes', name: 'CTHULHUETERNAL.Label.Characteristics', type: 'system' },
|
characteristics: { id: 'characteristics', name: 'CTHULHUETERNAL.Label.characteristics', type: 'system' },
|
||||||
luck: { id: 'luck', name: 'CTHULHUETERNAL.Label.Luck', type: 'system'},
|
luck: { id: 'luck', name: 'CTHULHUETERNAL.Label.luck', type: 'system' },
|
||||||
|
other: { id: 'other', name: 'CTHULHUETERNAL.Label.other', type: 'system' },
|
||||||
skills: { id: 'skills', name: 'CTHULHUETERNAL.Label.Skills', type: 'system' },
|
skills: { id: 'skills', name: 'CTHULHUETERNAL.Label.Skills', type: 'system' },
|
||||||
weapons: { id: 'weapons', name: 'CTHULHUETERNAL.Label.Weapons', type: 'system' },
|
weapons: { id: 'weapons', name: 'CTHULHUETERNAL.Label.weapons', type: 'system' },
|
||||||
rituals: { id: 'rituals', name: 'CTHULHUETERNAL.Label.Rituals', type: 'system' }
|
rituals: { id: 'rituals', name: 'CTHULHUETERNAL.Label.rituals', type: 'system' }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
{
|
{
|
||||||
nestId: 'statistics',
|
nestId: 'statistics',
|
||||||
id: 'statistics',
|
id: 'statistics',
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.Characteristics'),
|
name: game.i18n.localize('CTHULHUETERNAL.Label.characteristics'),
|
||||||
groups: [
|
groups: [
|
||||||
{ ...groups.attributes, nestId: 'statistics_attributes' },
|
{ ...groups.characteristics, nestId: 'statistics_characteristics' },
|
||||||
{ ...groups.other, nestId: 'statistics_other' },
|
{ ...groups.other, nestId: 'statistics_other' },
|
||||||
{ ...groups.luck, nestId: 'statistics_luck' }
|
{ ...groups.luck, nestId: 'statistics_luck' }
|
||||||
]
|
]
|
||||||
@@ -27,17 +27,15 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
{
|
{
|
||||||
nestId: 'skills',
|
nestId: 'skills',
|
||||||
id: 'skills',
|
id: 'skills',
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.Skills'),
|
name: game.i18n.localize('CTHULHUETERNAL.Label.skills'),
|
||||||
groups: [
|
groups: [
|
||||||
{ ...groups.skills, nestId: 'skills_skills' },
|
{ ...groups.skills, nestId: 'skills_skills' }
|
||||||
{ ...groups.typedSkills, nestId: 'skills_typed' },
|
|
||||||
{ ...groups.specialTraining, nestId: 'skills_special' }
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nestId: 'equipment',
|
nestId: 'equipment',
|
||||||
id: 'equipment',
|
id: 'equipment',
|
||||||
name: coreModule.api.Utils.i18n('CTHULHUETERNAL.Label.Gear'),
|
name: game.i18n.localize('CTHULHUETERNAL.Label.gear'),
|
||||||
groups: [
|
groups: [
|
||||||
{ ...groups.weapons, nestId: 'equipment_weapons' },
|
{ ...groups.weapons, nestId: 'equipment_weapons' },
|
||||||
{ ...groups.rituals, nestId: 'equipment_rituals' }
|
{ ...groups.rituals, nestId: 'equipment_rituals' }
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import { SYSTEM } from "../../config/system.mjs"
|
|
||||||
import CthulhuEternalRoll from '../../documents/roll.mjs'
|
|
||||||
|
|
||||||
export let RollHandler = null
|
export let RollHandler = null
|
||||||
|
|
||||||
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
||||||
@@ -18,7 +15,7 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
async handleActionClick(event, encodedValue) {
|
async handleActionClick(event, encodedValue) {
|
||||||
const [actionTypeId, actionId] = encodedValue.split('|')
|
const [actionTypeId, actionId] = encodedValue.split('|')
|
||||||
|
|
||||||
const knownCharacters = ['character']
|
const knownCharacters = ['protagonist', 'creature']
|
||||||
|
|
||||||
// If single actor is selected
|
// If single actor is selected
|
||||||
if (this.actor) {
|
if (this.actor) {
|
||||||
@@ -67,8 +64,8 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
*/
|
*/
|
||||||
async #handleAction(event, actor, token, actionTypeId, actionId) {
|
async #handleAction(event, actor, token, actionTypeId, actionId) {
|
||||||
switch (actionTypeId) {
|
switch (actionTypeId) {
|
||||||
case 'attributes':
|
case 'characteristics':
|
||||||
await this.#handleAttributesAction(event, actor, actionId)
|
await this.#handleCharacteristicsAction(event, actor, actionId)
|
||||||
break
|
break
|
||||||
case 'skills':
|
case 'skills':
|
||||||
await this.#handleSkillsAction(event, actor, actionId)
|
await this.#handleSkillsAction(event, actor, actionId)
|
||||||
@@ -82,12 +79,6 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
case 'lethality':
|
case 'lethality':
|
||||||
await this.#handleLethalityAction(event, actor, actionId)
|
await this.#handleLethalityAction(event, actor, actionId)
|
||||||
break
|
break
|
||||||
case 'specialTraining':
|
|
||||||
await this.#handleSpecialTrainingAction(event, actor, actionId)
|
|
||||||
break
|
|
||||||
case 'typedSkills':
|
|
||||||
await this.#handleCustomTypedAction(event, actor, actionId)
|
|
||||||
break
|
|
||||||
/* case 'rituals':
|
/* case 'rituals':
|
||||||
await this.#handleRitualsAction(event, actor, actionId)
|
await this.#handleRitualsAction(event, actor, actionId)
|
||||||
break */
|
break */
|
||||||
@@ -98,40 +89,42 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Attribute action
|
* Handle Characteristic action
|
||||||
* @private
|
* @private
|
||||||
* @param {object} event The event
|
* @param {object} event The event
|
||||||
* @param {object} actor The actor
|
* @param {object} actor The actor
|
||||||
* @param {string} actionId The action id
|
* @param {string} actionId The action id
|
||||||
*/
|
*/
|
||||||
async #handleAttributesAction (event, actor, actionId) {
|
async #handleCharacteristicsAction(event, actor, actionId) {
|
||||||
let rollType
|
if (actionId === 'wp' || actionId === 'hp') return
|
||||||
if (actionId === 'wp' || actionId === 'health') return
|
|
||||||
if (actionId.includes('_add') || actionId.includes('_subtract')) {
|
if (actionId.includes('_add') || actionId.includes('_subtract')) {
|
||||||
const attr = actionId.split('_')[0]
|
const attr = actionId.split('_')[0]
|
||||||
const action = actionId.split('_')[1]
|
const action = actionId.split('_')[1]
|
||||||
const update = {}
|
const update = {}
|
||||||
update.system = {}
|
update.system = {}
|
||||||
update.system[attr] = {}
|
update.system[attr] = {}
|
||||||
update.system[attr].value = action === 'add' ? this.actor.system[attr].value + 1 : this.actor.system[attr].value - 1
|
update.system[attr].value = action === 'add' ? actor.system[attr].value + 1 : actor.system[attr].value - 1
|
||||||
if (update.system[attr].value > this.actor.system[attr].max || update.system[attr].value < this.actor.system[attr].min) return
|
if (update.system[attr].value > actor.system[attr].max || update.system[attr].value < actor.system[attr].min) return
|
||||||
return await this.actor.update(update)
|
return await actor.update(update)
|
||||||
}
|
}
|
||||||
if (actionId === 'sanity') {
|
if (actionId === 'san') {
|
||||||
rollType = actionId
|
let item = foundry.utils.duplicate(actor.system.san)
|
||||||
|
item.name = game.i18n.localize("CTHULHUETERNAL.Label.SAN")
|
||||||
|
item.targetScore = item.value
|
||||||
|
await actor.system.roll('san', item)
|
||||||
} else if (actionId === 'luck') {
|
} else if (actionId === 'luck') {
|
||||||
rollType = actionId
|
let item = foundry.utils.duplicate(actor.system.characteristics.int)
|
||||||
|
item.name = game.i18n.localize("CTHULHUETERNAL.Label.Luck")
|
||||||
|
item.value = 10
|
||||||
|
item.targetScore = 50
|
||||||
|
await actor.system.roll('luck', item)
|
||||||
} else {
|
} else {
|
||||||
rollType = 'stat'
|
let item = foundry.utils.duplicate(actor.system.characteristics[actionId])
|
||||||
|
item.name = game.i18n.localize(`CTHULHUETERNAL.Label.${actionId}Long`)
|
||||||
|
item.targetScore = item.value * 5
|
||||||
|
await actor.system.roll('char', item)
|
||||||
}
|
}
|
||||||
const options = {
|
|
||||||
actor: this.actor,
|
|
||||||
rollType,
|
|
||||||
key: actionId
|
|
||||||
}
|
|
||||||
|
|
||||||
const roll = new DGPercentileRoll('1D100', {}, options)
|
|
||||||
return await this.actor.sheet.processRoll(event, roll)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,63 +135,11 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
* @param {string} actionId The action id
|
* @param {string} actionId The action id
|
||||||
*/
|
*/
|
||||||
async #handleSkillsAction(event, actor, actionId) {
|
async #handleSkillsAction(event, actor, actionId) {
|
||||||
const options = {
|
const skill = actor.items.find(i => i.type === 'skill' && i.id === actionId)
|
||||||
actor: this.actor,
|
if (!skill) return ui.notifications.warn(`Skill not found for action id '${actionId}'`)
|
||||||
rollType: 'skill',
|
await actor.system.roll('skill', skill)
|
||||||
key: actionId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const skill = this.actor.system.skills[actionId]
|
|
||||||
if (!skill) return ui.notifications.warn('Bad skill name in HUD.')
|
|
||||||
|
|
||||||
const roll = new DGPercentileRoll('1D100', {}, options)
|
|
||||||
await this.actor.sheet.processRoll(event, roll)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle Typed/Custom skills action
|
|
||||||
* @private
|
|
||||||
* @param {object} event The event
|
|
||||||
* @param {object} actor The actor
|
|
||||||
* @param {string} actionId The action id
|
|
||||||
*/
|
|
||||||
async #handleCustomTypedAction (event, actor, actionId) {
|
|
||||||
const options = {
|
|
||||||
actor: this.actor,
|
|
||||||
rollType: 'skill',
|
|
||||||
key: actionId
|
|
||||||
}
|
|
||||||
const roll = new DGPercentileRoll('1D100', {}, options)
|
|
||||||
await this.actor.sheet.processRoll(event, roll)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle SoecialTraining action
|
|
||||||
* @private
|
|
||||||
* @param {object} event The event
|
|
||||||
* @param {object} actor The actor
|
|
||||||
* @param {string} actionId The action id
|
|
||||||
*/
|
|
||||||
async #handleSpecialTrainingAction (event, actor, actionId) {
|
|
||||||
const attr = this.actor.system.specialTraining.find(a => a.name === actionId).attribute
|
|
||||||
let target = 0
|
|
||||||
if (DG.statistics.includes(attr)) {
|
|
||||||
target = this.actor.system.statistics[attr].x5
|
|
||||||
} else if (DG.skills.includes(attr)) {
|
|
||||||
target = this.actor.system.skills[attr].proficiency
|
|
||||||
} else {
|
|
||||||
target = this.actor.system.typedSkills[attr].proficiency
|
|
||||||
}
|
|
||||||
const options = {
|
|
||||||
actor: this.actor,
|
|
||||||
rollType: 'special-training',
|
|
||||||
key: attr,
|
|
||||||
specialTrainingName: actionId,
|
|
||||||
target
|
|
||||||
}
|
|
||||||
const roll = new DGPercentileRoll('1D100', {}, options)
|
|
||||||
await this.actor.sheet.processRoll(event, roll)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Weapon action
|
* Handle Weapon action
|
||||||
@@ -208,15 +149,11 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
* @param {string} actionId The action id
|
* @param {string} actionId The action id
|
||||||
*/
|
*/
|
||||||
async #handleWeaponsAction(event, actor, actionId) {
|
async #handleWeaponsAction(event, actor, actionId) {
|
||||||
const item = this.actor.items.get(actionId)
|
const weapon = actor.items.find(i => i.type === 'weapon' && i.id === actionId)
|
||||||
const options = {
|
if (!weapon) return ui.notifications.warn(`Weapon not found for action id '${actionId}'`)
|
||||||
actor: this.actor,
|
weapon.damageFormula = weapon.system.damage
|
||||||
rollType: 'weapon',
|
weapon.damageBonus = actor.system.damageBonus
|
||||||
key: item.system.skill,
|
await actor.system.roll('weapon', weapon)
|
||||||
item
|
|
||||||
}
|
|
||||||
const roll = new DGPercentileRoll('1D100', {}, options)
|
|
||||||
await this.actor.sheet.processRoll(event, roll)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -227,21 +164,11 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
* @param {string} actionId The action id
|
* @param {string} actionId The action id
|
||||||
*/
|
*/
|
||||||
async #handleDamageAction(event, actor, actionId) {
|
async #handleDamageAction(event, actor, actionId) {
|
||||||
const item = this.actor.items.get(actionId)
|
const weapon = actor.items.find(i => i.type === 'weapon' && i.id === actionId)
|
||||||
if (item.system.lethality > 0 && event.ctrlKey) {
|
if (!weapon) return ui.notifications.warn(`Weapon not found for action id '${actionId}'`)
|
||||||
// Toggle on/off lethality
|
weapon.damageFormula = weapon.system.damage
|
||||||
const isLethal = !item.system.isLethal
|
weapon.damageBonus = actor.system.damageBonus
|
||||||
await item.update({ 'system.isLethal': isLethal })
|
await actor.system.roll('damage', weapon)
|
||||||
} else {
|
|
||||||
const options = {
|
|
||||||
actor: this.actor,
|
|
||||||
rollType: 'damage',
|
|
||||||
key: item.system.damage,
|
|
||||||
item
|
|
||||||
}
|
|
||||||
const roll = new DGDamageRoll(item.system.damage, {}, options)
|
|
||||||
await this.actor.sheet.processRoll(event, roll)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -252,19 +179,20 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
* @param {string} actionId The action id
|
* @param {string} actionId The action id
|
||||||
*/
|
*/
|
||||||
async #handleLethalityAction(event, actor, actionId) {
|
async #handleLethalityAction(event, actor, actionId) {
|
||||||
const item = await this.actor.items.get(actionId)
|
const item = actor.items.get(actionId)
|
||||||
if (item.system.damage !== '' && event.ctrlKey) {
|
if (item.system.damage !== '' && event.ctrlKey) {
|
||||||
const isLethal = !item.system.isLethal
|
const isLethal = !item.system.isLethal
|
||||||
await item.update({ 'system.isLethal': isLethal })
|
await item.update({ 'system.isLethal': isLethal })
|
||||||
} else {
|
} else {
|
||||||
const options = {
|
const options = {
|
||||||
actor: this.actor,
|
actor,
|
||||||
rollType: 'lethality',
|
rollType: 'lethality',
|
||||||
key: item.system.lethality,
|
key: item.system.lethality,
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
|
/* TOFIX
|
||||||
const roll = new DGLethalityRoll(item.system.damage, {}, options)
|
const roll = new DGLethalityRoll(item.system.damage, {}, options)
|
||||||
await this.actor.sheet.processRoll(event, roll)
|
await this.actor.sheet.processRoll(event, roll)*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { MODULE } from './constants.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register module settings
|
||||||
|
* Called by Token Action HUD Core to register Token Action HUD system module settings
|
||||||
|
* @param {function} coreUpdate Token Action HUD Core update function
|
||||||
|
*/
|
||||||
|
export function register (coreUpdate) {
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
// System Module Imports
|
// System Module Imports
|
||||||
import { ActionHandler } from './action-handler.js'
|
import { ActionHandler } from './action-handler.js'
|
||||||
import { RollHandler as Core } from './roll-handler.js'
|
import { RollHandler as Core } from './roll-handler.js'
|
||||||
import { SYSTEM } from './constants.js'
|
|
||||||
import { DEFAULTS } from './defaults.js'
|
import { DEFAULTS } from './defaults.js'
|
||||||
|
import * as systemSettings from './settings.js'
|
||||||
|
|
||||||
export let SystemManager = null
|
export let SystemManager = null
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
* @param {function} coreUpdate The Token Action HUD Core update function
|
* @param {function} coreUpdate The Token Action HUD Core update function
|
||||||
*/
|
*/
|
||||||
registerSettings(coreUpdate) {
|
registerSettings(coreUpdate) {
|
||||||
/*systemSettings.register(coreUpdate)*/
|
systemSettings.register(coreUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,14 +78,8 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
* @returns {object} The TAH system styles
|
* @returns {object} The TAH system styles
|
||||||
*/
|
*/
|
||||||
registerStyles() {
|
registerStyles() {
|
||||||
return {
|
// No system-specific styles; use the core styles only
|
||||||
template: {
|
return {}
|
||||||
class: 'tah-style-template-style', // The class to add to first DIV element
|
|
||||||
file: 'tah-template-style', // The file without the css extension
|
|
||||||
moduleId: SYSTEM.ID, // The module ID
|
|
||||||
name: 'Template Style' // The name to display in the Token Action HUD Core 'Style' module setting
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,22 +1,7 @@
|
|||||||
import { SYSTEM } from './constants.js'
|
import { MODULE } from './constants.js'
|
||||||
|
|
||||||
export let Utils = null
|
export let Utils = null
|
||||||
|
|
||||||
function registerHUD() {
|
|
||||||
Hooks.on('tokenActionHudCoreApiReady', async () => {
|
|
||||||
/**
|
|
||||||
* Return the SystemManager and requiredCoreModuleVersion to Token Action HUD Core
|
|
||||||
*/
|
|
||||||
const module = game.system
|
|
||||||
module.api = {
|
|
||||||
requiredCoreModuleVersion: "2.0",
|
|
||||||
SystemManager
|
|
||||||
}
|
|
||||||
Hooks.call('tokenActionHudSystemReady', module)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
||||||
/**
|
/**
|
||||||
* Utility functions
|
* Utility functions
|
||||||
@@ -31,7 +16,7 @@ Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
|||||||
static getSetting(key, defaultValue = null) {
|
static getSetting(key, defaultValue = null) {
|
||||||
let value = defaultValue ?? null
|
let value = defaultValue ?? null
|
||||||
try {
|
try {
|
||||||
value = game.settings.get(SYSTEM.ID, key)
|
value = game.settings.get(MODULE.ID, key)
|
||||||
} catch {
|
} catch {
|
||||||
coreModule.api.Logger.debug(`Setting '${key}' not found`)
|
coreModule.api.Logger.debug(`Setting '${key}' not found`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,6 @@ export default class CthulhuEternalActorSheet extends HandlebarsApplicationMixin
|
|||||||
_onDragOver(event) {}
|
_onDragOver(event) {}
|
||||||
|
|
||||||
async _onDropItem(item) {
|
async _onDropItem(item) {
|
||||||
console.log("Dropped item", item)
|
|
||||||
let itemData = item.toObject()
|
let itemData = item.toObject()
|
||||||
await this.document.createEmbeddedDocuments("Item", [itemData], { renderSheet: false })
|
await this.document.createEmbeddedDocuments("Item", [itemData], { renderSheet: false })
|
||||||
}
|
}
|
||||||
@@ -160,15 +159,12 @@ export default class CthulhuEternalActorSheet extends HandlebarsApplicationMixin
|
|||||||
}
|
}
|
||||||
|
|
||||||
static #onUpdateCheckboxArray(event, target) {
|
static #onUpdateCheckboxArray(event, target) {
|
||||||
console.log("Update checkbox array", event, target)
|
|
||||||
let arrayName = target.dataset.name
|
let arrayName = target.dataset.name
|
||||||
let arrayIdx = Number(target.dataset.index)
|
let arrayIdx = Number(target.dataset.index)
|
||||||
let dataPath = `system.san.${arrayName}`
|
let dataPath = `system.san.${arrayName}`
|
||||||
let tab = foundry.utils.duplicate(this.document.system.san[arrayName])
|
let tab = foundry.utils.duplicate(this.document.system.san[arrayName])
|
||||||
tab[arrayIdx] = target.checked
|
tab[arrayIdx] = target.checked
|
||||||
this.actor.update( { [dataPath]: tab } )
|
this.actor.update( { [dataPath]: tab } )
|
||||||
// Dump
|
|
||||||
console.log("Array name", arrayName, arrayIdx, target.checked, dataPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export default class CthulhuEternalItemSheet extends HandlebarsApplicationMixin(
|
|||||||
dragover: this._onDragOver.bind(this),
|
dragover: this._onDragOver.bind(this),
|
||||||
drop: this._onDrop.bind(this),
|
drop: this._onDrop.bind(this),
|
||||||
}
|
}
|
||||||
return new DragDrop(d)
|
return new foundry.applications.ux.DragDrop.implementation(d)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ export default class CthulhuEternalCreatureSheet extends CthulhuEternalActorShee
|
|||||||
|
|
||||||
async _onDrop(event) {
|
async _onDrop(event) {
|
||||||
if (!this.isEditable || !this.isEditMode) return
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
const data = TextEditor.getDragEventData(event)
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
|
||||||
// Handle different data types
|
// Handle different data types
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export default class CthulhuEternalProtagonistSheet extends CthulhuEternalActorS
|
|||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setBP: CthulhuEternalProtagonistSheet.#onSetBP,
|
setBP: CthulhuEternalProtagonistSheet.#onSetBP,
|
||||||
|
toggleEquipped: CthulhuEternalProtagonistSheet.#onToggleEquipped,
|
||||||
createGear: CthulhuEternalProtagonistSheet.#onCreateGear,
|
createGear: CthulhuEternalProtagonistSheet.#onCreateGear,
|
||||||
createArmor: CthulhuEternalProtagonistSheet.#onCreateArmor,
|
createArmor: CthulhuEternalProtagonistSheet.#onCreateArmor,
|
||||||
createWeapon: CthulhuEternalProtagonistSheet.#onCreateWeapon,
|
createWeapon: CthulhuEternalProtagonistSheet.#onCreateWeapon,
|
||||||
@@ -155,6 +156,12 @@ export default class CthulhuEternalProtagonistSheet extends CthulhuEternalActorS
|
|||||||
this.document.system.setBP()
|
this.document.system.setBP()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static #onToggleEquipped(event, target) {
|
||||||
|
const itemId = target.dataset.itemId
|
||||||
|
const item = this.document.items.get(itemId)
|
||||||
|
if (item) item.update({ "system.equipped": !item.system.equipped })
|
||||||
|
}
|
||||||
|
|
||||||
static #onCreateGear(event, target) {
|
static #onCreateGear(event, target) {
|
||||||
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("CTHULHUETERNAL.Label.newGear"), type: "gear" }])
|
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("CTHULHUETERNAL.Label.newGear"), type: "gear" }])
|
||||||
}
|
}
|
||||||
@@ -257,7 +264,7 @@ export default class CthulhuEternalProtagonistSheet extends CthulhuEternalActorS
|
|||||||
|
|
||||||
async _onDrop(event) {
|
async _onDrop(event) {
|
||||||
if (!this.isEditable || !this.isEditMode) return
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
const data = TextEditor.getDragEventData(event)
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
|
||||||
// Handle different data types
|
// Handle different data types
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ export default class CthulhuEternalSkillSheet extends CthulhuEternalItemSheet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async #onRollProgress(event, target) {
|
static async #onRollProgress(event, target) {
|
||||||
console.log("Rolling progress for skill", this, event, target)
|
|
||||||
if (this.actor) {
|
if (this.actor) {
|
||||||
const roll = await new Roll("1d4").evaluate()
|
const roll = await new Roll("1d4").evaluate()
|
||||||
if (roll) {
|
if (roll) {
|
||||||
@@ -41,7 +40,7 @@ export default class CthulhuEternalSkillSheet extends CthulhuEternalItemSheet {
|
|||||||
user: game.user.id,
|
user: game.user.id,
|
||||||
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
||||||
content: `<div class="progress-roll">${game.i18n.localize("CTHULHUETERNAL.Label.skillProgress")} - ${this.document.name} +${roll.total}</div>`,
|
content: `<div class="progress-roll">${game.i18n.localize("CTHULHUETERNAL.Label.skillProgress")} - ${this.document.name} +${roll.total}</div>`,
|
||||||
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
|
type: CONST.CHAT_MESSAGE_STYLES.ROLL,
|
||||||
roll: roll,
|
roll: roll,
|
||||||
};
|
};
|
||||||
await ChatMessage.create(chatData);
|
await ChatMessage.create(chatData);
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ export default class CthulhuEternalVehicleSheet extends CthulhuEternalActorSheet
|
|||||||
break
|
break
|
||||||
case "description":
|
case "description":
|
||||||
context.tab = context.tabs.description
|
context.tab = context.tabs.description
|
||||||
context.enrichedDescription = await TextEditor.enrichHTML(doc.system.description, { async: true })
|
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.description, { async: true })
|
||||||
context.enrichedNotes = await TextEditor.enrichHTML(doc.system.notes, { async: true })
|
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.notes, { async: true })
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
@@ -104,7 +104,7 @@ export default class CthulhuEternalVehicleSheet extends CthulhuEternalActorSheet
|
|||||||
|
|
||||||
async _onDrop(event) {
|
async _onDrop(event) {
|
||||||
if (!this.isEditable || !this.isEditMode) return
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
const data = TextEditor.getDragEventData(event)
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
|
||||||
// Handle different data types
|
// Handle different data types
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ export default class CthulhuEternalActor extends Actor {
|
|||||||
data.items.push(skill.toObject())
|
data.items.push(skill.toObject())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.items.push({ type:"weapon", img: "systems/fvtt-cthulhu-eternal/assets/icons/icon_fist.svg",
|
data.items.push({
|
||||||
name: game.i18n.localize("CTHULHUETERNAL.Label.Unarmed"), system: { damage: "1d4-1", weaponType: "unarmed" } })
|
type: "weapon", img: "systems/fvtt-cthulhu-eternal/assets/icons/icon_fist.svg",
|
||||||
|
name: game.i18n.localize("CTHULHUETERNAL.Label.Unarmed"), system: { damage: "1d4-1", weaponType: "unarmed", applyDamageBonus: true }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.create(data, options);
|
return super.create(data, options);
|
||||||
@@ -40,9 +42,52 @@ export default class CthulhuEternalActor extends Actor {
|
|||||||
type: CONST.CHAT_MESSAGE_STYLES.OTHER
|
type: CONST.CHAT_MESSAGE_STYLES.OTHER
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (this.type === "protagonist" && changed?.system?.hp !== undefined) {
|
||||||
|
const hp = this.system.hp
|
||||||
|
this.toggleStatusEffect("dead", { active: !!hp.dead })
|
||||||
|
this.toggleStatusEffect("unconscious", { active: !!hp.unconscious && !hp.dead })
|
||||||
|
this.toggleStatusEffect("stun", { active: !!hp.stunned && !hp.dead && !hp.unconscious })
|
||||||
|
}
|
||||||
return super._onUpdate(changed, options, userId)
|
return super._onUpdate(changed, options, userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLastDefenseRoll(rollData) {
|
||||||
|
this.setFlag("fvtt-cthulhu-eternal", "last-defense-roll", rollData)
|
||||||
|
}
|
||||||
|
|
||||||
|
getLastDefenseRoll() {
|
||||||
|
return this.getFlag("fvtt-cthulhu-eternal", "last-defense-roll")
|
||||||
|
}
|
||||||
|
|
||||||
|
applyWounds(woundData) {
|
||||||
|
// Get available armor
|
||||||
|
let armors = this.items.filter(i => i.type === "armor" && i.system.equipped)
|
||||||
|
let totalArmor = 0
|
||||||
|
for (let armor of armors) {
|
||||||
|
totalArmor += armor.system.protection
|
||||||
|
}
|
||||||
|
let effectiveWounds = Math.max(woundData.rollResult - totalArmor, 0)
|
||||||
|
if (woundData.isLethal) {
|
||||||
|
effectiveWounds = this.system.hp.value // Killed!
|
||||||
|
}
|
||||||
|
// Apply armor reduction
|
||||||
|
let hp = Math.max(this.system.hp.value - effectiveWounds, 0)
|
||||||
|
if (this.system.hp.value !== hp) {
|
||||||
|
this.update({ "system.hp.value": hp })
|
||||||
|
}
|
||||||
|
// Chat message for GM only
|
||||||
|
if (game.user.isGM) {
|
||||||
|
let armorText = totalArmor > 0 ? game.i18n.format("CTHULHUETERNAL.Chat.armorAbsorbed", { armor: totalArmor }) : game.i18n.localize("CTHULHUETERNAL.Chat.noArmor")
|
||||||
|
ChatMessage.create({
|
||||||
|
user: game.user.id,
|
||||||
|
speaker: { alias: this.name },
|
||||||
|
rollMode: "gmroll",
|
||||||
|
content: game.i18n.format("CTHULHUETERNAL.Chat.woundsApplied", { name: this.name, effectiveWounds, armorText }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async createEmbeddedDocuments(embeddedName, data, operation) {
|
async createEmbeddedDocuments(embeddedName, data, operation) {
|
||||||
let newData = []
|
let newData = []
|
||||||
if (embeddedName === "Item") {
|
if (embeddedName === "Item") {
|
||||||
|
|||||||
+185
-76
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
import { SYSTEM } from "../config/system.mjs"
|
import { SYSTEM } from "../config/system.mjs"
|
||||||
|
import CthulhuEternalUtils from "../utils.mjs"
|
||||||
|
|
||||||
export default class CthulhuEternalRoll extends Roll {
|
export default class CthulhuEternalRoll extends Roll {
|
||||||
/**
|
/**
|
||||||
@@ -128,51 +129,55 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
static async processWeaponDamage(actor, options) {
|
static async processWeaponDamage(actor, options) {
|
||||||
let isLethal = false
|
let isLethal = false
|
||||||
let weapon = options.rollItem
|
let weapon = options.rollItem
|
||||||
let ammoUsed = weapon.system.weaponType.includes("ranged") ? 1 : 0 // Default ammo used for melee weapons is 0
|
|
||||||
options.isNudge = false
|
options.isNudge = false
|
||||||
|
|
||||||
// Selective fire management
|
// Selective fire management
|
||||||
if (weapon.system.hasSelectiveFire && weapon.selectiveFireChoice) {
|
if (weapon.system.hasSelectiveFire && weapon.selectiveFireChoice) {
|
||||||
let choice = SYSTEM.WEAPON_SELECTIVE_FIRE_CHOICES[weapon.selectiveFireChoice]
|
let choice = SYSTEM.WEAPON_SELECTIVE_FIRE_CHOICES[weapon.selectiveFireChoice]
|
||||||
if (choice.ammoUsed > weapon.system.ammo.value) {
|
|
||||||
ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Notifications.NoAmmo"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
weapon.system.selectiveFireChoiceLabel = choice.label // Store the choice in the weapon
|
weapon.system.selectiveFireChoiceLabel = choice.label // Store the choice in the weapon
|
||||||
weapon.system.lethality = choice.lethality // Override lethality
|
weapon.system.lethality = choice.lethality // Override lethality
|
||||||
weapon.system.killRadius = choice.killRadius // Override kill radius
|
weapon.system.killRadius = choice.killRadius // Override kill radius
|
||||||
ammoUsed = choice.ammoUsed // Override ammo used
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ammoUsed = Number(ammoUsed)
|
let targets = []
|
||||||
|
if (game?.combat?.combatants) {
|
||||||
|
for (let c of game.combat.combatants) {
|
||||||
|
if (c.actorId !== actor.id) {
|
||||||
|
targets.push({ id: c.id, name: c.name, actorId: c.actorId, tokenId: c.token?.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fall back to scene tokens if not in combat
|
||||||
|
if (targets.length === 0 && canvas?.scene) {
|
||||||
|
for (let tokenDoc of canvas.scene.tokens) {
|
||||||
|
if (tokenDoc.actorId !== actor.id) {
|
||||||
|
targets.push({ name: tokenDoc.name, actorId: tokenDoc.actorId, tokenId: tokenDoc.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (weapon.system.lethality > 0) {
|
if (weapon.system.lethality > 0) {
|
||||||
let lethalityRoll = new Roll("1d100")
|
let lethalityRoll = new Roll("1d100")
|
||||||
await lethalityRoll.evaluate()
|
await lethalityRoll.evaluate()
|
||||||
let lethalScore = (options?.previousResultType === "successCritical") ? weapon.system.lethality * 2 : weapon.system.lethality
|
let lethalScore = (options?.previousResultType === "successCritical") ? weapon.system.lethality * 2 : weapon.system.lethality
|
||||||
isLethal = (lethalityRoll.total <= lethalScore)
|
isLethal = (lethalityRoll.total <= lethalScore)
|
||||||
if (ammoUsed > 0) {
|
|
||||||
await actor.updateEmbeddedDocuments("Item", [{
|
|
||||||
_id: weapon._id,
|
|
||||||
"system.ammo.value": Math.max(0, weapon.system.ammo.value - ammoUsed)
|
|
||||||
}])
|
|
||||||
}
|
|
||||||
let wounds = Math.floor(lethalityRoll.total / 10) + (lethalityRoll.total % 10)
|
let wounds = Math.floor(lethalityRoll.total / 10) + (lethalityRoll.total % 10)
|
||||||
let msgData = {
|
let msgData = {
|
||||||
|
actorId: actor.id,
|
||||||
weapon,
|
weapon,
|
||||||
wounds,
|
wounds,
|
||||||
lethalScore,
|
lethalScore,
|
||||||
isLethal,
|
isLethal,
|
||||||
ammoUsed,
|
ammoUsed: weapon?.ammoUsed || 0,
|
||||||
rollResult: lethalityRoll.total,
|
rollResult: lethalityRoll.total,
|
||||||
|
combatants: targets
|
||||||
}
|
}
|
||||||
let flavor = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-lethal-damage.hbs", msgData)
|
let msg = await ChatMessage.create({
|
||||||
ChatMessage.create({
|
|
||||||
user: game.user.id,
|
user: game.user.id,
|
||||||
content: flavor,
|
content: await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-lethal-damage.hbs", msgData),
|
||||||
speaker: ChatMessage.getSpeaker({ actor: actor }),
|
speaker: ChatMessage.getSpeaker({ actor: actor }),
|
||||||
}, { rollMode: options.rollMode, create: true })
|
}, { rollMode: options.rollMode, create: true })
|
||||||
|
await msg.setFlag("fvtt-cthulhu-eternal", "woundData", msgData)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,27 +189,23 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
if (options?.previousResultType === "successCritical") {
|
if (options?.previousResultType === "successCritical") {
|
||||||
formula = `( ${formula} ) * 2`
|
formula = `( ${formula} ) * 2`
|
||||||
}
|
}
|
||||||
if (ammoUsed > 0) {
|
|
||||||
await actor.updateEmbeddedDocuments("Item", [{
|
|
||||||
_id: weapon._id,
|
|
||||||
"system.ammo.value": Math.max(0, weapon.system.ammo.value - ammoUsed)
|
|
||||||
}])
|
|
||||||
}
|
|
||||||
console.log("Weapon damage formula", formula, weapon, ammoUsed)
|
|
||||||
let damageRoll = new Roll(formula)
|
let damageRoll = new Roll(formula)
|
||||||
await damageRoll.evaluate()
|
await damageRoll.evaluate()
|
||||||
let msgData = {
|
let msgData = {
|
||||||
|
actorId: actor.id,
|
||||||
weapon,
|
weapon,
|
||||||
formula,
|
formula,
|
||||||
ammoUsed,
|
ammoUsed: weapon?.ammoUsed || 0,
|
||||||
rollResult: damageRoll.total,
|
rollResult: damageRoll.total,
|
||||||
|
combatants: targets
|
||||||
}
|
}
|
||||||
let flavor = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-regular-damage.hbs", msgData)
|
let msg = await ChatMessage.create({
|
||||||
ChatMessage.create({
|
|
||||||
user: game.user.id,
|
user: game.user.id,
|
||||||
content: flavor,
|
content: await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-regular-damage.hbs", msgData),
|
||||||
speaker: ChatMessage.getSpeaker({ actor: actor }),
|
speaker: ChatMessage.getSpeaker({ actor: actor }),
|
||||||
}, { rollMode: options.rollMode, create: true })
|
}, { rollMode: options.rollMode, create: true })
|
||||||
|
await msg.setFlag("fvtt-cthulhu-eternal", "woundData", msgData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -215,11 +216,32 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
modifier += SYSTEM.WEAPON_VISIBILITY[rollData.visibilityChoice]?.modifier || 0
|
modifier += SYSTEM.WEAPON_VISIBILITY[rollData.visibilityChoice]?.modifier || 0
|
||||||
modifier += SYSTEM.WEAPON_ATTACKER_STATE[rollData.attackerStateChoice]?.modifier || 0
|
modifier += SYSTEM.WEAPON_ATTACKER_STATE[rollData.attackerStateChoice]?.modifier || 0
|
||||||
modifier += SYSTEM.WEAPON_TARGET_SIZE[rollData.targetSizeChoice]?.modifier || 0
|
modifier += SYSTEM.WEAPON_TARGET_SIZE[rollData.targetSizeChoice]?.modifier || 0
|
||||||
modifier += (rollData.aimingLastRound) ? 20 : 0
|
modifier += (rollData.aimingLastRoundFlag) ? 20 : 0
|
||||||
modifier += (rollData.aimingWithSight) ? 20 : 0
|
modifier += (rollData.aimingWithSightFlag) ? 20 : 0
|
||||||
return modifier
|
return modifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async processAmmoUsed(actor, weapon) {
|
||||||
|
let ammoUsed = weapon.system.weaponType.includes("ranged") ? 1 : 0 // Default ammo used for melee weapons is 0
|
||||||
|
// Selective fire management
|
||||||
|
if (weapon.system.hasSelectiveFire && weapon.selectiveFireChoice) {
|
||||||
|
let choice = SYSTEM.WEAPON_SELECTIVE_FIRE_CHOICES[weapon.selectiveFireChoice]
|
||||||
|
if (choice.ammoUsed > weapon.system.ammo.value) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Notifications.NoAmmo"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ammoUsed = choice.ammoUsed // Override ammo used
|
||||||
|
}
|
||||||
|
ammoUsed = Number(ammoUsed)
|
||||||
|
if (ammoUsed > 0) {
|
||||||
|
await actor.updateEmbeddedDocuments("Item", [{
|
||||||
|
_id: weapon._id,
|
||||||
|
"system.ammo.value": Math.max(0, weapon.system.ammo.value - ammoUsed)
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
weapon.ammoUsed = ammoUsed
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompt the user with a dialog to configure and execute a roll.
|
* Prompt the user with a dialog to configure and execute a roll.
|
||||||
*
|
*
|
||||||
@@ -238,12 +260,14 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
let formula = "1d100"
|
let formula = "1d100"
|
||||||
let hasModifier = true
|
let hasModifier = true
|
||||||
let hasMultiplier = false
|
let hasMultiplier = false
|
||||||
options.isNudge = true
|
const nudgeEnabled = game.settings.get("fvtt-cthulhu-eternal", "settings-nudge")
|
||||||
|
options.isNudge = nudgeEnabled
|
||||||
let actor = game.actors.get(options.actorId)
|
let actor = game.actors.get(options.actorId)
|
||||||
|
|
||||||
|
let target = CthulhuEternalUtils.getTarget()
|
||||||
|
|
||||||
switch (options.rollType) {
|
switch (options.rollType) {
|
||||||
case "skill":
|
case "skill":
|
||||||
console.log(options.rollItem)
|
|
||||||
options.initialScore = options.rollItem.system.computeScore()
|
options.initialScore = options.rollItem.system.computeScore()
|
||||||
break
|
break
|
||||||
case "luck":
|
case "luck":
|
||||||
@@ -254,7 +278,7 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
case "san":
|
case "san":
|
||||||
case "char":
|
case "char":
|
||||||
options.initialScore = options.rollItem.targetScore
|
options.initialScore = options.rollItem.targetScore
|
||||||
options.isNudge = (options.rollType !== "san")
|
options.isNudge = nudgeEnabled && (options.rollType !== "san")
|
||||||
break
|
break
|
||||||
case "resource":
|
case "resource":
|
||||||
hasModifier = false
|
hasModifier = false
|
||||||
@@ -273,33 +297,30 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
let era = game.settings.get("fvtt-cthulhu-eternal", "settings-era")
|
let era = game.settings.get("fvtt-cthulhu-eternal", "settings-era")
|
||||||
if (era !== options.rollItem.system.settings) {
|
if (era !== options.rollItem.system.settings) {
|
||||||
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.WrongEra"))
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.WrongEra"))
|
||||||
console.log("WP Wrong Era", era, options.rollItem.system.weaponType)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!SYSTEM.WEAPON_SKILL_MAPPING[era]?.[options.rollItem.system.weaponType]) {
|
if (!SYSTEM.WEAPON_SKILL_MAPPING[era]?.[options.rollItem.system.weaponType]) {
|
||||||
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.NoWeaponType"))
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.NoWeaponType"))
|
||||||
console.log("WP Not found", era, options.rollItem.system.weaponType)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
/*if (!target) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Notifications.AttackNoTarget"))
|
||||||
|
}*/
|
||||||
// Check if the weapon has enouth ammo in case of a firearm
|
// Check if the weapon has enouth ammo in case of a firearm
|
||||||
if (options.rollItem.system.isFireArm() && options.rollItem.system.ammo.value <= 0) {
|
if (options.rollItem.system.isFireArm() && options.rollItem.system.ammo.value <= 0) {
|
||||||
ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Notifications.NoAmmo"))
|
ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Notifications.NoAmmo"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
options.weapon = options.rollItem
|
options.weapon = options.rollItem
|
||||||
if (options.rollItem.system.hasDirectSkill) {
|
|
||||||
let skillName = options.rollItem.name
|
options.rollItem = CthulhuEternalUtils.getWeaponSkill(actor, options.rollItem, era)
|
||||||
options.rollItem = { type: "skill", name: skillName, system: { base: 0, bonus: options.weapon.system.directSkillValue } }
|
options.initialScore = options.rollItem.system.skillTotal
|
||||||
options.initialScore = options.weapon.system.directSkillValue
|
if (options.weapon.system.state === "junk") {
|
||||||
} else {
|
options.isJunk = true
|
||||||
let skillName = game.i18n.localize(SYSTEM.WEAPON_SKILL_MAPPING[era][options.rollItem.system.weaponType])
|
options.initialScore = Math.max(0, options.initialScore - 20) // -20% for junk weapons
|
||||||
options.rollItem = actor.items.find(i => i.type === "skill" && i.name.toLowerCase() === skillName.toLowerCase())
|
|
||||||
if (!options.rollItem) {
|
|
||||||
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.NoWeaponSkill"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
options.initialScore = options.rollItem.system.computeScore()
|
if (options.weapon.system.state === "worn") {
|
||||||
console.log("WEAPON", skillName, era, options.rollItem)
|
options.isWorn = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@@ -308,7 +329,7 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes); //Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
|
const rollModes = foundry.utils.duplicate(CONFIG.ChatMessage.modes ?? 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,
|
||||||
@@ -331,6 +352,8 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
targetScore: options.initialScore,
|
targetScore: options.initialScore,
|
||||||
isLowWP: options.isLowWP,
|
isLowWP: options.isLowWP,
|
||||||
isZeroWP: options.isZeroWP,
|
isZeroWP: options.isZeroWP,
|
||||||
|
isJunk: options.isJunk,
|
||||||
|
isWorn: options.isWorn,
|
||||||
isExhausted: options.isExhausted,
|
isExhausted: options.isExhausted,
|
||||||
enableHand: options.rollItem.enableHand,
|
enableHand: options.rollItem.enableHand,
|
||||||
enableStowed: options.rollItem.enableStowed,
|
enableStowed: options.rollItem.enableStowed,
|
||||||
@@ -353,10 +376,11 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
visibilityChoice: "clear",
|
visibilityChoice: "clear",
|
||||||
attackerStateChoice: "normal",
|
attackerStateChoice: "normal",
|
||||||
targetSizeChoice: "normal",
|
targetSizeChoice: "normal",
|
||||||
aimingLastRound: false,
|
aimingLastRoundFlag: false,
|
||||||
aimingWithSight: false,
|
aimingWithSightFlag: false,
|
||||||
modifier,
|
modifier,
|
||||||
formula,
|
formula,
|
||||||
|
targetName: target?.name,
|
||||||
hasTarget: options.hasTarget,
|
hasTarget: options.hasTarget,
|
||||||
hasModifier,
|
hasModifier,
|
||||||
hasMultiplier,
|
hasMultiplier,
|
||||||
@@ -402,6 +426,14 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
options.multiplier = Number(event.target.value)
|
options.multiplier = Number(event.target.value)
|
||||||
this.updateResourceDialog(options)
|
this.updateResourceDialog(options)
|
||||||
})
|
})
|
||||||
|
$(".aimingLastRound").change(event => {
|
||||||
|
options.aimingLastRoundFlag = event.target.checked
|
||||||
|
this.updateResourceDialog(options)
|
||||||
|
})
|
||||||
|
$(".aimingWithSight").change(event => {
|
||||||
|
options.aimingWithSightFlag = event.target.checked
|
||||||
|
this.updateResourceDialog(options)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -409,10 +441,12 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
if (rollContext === null) return
|
if (rollContext === null) return
|
||||||
|
|
||||||
let rollData = foundry.utils.mergeObject(foundry.utils.duplicate(options), rollContext)
|
let rollData = foundry.utils.mergeObject(foundry.utils.duplicate(options), rollContext)
|
||||||
|
// If we have a target, get its data
|
||||||
|
rollData.targetId = target?.id
|
||||||
|
rollData.targetName = target?.name
|
||||||
rollData.rollMode = rollContext.visibility
|
rollData.rollMode = rollContext.visibility
|
||||||
|
|
||||||
// Update target score
|
// Update target score
|
||||||
console.log("Rolldata", rollData, options)
|
|
||||||
if (options.rollType === "resource") {
|
if (options.rollType === "resource") {
|
||||||
rollData.targetScore = options.initialScore * Number(rollContext.multiplier)
|
rollData.targetScore = options.initialScore * Number(rollContext.multiplier)
|
||||||
} else {
|
} else {
|
||||||
@@ -432,19 +466,24 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
rollData.modifier = "0"
|
rollData.modifier = "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.rollType === "weapon") {
|
||||||
|
await this.processAmmoUsed(actor, rollData.weapon)
|
||||||
|
}
|
||||||
|
|
||||||
if (Hooks.call("fvtt-cthulhu-eternal.preRoll", options, rollData) === false) return
|
if (Hooks.call("fvtt-cthulhu-eternal.preRoll", options, rollData) === false) return
|
||||||
|
|
||||||
const roll = new this(formula, options.data, rollData)
|
const roll = new this(formula, options.data, rollData)
|
||||||
await roll.evaluate()
|
await roll.evaluate()
|
||||||
|
|
||||||
roll.displayRollResult(roll, options, rollData)
|
await roll.displayRollResult(roll, options, rollData)
|
||||||
|
|
||||||
if (Hooks.call("fvtt-cthulhu-eternal.Roll", options, rollData, roll) === false) return
|
if (Hooks.call("fvtt-cthulhu-eternal.Roll", options, rollData, roll) === false) return
|
||||||
|
|
||||||
|
|
||||||
return roll
|
return roll
|
||||||
}
|
}
|
||||||
|
|
||||||
displayRollResult(formula, options, rollData) {
|
async displayRollResult(formula, options, rollData) {
|
||||||
|
|
||||||
// Compute the result quality
|
// Compute the result quality
|
||||||
let resultType = "failure"
|
let resultType = "failure"
|
||||||
@@ -462,6 +501,11 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
resultType = "failureCritical"
|
resultType = "failureCritical"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (rollData.weapon?.system?.state === "junk" && this.total > 95) {
|
||||||
|
this.options.isDestroyed = true
|
||||||
|
rollData.isDestroyed = true
|
||||||
|
resultType = "failureCritical"
|
||||||
|
}
|
||||||
// As per the rules, a roll of 100 is always a failure, even if the target is above 100
|
// As per the rules, a roll of 100 is always a failure, even if the target is above 100
|
||||||
if (this.total === 100) {
|
if (this.total === 100) {
|
||||||
resultType = "failureCritical"
|
resultType = "failureCritical"
|
||||||
@@ -489,10 +533,79 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
}
|
}
|
||||||
rollData.resultType = resultType
|
rollData.resultType = resultType
|
||||||
|
|
||||||
|
// Compute WP cost to nudge a failure into a success (minimum roll needed = targetScore)
|
||||||
|
if (this.options.isFailure && options.isNudge && !this.options.isNudgedRoll && rollData.targetScore > 0 && this.total > rollData.targetScore) {
|
||||||
|
const actor = game.actors.get(options.actorId)
|
||||||
|
const wpAvailable = actor?.system?.wp?.value ?? 0
|
||||||
|
const wpCostToSucceed = Math.ceil((this.total - rollData.targetScore) / 5)
|
||||||
|
this.options.wpCostToSucceed = (wpCostToSucceed <= wpAvailable) ? wpCostToSucceed : 0
|
||||||
|
} else {
|
||||||
|
this.options.wpCostToSucceed = 0
|
||||||
|
}
|
||||||
|
|
||||||
this.options.isLowWP = rollData.isLowWP
|
this.options.isLowWP = rollData.isLowWP
|
||||||
this.options.isZeroWP = rollData.isZeroWP
|
this.options.isZeroWP = rollData.isZeroWP
|
||||||
this.options.isExhausted = rollData.isExhausted
|
this.options.isExhausted = rollData.isExhausted
|
||||||
|
rollData.isSuccess = this.options.isSuccess
|
||||||
|
rollData.isFailure = this.options.isFailure
|
||||||
|
rollData.isCritical = this.options.isCritical
|
||||||
|
rollData.resultType = resultType
|
||||||
|
rollData.rollResult = this.total
|
||||||
|
rollData.total = this.total
|
||||||
this.options.rollData = foundry.utils.duplicate(rollData)
|
this.options.rollData = foundry.utils.duplicate(rollData)
|
||||||
|
|
||||||
|
// Keep track of the last defense roll for the actor
|
||||||
|
if (game.combat && (rollData?.weapon?.system.type === "melee" || (rollData?.rollItem.type === "skill" && rollData?.rollItem.name?.toLowerCase().includes(game.i18n.localize("CTHULHUETERNAL.Skill.dodgeName").toLowerCase())))) {
|
||||||
|
let actor = game.actors.get(options.actorId)
|
||||||
|
rollData.round = game.combat.round
|
||||||
|
actor.setLastDefenseRoll(foundry.utils.duplicate(rollData))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game.combat && rollData?.weapon) { // An attack roll
|
||||||
|
rollData.isAttackRoll = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check if we have a target for the current roll, and if the target has done its defense roll this round
|
||||||
|
if (rollData.targetId) {
|
||||||
|
let token = game.scenes.current.tokens.get(rollData.targetId)
|
||||||
|
let defender = token.actor
|
||||||
|
let lastDefenseRoll = defender?.getLastDefenseRoll()
|
||||||
|
// Now compare opposition
|
||||||
|
this.compareRolls(rollData, lastDefenseRoll)
|
||||||
|
rollData.defenseRoll = lastDefenseRoll
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
compareRolls(attackRoll, defenseRoll) {
|
||||||
|
if (!defenseRoll || defenseRoll.round !== game?.combat?.round) {
|
||||||
|
// ui.notifications.info(game.i18n.localize("CTHULHUETERNAL.Notifications.NoDefenseRoll"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (attackRoll.isFailure) {
|
||||||
|
attackRoll.attackSucess = false
|
||||||
|
}
|
||||||
|
if (attackRoll.isSuccess && defenseRoll.isFailure) {
|
||||||
|
attackRoll.attackSucess = true
|
||||||
|
}
|
||||||
|
if (attackRoll.isSuccess && defenseRoll.isSuccess) {
|
||||||
|
if (attackRoll.isCritical && !defenseRoll.isCritical) {
|
||||||
|
attackRoll.attackSucess = true
|
||||||
|
}
|
||||||
|
else if (!attackRoll.isCritical && defenseRoll.isCritical) {
|
||||||
|
attackRoll.attackSucess = false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Both are normal success, compare the roll results
|
||||||
|
if (attackRoll.total >= defenseRoll.total) {
|
||||||
|
// Attack successful
|
||||||
|
attackRoll.attackSucess = true
|
||||||
|
} else {
|
||||||
|
// Defense successful
|
||||||
|
attackRoll.attackSucess = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -589,8 +702,20 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
* @param {boolean} [options.create=true] Whether to create the message.
|
* @param {boolean} [options.create=true] Whether to create the message.
|
||||||
* @returns {Promise} - A promise that resolves when the message is created.
|
* @returns {Promise} - A promise that resolves when the message is created.
|
||||||
*/
|
*/
|
||||||
async toMessage(messageData = {}, { rollMode, create = true } = {}) {
|
async toMessage(messageData = {}, { rollMode, messageMode, create = true } = {}) {
|
||||||
super.toMessage(
|
let rollData = this.options.rollData || this.options
|
||||||
|
let rollItem = this.options.rollItem
|
||||||
|
|
||||||
|
// Determine skill progression before calling super.toMessage so _getChatCardData picks it up
|
||||||
|
let skillMarkedForProgress = false
|
||||||
|
if (rollData.resultType?.includes("failure") && rollItem?.type === "skill") {
|
||||||
|
if (rollItem.system.diceEvolved && !rollItem.system.rollFailed && !rollItem.system.isAdversary) {
|
||||||
|
skillMarkedForProgress = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.options.skillMarkedForProgress = skillMarkedForProgress
|
||||||
|
|
||||||
|
let rollMsg = await super.toMessage(
|
||||||
{
|
{
|
||||||
isFailure: this.resultType === "failure",
|
isFailure: this.resultType === "failure",
|
||||||
actingCharName: this.actorName,
|
actingCharName: this.actorName,
|
||||||
@@ -599,33 +724,17 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
realDamage: this.realDamage,
|
realDamage: this.realDamage,
|
||||||
...messageData,
|
...messageData,
|
||||||
},
|
},
|
||||||
{ rollMode: rollMode },
|
{ messageMode: messageMode ?? rollMode },
|
||||||
)
|
)
|
||||||
|
await rollMsg.setFlag("fvtt-cthulhu-eternal", "rollData", rollData)
|
||||||
|
|
||||||
// Manage the skill evolution if the roll is a failure
|
// Apply skill rollFailed flag after the message is created
|
||||||
let rollData = this.options.rollData || this.options
|
if (skillMarkedForProgress) {
|
||||||
let rollItem = this.options.rollItem
|
|
||||||
if (rollData.resultType.includes("failure") && rollItem.type === "skill") {
|
|
||||||
// Is the skill able to progress
|
|
||||||
if (rollItem.system.diceEvolved && !rollItem.system.rollFailed) {
|
|
||||||
// If the skill is not adversary, we can evolve it
|
|
||||||
if (!rollItem.system.isAdversary) {
|
|
||||||
rollItem.system.rollFailed = true
|
|
||||||
// Get the actor and update the skill
|
|
||||||
const actor = game.actors.get(rollData.actorId)
|
const actor = game.actors.get(rollData.actorId)
|
||||||
await actor.updateEmbeddedDocuments("Item", [{
|
await actor.updateEmbeddedDocuments("Item", [{
|
||||||
_id: rollItem._id,
|
_id: rollItem._id,
|
||||||
"system.rollFailed": true
|
"system.rollFailed": true
|
||||||
}])
|
}])
|
||||||
// Create a chat message to inform the user
|
|
||||||
const flavor = `${rollItem.name} - ${game.i18n.localize("CTHULHUETERNAL.Label.skillFailed")}`
|
|
||||||
await ChatMessage.create({
|
|
||||||
user: game.user.id,
|
|
||||||
content: `<div class="cthulhu-eternal-roll"><p>${flavor}</p></div>`,
|
|
||||||
speaker: ChatMessage.getSpeaker({ actor: rollData.actor }),
|
|
||||||
}, { rollMode: rollData.rollMode, create: true })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the roll is a SAN roll, we propose to select the SAN loss
|
// If the roll is a SAN roll, we propose to select the SAN loss
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default class CthulhuEternalArmor extends foundry.abstract.TypeDataModel
|
|||||||
|
|
||||||
schema.protection = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
|
schema.protection = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
|
||||||
schema.resourceLevel = new fields.NumberField({ required: true, initial: 0, min: 0 })
|
schema.resourceLevel = new fields.NumberField({ required: true, initial: 0, min: 0 })
|
||||||
|
schema.equipped = new fields.BooleanField({ required: true, initial: false })
|
||||||
|
|
||||||
return schema
|
return schema
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default class CthulhuEternalCreature extends foundry.abstract.TypeDataMod
|
|||||||
})
|
})
|
||||||
if (!roll) return null
|
if (!roll) return null
|
||||||
|
|
||||||
await roll.toMessage({}, { rollMode: roll.options.rollMode })
|
await roll.toMessage({}, { messageMode: roll.options.rollMode })
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData
|
|||||||
insanity: new fields.StringField({ required: true, nullable: false, initial: "none", choices: SYSTEM.INSANITY }),
|
insanity: new fields.StringField({ required: true, nullable: false, initial: "none", choices: SYSTEM.INSANITY }),
|
||||||
})
|
})
|
||||||
|
|
||||||
schema.damageBonus = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
schema.damageBonus = new fields.NumberField({ ...requiredInteger, initial: 0, min: -2 })
|
||||||
|
|
||||||
schema.resources = new fields.SchemaField({
|
schema.resources = new fields.SchemaField({
|
||||||
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), // Unused but kept for compatibility
|
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), // Unused but kept for compatibility
|
||||||
@@ -127,9 +127,11 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData
|
|||||||
dmgBonus = -2
|
dmgBonus = -2
|
||||||
} else if (this.characteristics.str.value <= 8) {
|
} else if (this.characteristics.str.value <= 8) {
|
||||||
dmgBonus = -1
|
dmgBonus = -1
|
||||||
|
} else if (this.characteristics.str.value <= 12) {
|
||||||
|
dmgBonus = 0
|
||||||
} else if (this.characteristics.str.value <= 16) {
|
} else if (this.characteristics.str.value <= 16) {
|
||||||
dmgBonus = 1
|
dmgBonus = 1
|
||||||
} else if (this.characteristics.str.value <= 20) {
|
} else if (this.characteristics.str.value <= 40) {
|
||||||
dmgBonus = 2
|
dmgBonus = 2
|
||||||
}
|
}
|
||||||
if (this.damageBonus !== dmgBonus) {
|
if (this.damageBonus !== dmgBonus) {
|
||||||
@@ -275,7 +277,6 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData
|
|||||||
template = "systems/fvtt-cthulhu-eternal/templates/chat-san-loss-none.hbs"
|
template = "systems/fvtt-cthulhu-eternal/templates/chat-san-loss-none.hbs"
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("CthulhuEternalProtagonist.applySANConsequences", rollData, updates, template)
|
|
||||||
let content = await foundry.applications.handlebars.renderTemplate(template, msgData)
|
let content = await foundry.applications.handlebars.renderTemplate(template, msgData)
|
||||||
let msg = await ChatMessage.create({
|
let msg = await ChatMessage.create({
|
||||||
content: content,
|
content: content,
|
||||||
@@ -400,7 +401,7 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData
|
|||||||
})
|
})
|
||||||
if (!roll) return null
|
if (!roll) return null
|
||||||
|
|
||||||
await roll.toMessage({}, { rollMode: roll.options.rollMode })
|
await roll.toMessage({}, { messageMode: roll.options.rollMode })
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ export default class CthulhuEternalWeapon extends foundry.abstract.TypeDataModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
isRanged() {
|
isRanged() {
|
||||||
console.log("isRanged", this.weaponType, this)
|
|
||||||
return this.weaponType.match("ranged")
|
return this.weaponType.match("ranged")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+375
-12
@@ -16,6 +16,22 @@ export default class CthulhuEternalUtils {
|
|||||||
config: true,
|
config: true,
|
||||||
onChange: _ => window.location.reload()
|
onChange: _ => window.location.reload()
|
||||||
});
|
});
|
||||||
|
game.settings.register("fvtt-cthulhu-eternal", "settings-nudge", {
|
||||||
|
name: game.i18n.localize("CTHULHUETERNAL.Settings.nudge"),
|
||||||
|
hint: game.i18n.localize("CTHULHUETERNAL.Settings.nudgeHint"),
|
||||||
|
default: true,
|
||||||
|
scope: "world",
|
||||||
|
type: Boolean,
|
||||||
|
config: true
|
||||||
|
});
|
||||||
|
game.settings.register("fvtt-cthulhu-eternal", "roll-opposed-store", {
|
||||||
|
name: "Roll Opposed Store",
|
||||||
|
hint: "Whether to store opposed roll results for later use",
|
||||||
|
default: { roll1: null, roll2: null },
|
||||||
|
scope: "world",
|
||||||
|
type: Object,
|
||||||
|
config: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async loadCompendiumData(compendium) {
|
static async loadCompendiumData(compendium) {
|
||||||
@@ -110,9 +126,6 @@ export default class CthulhuEternalUtils {
|
|||||||
return Object.keys(obj).length;
|
return Object.keys(obj).length;
|
||||||
})
|
})
|
||||||
|
|
||||||
Handlebars.registerHelper('isEnabled', function (configKey) {
|
|
||||||
return game.settings.get("bol", configKey);
|
|
||||||
})
|
|
||||||
Handlebars.registerHelper('split', function (str, separator, keep) {
|
Handlebars.registerHelper('split', function (str, separator, keep) {
|
||||||
return str.split(separator)[keep];
|
return str.split(separator)[keep];
|
||||||
})
|
})
|
||||||
@@ -156,7 +169,6 @@ export default class CthulhuEternalUtils {
|
|||||||
return eval(expr);
|
return eval(expr);
|
||||||
})
|
})
|
||||||
Handlebars.registerHelper('isOwnerOrGM', function (actor) {
|
Handlebars.registerHelper('isOwnerOrGM', function (actor) {
|
||||||
console.log("Testing actor", actor.isOwner, game.userId)
|
|
||||||
return actor.isOwner || game.isGM;
|
return actor.isOwner || game.isGM;
|
||||||
})
|
})
|
||||||
Handlebars.registerHelper('upperFirst', function (text) {
|
Handlebars.registerHelper('upperFirst', function (text) {
|
||||||
@@ -180,6 +192,53 @@ export default class CthulhuEternalUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static removeChatMessageId(messageId) {
|
||||||
|
if (messageId) {
|
||||||
|
game.messages.get(messageId)?.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static findChatMessageId(current) {
|
||||||
|
return HawkmoonUtility.getChatMessageId(HawkmoonUtility.findChatMessage(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
static getChatMessageId(node) {
|
||||||
|
return node?.attributes.getNamedItem('data-message-id')?.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static findChatMessage(current) {
|
||||||
|
return HawkmoonUtility.findNodeMatching(current, it => it.classList.contains('chat-message') && it.attributes.getNamedItem('data-message-id'))
|
||||||
|
}
|
||||||
|
|
||||||
|
static findNodeMatching(current, predicate) {
|
||||||
|
if (current) {
|
||||||
|
if (predicate(current)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
return HawkmoonUtility.findNodeMatching(current.parentElement, predicate);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getWeaponSkill(actor, weapon, era) {
|
||||||
|
let skill
|
||||||
|
if (weapon.system.hasDirectSkill) {
|
||||||
|
let skillName = weapon.name
|
||||||
|
skill = { type: "skill", name: skillName, system: { base: 0, bonus: weapon.system.directSkillValue, skillTotal: weapon.system.directSkillValue } }
|
||||||
|
} else {
|
||||||
|
let skillName = game.i18n.localize(SYSTEM.WEAPON_SKILL_MAPPING[era][weapon.system.weaponType])
|
||||||
|
skill = actor.items.find(i => i.type === "skill" && i.name.toLowerCase() === skillName.toLowerCase())
|
||||||
|
if (!skill) {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.NoWeaponSkill"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return skill
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
static async applySANType(rollMessage, event) {
|
static async applySANType(rollMessage, event) {
|
||||||
let rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
|
let rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
|
||||||
if (!rollData) {
|
if (!rollData) {
|
||||||
@@ -248,6 +307,132 @@ export default class CthulhuEternalUtils {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async opposedRollManagement(rollMessage, event) {
|
||||||
|
let rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
|
||||||
|
if (!rollData) {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noRollDataFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the store
|
||||||
|
let store = game.settings.get("fvtt-cthulhu-eternal", "roll-opposed-store")
|
||||||
|
if (!store.roll1) {
|
||||||
|
store.roll1 = {
|
||||||
|
rollData: rollData,
|
||||||
|
messageId: rollMessage.id
|
||||||
|
}
|
||||||
|
await game.settings.set("fvtt-cthulhu-eternal", "roll-opposed-store", store)
|
||||||
|
ui.notifications.info(game.i18n.localize("CTHULHUETERNAL.Notifications.opposedRollFirstStored"))
|
||||||
|
}
|
||||||
|
else if (!store.roll2) {
|
||||||
|
store.roll2 = {
|
||||||
|
rollData: rollData,
|
||||||
|
messageId: rollMessage.id
|
||||||
|
}
|
||||||
|
await game.settings.set("fvtt-cthulhu-eternal", "roll-opposed-store", store)
|
||||||
|
ui.notifications.info(game.i18n.localize("CTHULHUETERNAL.Notifications.opposedRollSecondStored"))
|
||||||
|
// Now perform the opposed roll resolution
|
||||||
|
await this.resolveOpposedRolls(store.roll1, store.roll2)
|
||||||
|
// Clear the store
|
||||||
|
store.roll1 = null
|
||||||
|
store.roll2 = null
|
||||||
|
await game.settings.set("fvtt-cthulhu-eternal", "roll-opposed-store", store)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.opposedRollStoreFull"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async resolveOpposedRolls(roll1, roll2) {
|
||||||
|
// Get actors
|
||||||
|
let actor1 = game.actors.get(roll1.rollData.actorId)
|
||||||
|
let actor2 = game.actors.get(roll2.rollData.actorId)
|
||||||
|
if (!actor1 || !actor2) {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Determine winner
|
||||||
|
let winner = null
|
||||||
|
let loser = null
|
||||||
|
// If there critical success/failure, apply them first (remark : this d100 results)
|
||||||
|
roll1.rollData.rollCompare = roll1.rollData.rollResult
|
||||||
|
roll2.rollData.rollCompare = roll2.rollData.rollResult
|
||||||
|
if (roll1.rollData.resultType === "successCritical") {
|
||||||
|
roll1.rollData.rollCompare = -roll1.rollData.rollResult
|
||||||
|
}
|
||||||
|
if (roll2.rollData.resultType === "failureCritical") {
|
||||||
|
roll2.rollData.rollCompare = 100 + roll2.rollData.rollResult
|
||||||
|
}
|
||||||
|
if (roll2.rollData.resultType === "successCritical") {
|
||||||
|
roll2.rollData.rollCompare = -roll2.rollData.rollResult
|
||||||
|
}
|
||||||
|
if (roll1.rollData.resultType === "failureCritical") {
|
||||||
|
roll1.rollData.rollCompare = roll1.rollData.rollResult * 2
|
||||||
|
}
|
||||||
|
if (roll1.rollData.isSuccess && roll2.rollData.isFailure) {
|
||||||
|
winner = { actor: actor1, rollData: roll1.rollData, messageId: roll1.messageId }
|
||||||
|
loser = { actor: actor2, rollData: roll2.rollData, messageId: roll2.messageId }
|
||||||
|
}
|
||||||
|
else if (roll2.rollData.isSuccess && roll1.rollData.isFailure) {
|
||||||
|
winner = { actor: actor2, rollData: roll2.rollData, messageId: roll2.messageId }
|
||||||
|
loser = { actor: actor1, rollData: roll1.rollData, messageId: roll1.messageId }
|
||||||
|
}
|
||||||
|
else if (roll1.rollData.rollCompare < roll2.rollData.rollCompare) {
|
||||||
|
winner = { actor: actor1, rollData: roll1.rollData, messageId: roll1.messageId }
|
||||||
|
loser = { actor: actor2, rollData: roll2.rollData, messageId: roll2.messageId }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
winner = { actor: actor2, rollData: roll2.rollData, messageId: roll2.messageId }
|
||||||
|
loser = { actor: actor1, rollData: roll1.rollData, messageId: roll1.messageId }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if winner was attacking with a weapon that can apply damage
|
||||||
|
let canApplyDamage = winner && winner.rollData?.weapon && winner.rollData.weapon.system
|
||||||
|
|
||||||
|
// Prepare data for the template
|
||||||
|
let msgData = {
|
||||||
|
winner: {
|
||||||
|
actor: {
|
||||||
|
name: winner.actor.name,
|
||||||
|
img: winner.actor.img
|
||||||
|
},
|
||||||
|
rollData: {
|
||||||
|
rollResult: winner.rollData.rollResult || winner.rollData.total,
|
||||||
|
isCritical: winner.rollData.isCritical,
|
||||||
|
weapon: winner.rollData.weapon
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loser: {
|
||||||
|
actor: {
|
||||||
|
name: loser.actor.name,
|
||||||
|
img: loser.actor.img
|
||||||
|
},
|
||||||
|
rollData: {
|
||||||
|
rollResult: loser.rollData.rollResult || loser.rollData.total,
|
||||||
|
isCritical: loser.rollData.isCritical
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canApplyDamage: canApplyDamage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the template
|
||||||
|
let content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
"systems/fvtt-cthulhu-eternal/templates/chat-opposed-result.hbs",
|
||||||
|
msgData
|
||||||
|
)
|
||||||
|
|
||||||
|
// Display the result in chat
|
||||||
|
let chatMsg = await ChatMessage.create({
|
||||||
|
speaker: ChatMessage.getSpeaker({ actor: winner.actor.id }),
|
||||||
|
content: content
|
||||||
|
})
|
||||||
|
|
||||||
|
// Store the winner's roll data for damage roll if applicable
|
||||||
|
if (canApplyDamage) {
|
||||||
|
await chatMsg.setFlag("fvtt-cthulhu-eternal", "rollData", winner.rollData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static translateRangeUnit(range) {
|
static translateRangeUnit(range) {
|
||||||
if (typeof range === 'string') {
|
if (typeof range === 'string') {
|
||||||
return game.i18n.localize(`CTHULHUETERNAL.Label.${range}`)
|
return game.i18n.localize(`CTHULHUETERNAL.Label.${range}`)
|
||||||
@@ -280,31 +465,143 @@ export default class CthulhuEternalUtils {
|
|||||||
|
|
||||||
static async damageRoll(rollMessage, formula = null) {
|
static async damageRoll(rollMessage, formula = null) {
|
||||||
let rollData = rollMessage.rolls[0]?.options?.rollData
|
let rollData = rollMessage.rolls[0]?.options?.rollData
|
||||||
|
if (!rollData) {
|
||||||
|
rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
|
||||||
|
}
|
||||||
|
if (!rollData) {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noRollDataFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
let actor = game.actors.get(rollData.actorId)
|
let actor = game.actors.get(rollData.actorId)
|
||||||
if (!actor) {
|
if (!actor) {
|
||||||
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noActorFound"))
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noActorFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log("Damage roll data", rollData)
|
|
||||||
rollData.weapon.resultType = rollData.resultType // Keep the result type from the roll message
|
rollData.weapon.resultType = rollData.resultType // Keep the result type from the roll message
|
||||||
rollData.weapon.selectiveFireChoice = rollData.selectiveFireChoice // Keep the selected fire choice from the roll message
|
rollData.weapon.selectiveFireChoice = rollData.selectiveFireChoice // Keep the selected fire choice from the roll message
|
||||||
rollData.weapon.damageFormula = formula || rollData.weapon.system.damage
|
rollData.weapon.damageFormula = formula || rollData.weapon.system.damage
|
||||||
actor.system.roll("damage", rollData.weapon)
|
actor.system.roll("damage", rollData.weapon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async wornWeaponCheck(rollMessage) {
|
||||||
|
let rollData = rollMessage.rolls[0]?.options?.rollData
|
||||||
|
if (!rollData) {
|
||||||
|
rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
|
||||||
|
}
|
||||||
|
if (!rollData) {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noRollDataFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let actor = game.actors.get(rollData.actorId)
|
||||||
|
if (!actor) {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noActorFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let weapon = rollData.weapon
|
||||||
|
if (!weapon) {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noWeaponFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lance un jet de chance (50%)
|
||||||
|
let luckRoll = new Roll("1d100")
|
||||||
|
await luckRoll.evaluate()
|
||||||
|
|
||||||
|
let isSuccess = luckRoll.total <= 50
|
||||||
|
let resultMsg = ""
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
// Succès - l'arme reste worn
|
||||||
|
resultMsg = game.i18n.format("CTHULHUETERNAL.Label.wornWeaponCheckSuccess", {
|
||||||
|
weapon: weapon.name,
|
||||||
|
roll: luckRoll.total
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Échec - l'arme devient junk
|
||||||
|
resultMsg = game.i18n.format("CTHULHUETERNAL.Label.wornWeaponCheckFailure", {
|
||||||
|
weapon: weapon.name,
|
||||||
|
roll: luckRoll.total
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mettre à jour l'état de l'arme
|
||||||
|
await actor.updateEmbeddedDocuments("Item", [{
|
||||||
|
_id: weapon._id,
|
||||||
|
"system.state": "junk"
|
||||||
|
}])
|
||||||
|
|
||||||
|
ui.notifications.warn(game.i18n.format("CTHULHUETERNAL.Notifications.WeaponBecameJunk", { weapon: weapon.name }))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Créer un message de chat avec le résultat
|
||||||
|
await ChatMessage.create({
|
||||||
|
user: game.user.id,
|
||||||
|
content: `<div class="cthulhu-eternal-roll">
|
||||||
|
<h4>${game.i18n.localize("CTHULHUETERNAL.Label.wornWeaponCheckTitle")}</h4>
|
||||||
|
<p>${resultMsg}</p>
|
||||||
|
</div>`,
|
||||||
|
speaker: ChatMessage.getSpeaker({ actor: actor }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async nudgeToSuccess(rollMessage) {
|
||||||
|
if (!game.settings.get("fvtt-cthulhu-eternal", "settings-nudge")) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Settings.nudgeDisabled"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const rollOptions = rollMessage.rolls[0]?.options
|
||||||
|
if (!rollOptions) return
|
||||||
|
const actor = game.actors.get(rollOptions.actorId)
|
||||||
|
if (!actor) return
|
||||||
|
const rollTotal = rollMessage.rolls[0].total
|
||||||
|
const targetScore = rollOptions.rollData?.targetScore ?? rollOptions.targetScore
|
||||||
|
const wpCostToSucceed = Math.ceil((rollTotal - targetScore) / 5)
|
||||||
|
|
||||||
|
if (actor.system.wp.value < wpCostToSucceed) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Label.notEnoughWP"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogContext = foundry.utils.duplicate(rollOptions)
|
||||||
|
dialogContext.nudgedValue = targetScore
|
||||||
|
dialogContext.wpCost = wpCostToSucceed
|
||||||
|
dialogContext.isNudgedRoll = true
|
||||||
|
dialogContext.isNudge = false
|
||||||
|
|
||||||
|
const roll = new CthulhuEternalRoll(String(targetScore))
|
||||||
|
await roll.evaluate()
|
||||||
|
roll.options = dialogContext
|
||||||
|
roll.displayRollResult(roll, dialogContext, dialogContext.rollData)
|
||||||
|
roll.toMessage()
|
||||||
|
|
||||||
|
actor.system.modifyWP(-wpCostToSucceed)
|
||||||
|
|
||||||
|
// Original roll was a failure with skill progression — nudge succeeded, so unmark progression
|
||||||
|
if (rollOptions.skillMarkedForProgress && rollOptions.rollItem?._id) {
|
||||||
|
const skillItem = actor.items.get(rollOptions.rollItem._id)
|
||||||
|
if (skillItem) await skillItem.update({ "system.rollFailed": false })
|
||||||
|
}
|
||||||
|
|
||||||
|
await rollMessage.delete()
|
||||||
|
}
|
||||||
|
|
||||||
static async nudgeRoll(rollMessage) {
|
static async nudgeRoll(rollMessage) {
|
||||||
|
if (!game.settings.get("fvtt-cthulhu-eternal", "settings-nudge")) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Settings.nudgeDisabled"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let dialogContext = rollMessage.rolls[0]?.options
|
let dialogContext = rollMessage.rolls[0]?.options
|
||||||
let actor = game.actors.get(dialogContext.actorId)
|
let actor = game.actors.get(dialogContext.actorId)
|
||||||
|
const rollTotal = rollMessage.rolls[0].total
|
||||||
dialogContext.wpValue = actor.system.wp.value
|
dialogContext.wpValue = actor.system.wp.value
|
||||||
dialogContext.rollResultIndex = rollMessage.rolls[0].total - 1
|
// Rule: 1 WP can decrease the roll by 1–5. Only decrease is allowed.
|
||||||
dialogContext.minValue = Math.max(rollMessage.rolls[0].total - (dialogContext.wpValue * 5), 1)
|
dialogContext.minValue = Math.max(rollTotal - (dialogContext.wpValue * 5), 1)
|
||||||
dialogContext.maxValue = Math.min(rollMessage.rolls[0].total + (dialogContext.wpValue * 5), 100)
|
dialogContext.maxValue = rollTotal
|
||||||
|
dialogContext.rollResultIndex = rollTotal - dialogContext.minValue // index of current value in nudgeOptions
|
||||||
dialogContext.wpCost = 0
|
dialogContext.wpCost = 0
|
||||||
|
dialogContext.nudgedValue = rollTotal // default: no change for the select: values from minValue to maxValue (current roll)
|
||||||
// Build options table for the select operator between minValue and maxValue
|
|
||||||
dialogContext.nudgeOptions = Array.from({ length: dialogContext.maxValue - dialogContext.minValue + 1 }, (_, i) => dialogContext.minValue + i)
|
dialogContext.nudgeOptions = Array.from({ length: dialogContext.maxValue - dialogContext.minValue + 1 }, (_, i) => dialogContext.minValue + i)
|
||||||
console.log(dialogContext)
|
|
||||||
|
|
||||||
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/nudge-dialog.hbs", dialogContext)
|
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/nudge-dialog.hbs", dialogContext)
|
||||||
|
|
||||||
@@ -322,6 +619,9 @@ export default class CthulhuEternalUtils {
|
|||||||
if (input.name) obj[input.name] = input.value
|
if (input.name) obj[input.name] = input.value
|
||||||
return obj
|
return obj
|
||||||
}, {})
|
}, {})
|
||||||
|
// Compute nudgedValue from the selected index (selectOptions uses 0-based indices for arrays)
|
||||||
|
dialogContext.nudgedValue = dialogContext.minValue + Number(output.modifiedValue)
|
||||||
|
dialogContext.wpCost = Math.ceil(Math.abs(rollTotal - dialogContext.nudgedValue) / 5)
|
||||||
return output
|
return output
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -336,7 +636,7 @@ export default class CthulhuEternalUtils {
|
|||||||
rejectClose: false, // Click on Close button will not launch an error
|
rejectClose: false, // Click on Close button will not launch an error
|
||||||
render: (event, dialog) => {
|
render: (event, dialog) => {
|
||||||
$(".nudged-score-select").change(event => {
|
$(".nudged-score-select").change(event => {
|
||||||
dialogContext.nudgedValue = Number(event.target.value)+1
|
dialogContext.nudgedValue = dialogContext.minValue + Number(event.target.value)
|
||||||
dialogContext.wpCost = Math.ceil(Math.abs(rollMessage.rolls[0].total - dialogContext.nudgedValue) / 5)
|
dialogContext.wpCost = Math.ceil(Math.abs(rollMessage.rolls[0].total - dialogContext.nudgedValue) / 5)
|
||||||
$("#nudged-wp-cost").val(dialogContext.wpCost)
|
$("#nudged-wp-cost").val(dialogContext.wpCost)
|
||||||
})
|
})
|
||||||
@@ -348,6 +648,11 @@ export default class CthulhuEternalUtils {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (actor.system.wp.value < dialogContext.wpCost) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Label.notEnoughWP"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const roll = new CthulhuEternalRoll(String(dialogContext.nudgedValue))
|
const roll = new CthulhuEternalRoll(String(dialogContext.nudgedValue))
|
||||||
await roll.evaluate()
|
await roll.evaluate()
|
||||||
roll.options = dialogContext
|
roll.options = dialogContext
|
||||||
@@ -357,6 +662,17 @@ export default class CthulhuEternalUtils {
|
|||||||
roll.toMessage()
|
roll.toMessage()
|
||||||
|
|
||||||
actor.system.modifyWP(-dialogContext.wpCost)
|
actor.system.modifyWP(-dialogContext.wpCost)
|
||||||
|
|
||||||
|
// If original roll had skill progression and nudge converts it to success, unmark progression
|
||||||
|
const nudgedTargetScore = dialogContext.rollData?.targetScore ?? dialogContext.targetScore
|
||||||
|
if (dialogContext.skillMarkedForProgress && dialogContext.nudgedValue <= nudgedTargetScore && dialogContext.rollItem?._id) {
|
||||||
|
const skillItem = actor.items.get(dialogContext.rollItem._id)
|
||||||
|
if (skillItem) await skillItem.update({ "system.rollFailed": false })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the initial roll message
|
||||||
|
await rollMessage.delete()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static setupCSSRootVariables() {
|
static setupCSSRootVariables() {
|
||||||
@@ -375,4 +691,51 @@ export default class CthulhuEternalUtils {
|
|||||||
document.documentElement.style.setProperty('--background-image-base', `linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)), url("../assets/ui/${era}_background_main.webp")`);
|
document.documentElement.style.setProperty('--background-image-base', `linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)), url("../assets/ui/${era}_background_main.webp")`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getTarget() {
|
||||||
|
if (game.user.targets && game.user.targets.size === 1) {
|
||||||
|
for (let target of game.user.targets) {
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
static applyWounds(message, event) {
|
||||||
|
let woundData = message.getFlag("fvtt-cthulhu-eternal", "woundData")
|
||||||
|
if (!woundData) {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noRollDataFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let actor = game.actors.get(woundData.actorId)
|
||||||
|
if (!actor) {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.removeChatMessageId(message.id)
|
||||||
|
|
||||||
|
// Resolve the target actor: prefer canvas token (works for both combat and scene tokens)
|
||||||
|
const targetTokenId = event.currentTarget.dataset.tokenId
|
||||||
|
const targetActorId = event.currentTarget.dataset.actorId
|
||||||
|
let targetActor
|
||||||
|
if (targetTokenId) {
|
||||||
|
const token = canvas.tokens.get(targetTokenId)
|
||||||
|
targetActor = token?.actor
|
||||||
|
}
|
||||||
|
if (!targetActor && targetActorId) {
|
||||||
|
targetActor = game.actors.get(targetActorId)
|
||||||
|
}
|
||||||
|
// Legacy fallback: combatant lookup
|
||||||
|
if (!targetActor) {
|
||||||
|
const combatantId = event.currentTarget.dataset.combatantId
|
||||||
|
if (combatantId && game.combat) {
|
||||||
|
const combatant = game.combat.combatants.get(combatantId)
|
||||||
|
targetActor = combatant?.token?.actor || game.actors.get(combatant?.actorId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!targetActor) {
|
||||||
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noTargetActorFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
targetActor.applyWounds(woundData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
MANIFEST-000230
|
MANIFEST-000306
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2025/07/29-18:28:11.485462 7fbfdeffd6c0 Recovering log #227
|
2026/05/13-20:38:21.073323 7ff670fed6c0 Recovering log #304
|
||||||
2025/07/29-18:28:11.542963 7fbfdeffd6c0 Delete type=3 #225
|
2026/05/13-20:38:21.083446 7ff670fed6c0 Delete type=3 #302
|
||||||
2025/07/29-18:28:11.543020 7fbfdeffd6c0 Delete type=0 #227
|
2026/05/13-20:38:21.083514 7ff670fed6c0 Delete type=0 #304
|
||||||
2025/07/29-18:28:25.939648 7fbd3ffff6c0 Level-0 table #233: started
|
2026/05/13-21:29:49.379063 7ff6637fe6c0 Level-0 table #309: started
|
||||||
2025/07/29-18:28:25.939685 7fbd3ffff6c0 Level-0 table #233: 0 bytes OK
|
2026/05/13-21:29:49.379189 7ff6637fe6c0 Level-0 table #309: 0 bytes OK
|
||||||
2025/07/29-18:28:25.996353 7fbd3ffff6c0 Delete type=0 #231
|
2026/05/13-21:29:49.386620 7ff6637fe6c0 Delete type=0 #307
|
||||||
2025/07/29-18:28:25.996608 7fbd3ffff6c0 Manual compaction at level-0 from '!items!4oyPRBWPBWAChrJP' @ 72057594037927935 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at (end)
|
2026/05/13-21:29:49.393683 7ff6637fe6c0 Manual compaction at level-0 from '!items!4oyPRBWPBWAChrJP' @ 72057594037927935 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
2025/07/29-17:56:07.304105 7fbfddffb6c0 Delete type=3 #1
|
2026/05/13-13:05:01.465748 7ff6717ee6c0 Recovering log #300
|
||||||
2025/07/29-18:20:36.901079 7fbd3ffff6c0 Level-0 table #228: started
|
2026/05/13-13:05:01.518261 7ff6717ee6c0 Delete type=3 #298
|
||||||
2025/07/29-18:20:36.901119 7fbd3ffff6c0 Level-0 table #228: 0 bytes OK
|
2026/05/13-13:05:01.518318 7ff6717ee6c0 Delete type=0 #300
|
||||||
2025/07/29-18:20:36.907068 7fbd3ffff6c0 Delete type=0 #226
|
2026/05/13-13:57:22.815510 7ff6637fe6c0 Level-0 table #305: started
|
||||||
2025/07/29-18:20:36.913035 7fbd3ffff6c0 Manual compaction at level-0 from '!items!4oyPRBWPBWAChrJP' @ 72057594037927935 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at '!items!zVFfp3o0G0Zg3Ia4' @ 52 : 1
|
2026/05/13-13:57:22.815533 7ff6637fe6c0 Level-0 table #305: 0 bytes OK
|
||||||
2025/07/29-18:20:36.913051 7fbd3ffff6c0 Compacting 1@0 + 0@1 files
|
2026/05/13-13:57:22.821566 7ff6637fe6c0 Delete type=0 #303
|
||||||
2025/07/29-18:20:36.917150 7fbd3ffff6c0 Generated table #229@0: 26 keys, 60964 bytes
|
2026/05/13-13:57:22.821711 7ff6637fe6c0 Manual compaction at level-0 from '!items!4oyPRBWPBWAChrJP' @ 72057594037927935 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at (end)
|
||||||
2025/07/29-18:20:36.917172 7fbd3ffff6c0 Compacted 1@0 + 0@1 files => 60964 bytes
|
|
||||||
2025/07/29-18:20:36.923988 7fbd3ffff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
|
||||||
2025/07/29-18:20:36.924086 7fbd3ffff6c0 Delete type=2 #60
|
|
||||||
2025/07/29-18:20:36.941818 7fbd3ffff6c0 Manual compaction at level-0 from '!items!zVFfp3o0G0Zg3Ia4' @ 52 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at (end)
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000399
|
MANIFEST-000475
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2025/07/29-18:28:11.326583 7fbfdd7fa6c0 Recovering log #396
|
2026/05/13-20:38:21.045516 7ff6717ee6c0 Recovering log #473
|
||||||
2025/07/29-18:28:11.422544 7fbfdd7fa6c0 Delete type=3 #394
|
2026/05/13-20:38:21.056349 7ff6717ee6c0 Delete type=3 #471
|
||||||
2025/07/29-18:28:11.422600 7fbfdd7fa6c0 Delete type=0 #396
|
2026/05/13-20:38:21.056423 7ff6717ee6c0 Delete type=0 #473
|
||||||
2025/07/29-18:28:25.859630 7fbd3ffff6c0 Level-0 table #402: started
|
2026/05/13-21:29:49.443482 7ff6637fe6c0 Level-0 table #478: started
|
||||||
2025/07/29-18:28:25.859675 7fbd3ffff6c0 Level-0 table #402: 0 bytes OK
|
2026/05/13-21:29:49.443606 7ff6637fe6c0 Level-0 table #478: 0 bytes OK
|
||||||
2025/07/29-18:28:25.902834 7fbd3ffff6c0 Delete type=0 #400
|
2026/05/13-21:29:49.450275 7ff6637fe6c0 Delete type=0 #476
|
||||||
2025/07/29-18:28:25.996583 7fbd3ffff6c0 Manual compaction at level-0 from '!folders!5PrT9QmN1cFPzDFP' @ 72057594037927935 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at (end)
|
2026/05/13-21:29:49.450420 7ff6637fe6c0 Manual compaction at level-0 from '!folders!5PrT9QmN1cFPzDFP' @ 72057594037927935 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
2025/07/29-17:56:07.265693 7fbfddffb6c0 Delete type=3 #1
|
2026/05/13-13:05:01.363329 7ff663fff6c0 Recovering log #469
|
||||||
2025/07/29-18:20:36.941849 7fbd3ffff6c0 Level-0 table #397: started
|
2026/05/13-13:05:01.408343 7ff663fff6c0 Delete type=3 #467
|
||||||
2025/07/29-18:20:36.941895 7fbd3ffff6c0 Level-0 table #397: 0 bytes OK
|
2026/05/13-13:05:01.408405 7ff663fff6c0 Delete type=0 #469
|
||||||
2025/07/29-18:20:36.948373 7fbd3ffff6c0 Delete type=0 #395
|
2026/05/13-13:57:22.802421 7ff6637fe6c0 Level-0 table #474: started
|
||||||
2025/07/29-18:20:36.960564 7fbd3ffff6c0 Manual compaction at level-0 from '!folders!5PrT9QmN1cFPzDFP' @ 72057594037927935 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at '!items!zvoUByzWSWZ87fxA' @ 1281 : 1
|
2026/05/13-13:57:22.802455 7ff6637fe6c0 Level-0 table #474: 0 bytes OK
|
||||||
2025/07/29-18:20:36.960577 7fbd3ffff6c0 Compacting 1@0 + 0@1 files
|
2026/05/13-13:57:22.809135 7ff6637fe6c0 Delete type=0 #472
|
||||||
2025/07/29-18:20:36.968003 7fbd3ffff6c0 Generated table #398@0: 556 keys, 320457 bytes
|
2026/05/13-13:57:22.821691 7ff6637fe6c0 Manual compaction at level-0 from '!folders!5PrT9QmN1cFPzDFP' @ 72057594037927935 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at (end)
|
||||||
2025/07/29-18:20:36.968044 7fbd3ffff6c0 Compacted 1@0 + 0@1 files => 320457 bytes
|
|
||||||
2025/07/29-18:20:36.974076 7fbd3ffff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
|
||||||
2025/07/29-18:20:36.974209 7fbd3ffff6c0 Delete type=2 #277
|
|
||||||
2025/07/29-18:20:36.980862 7fbd3ffff6c0 Manual compaction at level-0 from '!items!zvoUByzWSWZ87fxA' @ 1281 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at (end)
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000045
|
MANIFEST-000121
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2025/07/29-18:28:11.426069 7fbfde7fc6c0 Recovering log #42
|
2026/05/13-20:38:21.061357 7ff663fff6c0 Recovering log #119
|
||||||
2025/07/29-18:28:11.482529 7fbfde7fc6c0 Delete type=3 #40
|
2026/05/13-20:38:21.070703 7ff663fff6c0 Delete type=3 #117
|
||||||
2025/07/29-18:28:11.482582 7fbfde7fc6c0 Delete type=0 #42
|
2026/05/13-20:38:21.070750 7ff663fff6c0 Delete type=0 #119
|
||||||
2025/07/29-18:28:25.996691 7fbd3ffff6c0 Level-0 table #48: started
|
2026/05/13-21:29:49.386758 7ff6637fe6c0 Level-0 table #124: started
|
||||||
2025/07/29-18:28:25.996717 7fbd3ffff6c0 Level-0 table #48: 0 bytes OK
|
2026/05/13-21:29:49.386971 7ff6637fe6c0 Level-0 table #124: 0 bytes OK
|
||||||
2025/07/29-18:28:26.032508 7fbd3ffff6c0 Delete type=0 #46
|
2026/05/13-21:29:49.393349 7ff6637fe6c0 Delete type=0 #122
|
||||||
2025/07/29-18:28:26.140428 7fbd3ffff6c0 Manual compaction at level-0 from '!folders!0DI3T2jve3nsmsfZ' @ 72057594037927935 : 1 .. '!items!zyxA9DhO36t5OBDv' @ 0 : 0; will stop at (end)
|
2026/05/13-21:29:49.393700 7ff6637fe6c0 Manual compaction at level-0 from '!folders!0DI3T2jve3nsmsfZ' @ 72057594037927935 : 1 .. '!items!zyxA9DhO36t5OBDv' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
2025/07/29-17:56:07.286370 7fbfdeffd6c0 Delete type=3 #1
|
2026/05/13-13:05:01.411955 7ff671fef6c0 Recovering log #115
|
||||||
2025/07/29-18:20:36.907148 7fbd3ffff6c0 Level-0 table #43: started
|
2026/05/13-13:05:01.463033 7ff671fef6c0 Delete type=3 #113
|
||||||
2025/07/29-18:20:36.907174 7fbd3ffff6c0 Level-0 table #43: 0 bytes OK
|
2026/05/13-13:05:01.463084 7ff671fef6c0 Delete type=0 #115
|
||||||
2025/07/29-18:20:36.912933 7fbd3ffff6c0 Delete type=0 #41
|
2026/05/13-13:57:22.809226 7ff6637fe6c0 Level-0 table #120: started
|
||||||
2025/07/29-18:20:36.924240 7fbd3ffff6c0 Manual compaction at level-0 from '!folders!0DI3T2jve3nsmsfZ' @ 72057594037927935 : 1 .. '!items!zyxA9DhO36t5OBDv' @ 0 : 0; will stop at '!items!zyxA9DhO36t5OBDv' @ 55 : 1
|
2026/05/13-13:57:22.809250 7ff6637fe6c0 Level-0 table #120: 0 bytes OK
|
||||||
2025/07/29-18:20:36.924252 7fbd3ffff6c0 Compacting 1@0 + 0@1 files
|
2026/05/13-13:57:22.815441 7ff6637fe6c0 Delete type=0 #118
|
||||||
2025/07/29-18:20:36.928859 7fbd3ffff6c0 Generated table #44@0: 362 keys, 93592 bytes
|
2026/05/13-13:57:22.821701 7ff6637fe6c0 Manual compaction at level-0 from '!folders!0DI3T2jve3nsmsfZ' @ 72057594037927935 : 1 .. '!items!zyxA9DhO36t5OBDv' @ 0 : 0; will stop at (end)
|
||||||
2025/07/29-18:20:36.928870 7fbd3ffff6c0 Compacted 1@0 + 0@1 files => 93592 bytes
|
|
||||||
2025/07/29-18:20:36.935549 7fbd3ffff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
|
||||||
2025/07/29-18:20:36.935675 7fbd3ffff6c0 Delete type=2 #35
|
|
||||||
2025/07/29-18:20:36.941835 7fbd3ffff6c0 Manual compaction at level-0 from '!items!zyxA9DhO36t5OBDv' @ 55 : 1 .. '!items!zyxA9DhO36t5OBDv' @ 0 : 0; will stop at (end)
|
|
||||||
|
|||||||
Binary file not shown.
+27
-4
@@ -111,8 +111,8 @@ i.fvtt-cthulhu-eternal {
|
|||||||
padding: 2px 2px;
|
padding: 2px 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
min-width: 6.0rem;
|
min-width: 6rem;
|
||||||
max-width: 6.0rem;
|
max-width: 6rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.san-loose-buttons {
|
.san-loose-buttons {
|
||||||
@@ -128,8 +128,8 @@ i.fvtt-cthulhu-eternal {
|
|||||||
padding: 2px 2px;
|
padding: 2px 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
min-width: 3.0rem;
|
min-width: 3rem;
|
||||||
max-width: 3.0rem;
|
max-width: 3rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.result-non-lethal {
|
.result-non-lethal {
|
||||||
@@ -141,5 +141,28 @@ i.fvtt-cthulhu-eternal {
|
|||||||
font-family: var(--font-primary);
|
font-family: var(--font-primary);
|
||||||
font-size: calc(var(--font-size-standard) * 1.02);
|
font-size: calc(var(--font-size-standard) * 1.02);
|
||||||
}
|
}
|
||||||
|
.combatants-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
gap: 4px;
|
||||||
|
margin: 8px 0;
|
||||||
|
button.apply-wounds-btn {
|
||||||
|
font-family: var(--font-primary);
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.7);
|
||||||
|
border: 1px solid #4b4a44;
|
||||||
|
padding: 4px 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
background-color: #f0f0e0;
|
||||||
|
color: #1c1c1c;
|
||||||
|
&:hover {
|
||||||
|
background-color: #d5d5c5;
|
||||||
|
border-color: #2d2d2a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+228
-16
@@ -53,6 +53,10 @@
|
|||||||
color: var(--color-dark-1);
|
color: var(--color-dark-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.li-apply-wounds {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
&.dice-roll {
|
&.dice-roll {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
@@ -91,22 +95,7 @@
|
|||||||
li {
|
li {
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
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);
|
||||||
}
|
|
||||||
.nudge-roll {
|
|
||||||
font-size: calc(var(--font-size-standard) * 1.0);
|
|
||||||
margin-left: 2rem;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.healing-roll {
|
|
||||||
font-size: calc(var(--font-size-standard) * 1.0);
|
|
||||||
margin-left: 2rem;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.roll-damage {
|
|
||||||
font-size: calc(var(--font-size-standard) * 1.0);
|
|
||||||
margin-left: 2rem;
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
.result-success {
|
.result-success {
|
||||||
color: var(--color-success);
|
color: var(--color-success);
|
||||||
@@ -145,4 +134,227 @@
|
|||||||
font-size: calc(var(--font-size-standard) * 1.2);
|
font-size: calc(var(--font-size-standard) * 1.2);
|
||||||
text-shadow: 0 0 10px var(--color-shadow-primary);
|
text-shadow: 0 0 10px var(--color-shadow-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.375rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
border-top: 1px solid var(--color-border-light-primary);
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 0 0 5px 5px;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.chat-action-button {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
background: var(--color-dark-6);
|
||||||
|
color: var(--color-light-1);
|
||||||
|
border: 1px solid var(--color-border-light-primary);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-dark-5);
|
||||||
|
border-color: var(--color-border-dark);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 1rem !important;
|
||||||
|
line-height: 1;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nudge-roll,
|
||||||
|
.damage-roll,
|
||||||
|
.healing-roll,
|
||||||
|
.opposed-roll {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opposed-roll-result {
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 5px;
|
||||||
|
font-family: var(--font-primary);
|
||||||
|
|
||||||
|
.opposed-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
padding-bottom: 0.2rem;
|
||||||
|
border-bottom: 1px solid var(--color-border-light-primary);
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--font-title);
|
||||||
|
font-size: calc(var(--font-size-standard) * 1);
|
||||||
|
color: var(--color-dark-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opposed-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.15rem;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opposed-winner,
|
||||||
|
.opposed-loser {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.3rem 0.4rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opposed-winner {
|
||||||
|
background: rgba(34, 139, 34, 0.1);
|
||||||
|
border: 2px solid var(--color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.opposed-loser {
|
||||||
|
background: rgba(220, 20, 60, 0.1);
|
||||||
|
border: 2px solid var(--color-failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid var(--color-border-light-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.15rem;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.75);
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-dark-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.winner-name {
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.95);
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loser-name {
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.95);
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-failure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.roll-result {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.3rem;
|
||||||
|
|
||||||
|
.roll-value {
|
||||||
|
font-size: calc(var(--font-size-standard) * 1.2);
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: var(--font-title);
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-badge {
|
||||||
|
padding: 0.15rem 0.3rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.7);
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background: var(--color-critical-success);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.winner-result .roll-value {
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loser-result .roll-value {
|
||||||
|
color: var(--color-failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
.versus-separator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.3rem;
|
||||||
|
padding: 0;
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.85);
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-dark-2);
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: calc(var(--font-size-standard) * 0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.375rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
border-top: 1px solid var(--color-border-light-primary);
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 0 0 5px 5px;
|
||||||
|
|
||||||
|
.chat-action-button {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
background: var(--color-dark-6);
|
||||||
|
color: var(--color-light-1);
|
||||||
|
border: 1px solid var(--color-border-light-primary);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-dark-5);
|
||||||
|
border-color: var(--color-border-dark);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 1rem !important;
|
||||||
|
line-height: 1;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
"download": "#{DOWNLOAD}#",
|
"download": "#{DOWNLOAD}#",
|
||||||
"url": "https://www.uberwald.me/gitea/public/fvtt-cthulhu-eternal",
|
"url": "https://www.uberwald.me/gitea/public/fvtt-cthulhu-eternal",
|
||||||
"license": "LICENSE",
|
"license": "LICENSE",
|
||||||
"version": "13.0.1",
|
"version": "13.0.2",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Uberwald",
|
"name": "Uberwald",
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
},
|
},
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "13",
|
"minimum": "13",
|
||||||
"verified": "13"
|
"verified": "14"
|
||||||
},
|
},
|
||||||
"esmodules": [
|
"esmodules": [
|
||||||
"cthulhu-eternal.mjs"
|
"cthulhu-eternal.mjs"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
{{formField systemFields.settings value=system.settings localize=true}}
|
{{formField systemFields.settings value=system.settings localize=true}}
|
||||||
{{formField systemFields.protection value=system.protection}}
|
{{formField systemFields.protection value=system.protection}}
|
||||||
{{formField systemFields.resourceLevel value=system.resourceLevel}}
|
{{formField systemFields.resourceLevel value=system.resourceLevel}}
|
||||||
|
{{formField systemFields.equipped value=system.equipped}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,30 @@
|
|||||||
<li>{{localize "CTHULHUETERNAL.Label.ammoUsed"}}: {{ammoUsed}} / {{weapon.system.ammo.value}}</li>
|
<li>{{localize "CTHULHUETERNAL.Label.ammoUsed"}}: {{ammoUsed}} / {{weapon.system.ammo.value}}</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
<li class="li-apply-wounds">
|
||||||
|
<div>{{localize "CTHULHUETERNAL.Label.applyWounds"}}</div>
|
||||||
|
<div class="combatants-grid">
|
||||||
|
{{#each combatants}}
|
||||||
|
<button
|
||||||
|
class="apply-wounds-btn chat-action-button"
|
||||||
|
data-combatant-id="{{this.id}}"
|
||||||
|
data-actor-id="{{this.actorId}}"
|
||||||
|
data-token-id="{{this.tokenId}}"
|
||||||
|
title="{{this.name}}"
|
||||||
|
>
|
||||||
|
{{this.name}}
|
||||||
|
</button>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
{{#if isLethal}}
|
{{#if isLethal}}
|
||||||
<li class="result-lethal">{{localize "CTHULHUETERNAL.Label.lethalityLethal"}}</li>
|
<li class="result-lethal">{{localize "CTHULHUETERNAL.Label.lethalityLethal"}}</li>
|
||||||
<li class="result-lethal">{{localize "CTHULHUETERNAL.Label.lethalityWounded"}}</li>
|
<li class="result-lethal">{{localize "CTHULHUETERNAL.Label.lethalityWounded"}}</li>
|
||||||
{{else}}
|
{{else}}
|
||||||
<li class="result-non-lethal">{{localize "CTHULHUETERNAL.Label.lethalityNotLethal"}}</li>
|
<li class="result-non-lethal">{{localize "CTHULHUETERNAL.Label.lethalityNotLethal"}}</li>
|
||||||
<li class="result-non-lethal">{{localize "CTHULHUETERNAL.Label.lethalityNotWounded"}}: <strong>{{wounds}}</strong></li>
|
<li class="result-non-lethal">{{localize "CTHULHUETERNAL.Label.lethalityNotWounded"}}: <strong>{{wounds}}</strong>
|
||||||
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
+184
-53
@@ -6,20 +6,28 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="intro-right">
|
<div class="intro-right">
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><strong>{{actingCharName}}</strong></li>
|
||||||
{{#if (eq rollType "char")}}
|
{{#if (eq rollType "char")}}
|
||||||
<li><strong>{{localize "CTHULHUETERNAL.Label.charRoll"}}</strong></li>
|
<li><strong>{{localize "CTHULHUETERNAL.Label.charRoll"}}</strong></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if (eq rollType "skill")}}
|
{{#if (eq rollType "skill")}}
|
||||||
<li><strong>{{localize "CTHULHUETERNAL.Label.skillRoll"}}</strong></li>
|
<li><strong>{{localize
|
||||||
|
"CTHULHUETERNAL.Label.skillRoll"
|
||||||
|
}}</strong></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isNudgedRoll}}
|
{{#if isNudgedRoll}}
|
||||||
<li><strong>{{localize "CTHULHUETERNAL.Label.nudgedRoll"}} : {{wpCost}} {{localize "CTHULHUETERNAL.Label.WPSpent"}}</strong></li>
|
<li><strong>{{localize "CTHULHUETERNAL.Label.nudgedRoll"}}
|
||||||
|
:
|
||||||
|
{{wpCost}}
|
||||||
|
{{localize "CTHULHUETERNAL.Label.WPSpent"}}</strong></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if weapon}}
|
{{#if weapon}}
|
||||||
<li><strong>{{localize "CTHULHUETERNAL.Label.Weapon"}} : {{weapon.name}}</strong></li>
|
<li><strong>{{localize "CTHULHUETERNAL.Label.Weapon"}}
|
||||||
|
:
|
||||||
|
{{weapon.name}}</strong></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if (eq rollType "resource")}}
|
{{#if (eq rollType "resource")}}
|
||||||
@@ -29,95 +37,108 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isZeroWP}}
|
{{#if isZeroWP}}
|
||||||
<li class="red-warning">{{localize "CTHULHUETERNAL.Label.ZeroWP"}}</li>
|
<li class="red-warning">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.ZeroWP"
|
||||||
|
}}</li>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if isLowWP}}
|
{{#if isLowWP}}
|
||||||
<li class="orange-warning">{{localize "CTHULHUETERNAL.Label.LowWP"}} : -20%</li>
|
<li class="orange-warning">{{localize "CTHULHUETERNAL.Label.LowWP"}}
|
||||||
|
: -20%</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isExhausted}}
|
{{#if isExhausted}}
|
||||||
<li class="orange-warning">{{localize "CTHULHUETERNAL.Label.Exhausted"}} : -20%</li>
|
<li class="orange-warning">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.Exhausted"
|
||||||
|
}}
|
||||||
|
: -20%</li>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isWorn}}
|
||||||
|
<li class="orange-warning"><i
|
||||||
|
class="fa-solid fa-triangle-exclamation"
|
||||||
|
></i>
|
||||||
|
{{localize "CTHULHUETERNAL.Label.wornWeaponWarning"}}</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if (eq rollType "resource")}}
|
{{#if (eq rollType "resource")}}
|
||||||
<li>{{localize "CTHULHUETERNAL.Label.multiplier"}} : {{multiplier}}</li>
|
<li>{{localize "CTHULHUETERNAL.Label.multiplier"}}
|
||||||
|
:
|
||||||
|
{{multiplier}}</li>
|
||||||
{{else}}
|
{{else}}
|
||||||
<li>{{localize "CTHULHUETERNAL.Label.modifier"}} : {{totalModifier}}%</li>
|
<li>{{localize "CTHULHUETERNAL.Label.modifier"}}
|
||||||
|
:
|
||||||
|
{{totalModifier}}%</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<li>{{localize "CTHULHUETERNAL.Label.targetScore"}} : {{targetScore}}%</li>
|
<li>{{localize "CTHULHUETERNAL.Label.targetScore"}}
|
||||||
|
:
|
||||||
|
{{targetScore}}%</li>
|
||||||
|
|
||||||
{{#if isSuccess}}
|
{{#if isSuccess}}
|
||||||
{{#if isCritical}}
|
{{#if isCritical}}
|
||||||
<li class="result-critical-success">{{localize "CTHULHUETERNAL.Label.criticalSuccess"}}
|
<li class="result-critical-success">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.criticalSuccess"
|
||||||
|
}}
|
||||||
</li>
|
</li>
|
||||||
{{else}}
|
{{else}}
|
||||||
<li class="result-success">
|
<li class="result-success">
|
||||||
{{localize "CTHULHUETERNAL.Label.success"}}
|
{{localize "CTHULHUETERNAL.Label.success"}}
|
||||||
{{#if isNudge}}
|
|
||||||
<a class="nudge-roll" data-tooltip="{{localize "CTHULHUETERNAL.Label.rollNudge"}}"><i class="fa-solid fa-circle-sort-down"></i></a>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq rollType "weapon")}}
|
|
||||||
<li>
|
|
||||||
{{#if (eq weapon.system.weaponType "rangedfirearm")}}
|
|
||||||
{{#if weapon.system.hasDamageDistance}}
|
|
||||||
{{#each weapon.system.damageDistance as |damageDistance|}}
|
|
||||||
{{#if (gt damageDistance.distance 0)}}
|
|
||||||
<a class="damage-roll" data-item-id="{{weapon.id}}" data-action="roll" data-roll-type="damage"
|
|
||||||
data-roll-value="{{damageDistance.damage}}" >
|
|
||||||
<i class="fa-solid fa-gun"></i>
|
|
||||||
<span class="damage-distance">{{damageDistance.distance}}:{{damageDistance.damage}} </span>
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
{{/each}}
|
|
||||||
{{else}}
|
|
||||||
<a class="damage-roll" data-roll-value="{{weapon.system.damage}}" data-tooltip="{{localize "CTHULHUETERNAL.Label.rollDamage"}}"><i class="fa-solid fa-gun"></i></a>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
<a class="damage-roll" data-roll-value="{{weapon.system.damage}}" data-tooltip="{{localize "CTHULHUETERNAL.Label.rollDamage"}}"><i class="fa-solid fa-sword"></i></a>
|
|
||||||
{{/if}}
|
|
||||||
</li>
|
|
||||||
{{/if}}
|
|
||||||
{{#if (eq rollType "skill") }}
|
|
||||||
{{#if rollItem.system.isHealing}}
|
|
||||||
<li>
|
|
||||||
<a class="healing-roll" data-tooltip="{{localize "CTHULHUETERNAL.Label.rollHealing"}}"><i class="fa-solid fa-heart"></i></a>
|
|
||||||
</li>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</li>
|
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isFailure}}
|
{{#if isFailure}}
|
||||||
{{#if isCritical}}
|
{{#if isCritical}}
|
||||||
<li class="result-critical-failure">{{localize "CTHULHUETERNAL.Label.criticalFailure"}}
|
<li class="result-critical-failure">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.criticalFailure"
|
||||||
|
}}
|
||||||
</li>
|
</li>
|
||||||
{{else}}
|
{{else}}
|
||||||
<li class="result-failure">
|
<li class="result-failure">
|
||||||
{{localize "CTHULHUETERNAL.Label.failure"}}
|
{{localize "CTHULHUETERNAL.Label.failure"}}
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isNudge}}
|
|
||||||
<li>
|
|
||||||
<a class="nudge-roll" data-tooltip="{{localize "CTHULHUETERNAL.Label.rollNudge"}}"><i class="fa-solid fa-circle-sort-down"></i></a>
|
|
||||||
</li>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
<!-- {{#if isAttackRoll}}
|
||||||
|
{{#if defenseRoll}}
|
||||||
|
<li>{{localize "CTHULHUETERNAL.Label.defenseRoll"}}</li>
|
||||||
|
{{#if attackSuccess}}
|
||||||
|
<li class="result-success">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.attackSuccess"
|
||||||
|
}}</li>
|
||||||
|
{{else}}
|
||||||
|
<li class="result-failure">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.attackFailure"
|
||||||
|
}}</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{#if targetId}}
|
||||||
|
<li class="orange-warning">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.noDefenseRoll"
|
||||||
|
}}</li>
|
||||||
|
{{else}}
|
||||||
|
<li class="orange-warning">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.noTarget"
|
||||||
|
}}</li>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}} -->
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if isDamage}}
|
{{#if isDamage}}
|
||||||
<div>
|
<div>
|
||||||
{{#if (and isGM hasTarget)}}
|
{{#if (and isGM hasTarget)}}
|
||||||
{{{localize "CTHULHUETERNAL.Roll.displayArmor" targetName=targetName targetArmor=targetArmor
|
{{{localize
|
||||||
realDamage=realDamage}}}
|
"CTHULHUETERNAL.Roll.displayArmor"
|
||||||
|
targetName=targetName
|
||||||
|
targetArmor=targetArmor
|
||||||
|
realDamage=realDamage
|
||||||
|
}}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -128,4 +149,114 @@
|
|||||||
{{{tooltip}}}
|
{{{tooltip}}}
|
||||||
</div>
|
</div>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#if skillMarkedForProgress}}
|
||||||
|
<p class="skill-progress-notice">
|
||||||
|
<i class="fa-solid fa-arrow-up-right-dots"></i>
|
||||||
|
{{rollItem.name}} — {{localize "CTHULHUETERNAL.Label.skillFailed"}}
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{! Zone d'actions regroupées }}
|
||||||
|
<div class="chat-actions">
|
||||||
|
{{#if isSuccess}}
|
||||||
|
{{#if isNudge}}
|
||||||
|
<a
|
||||||
|
class="nudge-roll chat-action-button"
|
||||||
|
data-tooltip="{{localize 'CTHULHUETERNAL.Label.rollNudge'}}"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-circle-sort-down"></i>
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (eq rollType "weapon")}}
|
||||||
|
{{#if (eq weapon.system.weaponType "rangedfirearm")}}
|
||||||
|
{{#if weapon.system.hasDamageDistance}}
|
||||||
|
{{#each weapon.system.damageDistance as |damageDistance|}}
|
||||||
|
{{#if (gt damageDistance.distance 0)}}
|
||||||
|
<a
|
||||||
|
class="damage-roll chat-action-button"
|
||||||
|
data-item-id="{{weapon.id}}"
|
||||||
|
data-action="roll"
|
||||||
|
data-roll-type="damage"
|
||||||
|
data-roll-value="{{damageDistance.damage}}"
|
||||||
|
data-tooltip="{{localize
|
||||||
|
'CTHULHUETERNAL.Label.rollDamage'
|
||||||
|
}} ({{damageDistance.distance}}m : {{damageDistance.damage}})"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-gun"></i>
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
{{else}}
|
||||||
|
<a
|
||||||
|
class="damage-roll chat-action-button"
|
||||||
|
data-roll-value="{{weapon.system.damage}}"
|
||||||
|
data-tooltip="{{localize 'CTHULHUETERNAL.Label.rollDamage'}}"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-gun"></i>
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
<a
|
||||||
|
class="damage-roll chat-action-button"
|
||||||
|
data-roll-value="{{weapon.system.damage}}"
|
||||||
|
data-tooltip="{{localize 'CTHULHUETERNAL.Label.rollDamage'}}"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-sword"></i>
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (eq rollType "skill")}}
|
||||||
|
{{#if rollItem.system.isHealing}}
|
||||||
|
<a
|
||||||
|
class="healing-roll chat-action-button"
|
||||||
|
data-tooltip="{{localize 'CTHULHUETERNAL.Label.rollHealing'}}"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-heart"></i>
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (eq rollType "weapon")}}
|
||||||
|
{{#if isWorn}}
|
||||||
|
<a
|
||||||
|
class="worn-weapon-check chat-action-button"
|
||||||
|
data-weapon-id="{{weapon.id}}"
|
||||||
|
data-tooltip="{{localize 'CTHULHUETERNAL.Label.wornWeaponCheck'}}"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-dice"></i>
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isFailure}}
|
||||||
|
{{#if isNudge}}
|
||||||
|
<a
|
||||||
|
class="nudge-roll chat-action-button"
|
||||||
|
data-tooltip="{{localize 'CTHULHUETERNAL.Label.rollNudge'}}"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-circle-sort-down"></i>
|
||||||
|
</a>
|
||||||
|
{{#if wpCostToSucceed}}
|
||||||
|
<a
|
||||||
|
class="nudge-to-success chat-action-button"
|
||||||
|
data-tooltip="{{localize 'CTHULHUETERNAL.Roll.nudgeToSuccess' score=targetScore wp=wpCostToSucceed}}"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-circle-check"></i>
|
||||||
|
{{localize "CTHULHUETERNAL.Roll.nudgeToSuccess" score=targetScore wp=wpCostToSucceed}}
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="opposed-roll chat-action-button"
|
||||||
|
data-tooltip="{{localize 'CTHULHUETERNAL.Label.opposedRoll'}}"
|
||||||
|
>
|
||||||
|
<i class="fa-duotone fa-light fa-arrows-to-line"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
{{! Template for opposed roll result }}
|
||||||
|
<div class="cthulhu-eternal-roll opposed-roll-result">
|
||||||
|
<div class="opposed-header">
|
||||||
|
<h3>{{localize "CTHULHUETERNAL.Label.opposedRollResult"}}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="opposed-content">
|
||||||
|
<div class="opposed-winner">
|
||||||
|
<div class="character-info">
|
||||||
|
<img src="{{winner.actor.img}}" alt="{{winner.actor.name}}" />
|
||||||
|
<div class="character-name">
|
||||||
|
<strong>{{localize "CTHULHUETERNAL.Label.opposedRollWinner"}}</strong>
|
||||||
|
<span class="winner-name">{{winner.actor.name}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="roll-result winner-result">
|
||||||
|
<span class="roll-value">{{winner.rollData.rollResult}}</span>
|
||||||
|
{{#if winner.rollData.isCritical}}
|
||||||
|
<span class="critical-badge">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.critical"
|
||||||
|
}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="versus-separator">
|
||||||
|
<i class="fas fa-swords"></i>
|
||||||
|
<span>{{localize "CTHULHUETERNAL.Label.defeats"}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="opposed-loser">
|
||||||
|
<div class="character-info">
|
||||||
|
<img src="{{loser.actor.img}}" alt="{{loser.actor.name}}" />
|
||||||
|
<div class="character-name">
|
||||||
|
<span class="loser-name">{{loser.actor.name}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="roll-result loser-result">
|
||||||
|
<span class="roll-value">{{loser.rollData.rollResult}}</span>
|
||||||
|
{{#if loser.rollData.isCritical}}
|
||||||
|
<span class="critical-badge">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.critical"
|
||||||
|
}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if canApplyDamage}}
|
||||||
|
<div class="chat-actions">
|
||||||
|
<a
|
||||||
|
class="damage-roll chat-action-button"
|
||||||
|
data-roll-value="{{winner.rollData.weapon.system.damage}}"
|
||||||
|
data-tooltip="{{localize 'CTHULHUETERNAL.Label.rollDamage'}}"
|
||||||
|
>
|
||||||
|
{{#if (eq winner.rollData.weapon.system.weaponType "rangedfirearm")}}
|
||||||
|
<i class="fa-solid fa-gun"></i>
|
||||||
|
{{else}}
|
||||||
|
<i class="fa-solid fa-sword"></i>
|
||||||
|
{{/if}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
@@ -1,22 +1,61 @@
|
|||||||
<div class="{{cssClass}}">
|
<div class="{{cssClass}}">
|
||||||
<div class="chat-lethal-damage">
|
<div class="chat-lethal-damage">
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>{{weapon.name}} : {{localize "CTHULHUETERNAL.Label.damageRoll"}}</strong></li>
|
<li><strong>{{weapon.name}}
|
||||||
<li>{{localize "CTHULHUETERNAL.Label.result"}} :{{rollResult}} ({{formula}})</li>
|
:
|
||||||
|
{{localize "CTHULHUETERNAL.Label.damageRoll"}}</strong></li>
|
||||||
|
<li>{{localize "CTHULHUETERNAL.Label.result"}}
|
||||||
|
:{{rollResult}}
|
||||||
|
({{formula}})</li>
|
||||||
|
|
||||||
{{#if (gt weapon.system.killRadius 0)}}
|
{{#if (gt weapon.system.killRadius 0)}}
|
||||||
<li>{{localize "CTHULHUETERNAL.Label.killRadius"}} : {{weapon.system.killRadius}} {{weapon.system.rangeUnit}}</li>
|
<li>{{localize "CTHULHUETERNAL.Label.killRadius"}}
|
||||||
|
:
|
||||||
|
{{weapon.system.killRadius}}
|
||||||
|
{{weapon.system.rangeUnit}}</li>
|
||||||
<li>{{localize "CTHULHUETERNAL.Label.killRadiusInfo"}}</li>
|
<li>{{localize "CTHULHUETERNAL.Label.killRadiusInfo"}}</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if (gt weapon.system.armorPiercing 0)}}
|
{{#if (gt weapon.system.armorPiercing 0)}}
|
||||||
<li>{{localize "CTHULHUETERNAL.Label.armorPiercing"}} : {{weapon.system.armorPiercing}}</li>
|
<li>{{localize "CTHULHUETERNAL.Label.armorPiercing"}}
|
||||||
|
:
|
||||||
|
{{weapon.system.armorPiercing}}</li>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (gt weapon.system.penetration 0)}}
|
||||||
|
<li>{{localize "CTHULHUETERNAL.Label.penetration"}}
|
||||||
|
:
|
||||||
|
{{weapon.system.penetration}}</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if ammoUsed}}
|
{{#if ammoUsed}}
|
||||||
<li>{{localize "CTHULHUETERNAL.Label.ammoUsed"}}: {{ammoUsed}} / {{weapon.system.ammo.value}}</li>
|
<li>{{localize "CTHULHUETERNAL.Label.ammoUsed"}}:
|
||||||
|
{{ammoUsed}}
|
||||||
|
/
|
||||||
|
{{weapon.system.ammo.value}}</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li class="result-non-lethal">{{localize "CTHULHUETERNAL.Label.damageMessage"}}: <strong>{{rollResult}}</strong></li>
|
|
||||||
|
<li class="li-apply-wounds">
|
||||||
|
<div>{{localize "CTHULHUETERNAL.Label.applyWounds"}}</div>
|
||||||
|
<div class="combatants-grid">
|
||||||
|
{{#each combatants}}
|
||||||
|
<button
|
||||||
|
class="apply-wounds-btn chat-action-button"
|
||||||
|
data-combatant-id="{{this.id}}"
|
||||||
|
data-actor-id="{{this.actorId}}"
|
||||||
|
data-token-id="{{this.tokenId}}"
|
||||||
|
title="{{this.name}}"
|
||||||
|
>
|
||||||
|
{{this.name}}
|
||||||
|
</button>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="result-non-lethal">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.damageMessage"
|
||||||
|
}}:
|
||||||
|
<strong>{{rollResult}}</strong>
|
||||||
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|||||||
@@ -8,19 +8,19 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<li class="san-loose-buttons">
|
<li class="san-loose-buttons">
|
||||||
<button class="san-loose" data-san-value="0">0</button>
|
<button class="san-loose chat-action-button" data-san-value="0">0</button>
|
||||||
<button class="san-loose" data-san-value="1">1</button>
|
<button class="san-loose chat-action-button" data-san-value="1">1</button>
|
||||||
<button class="san-loose" data-san-value="2">2</button>
|
<button class="san-loose chat-action-button" data-san-value="2">2</button>
|
||||||
<button class="san-loose" data-san-value="3">3</button>
|
<button class="san-loose chat-action-button" data-san-value="3">3</button>
|
||||||
<button class="san-loose" data-san-value="4">4</button>
|
<button class="san-loose chat-action-button" data-san-value="4">4</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="san-loose-buttons">
|
<li class="san-loose-buttons">
|
||||||
<button class="san-loose" data-san-value="1d4">1d4</button>
|
<button class="san-loose chat-action-button" data-san-value="1d4">1d4</button>
|
||||||
<button class="san-loose" data-san-value="1d6">1d6</button>
|
<button class="san-loose chat-action-button" data-san-value="1d6">1d6</button>
|
||||||
<button class="san-loose" data-san-value="1d8">1d8</button>
|
<button class="san-loose chat-action-button" data-san-value="1d8">1d8</button>
|
||||||
<button class="san-loose" data-san-value="1d10">1d10</button>
|
<button class="san-loose chat-action-button" data-san-value="1d10">1d10</button>
|
||||||
<button class="san-loose" data-san-value="1d12">1d12</button>
|
<button class="san-loose chat-action-button" data-san-value="1d12">1d12</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
<li><strong>{{localize "CTHULHUETERNAL.Label.selectSANType"}}</strong></li>
|
<li><strong>{{localize "CTHULHUETERNAL.Label.selectSANType"}}</strong></li>
|
||||||
|
|
||||||
<li class="san-type-buttons">
|
<li class="san-type-buttons">
|
||||||
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="violence">{{localize "CTHULHUETERNAL.Label.Violence"}}</button>
|
<button class="san-type chat-action-button" data-san-value="{{sanLoss}}" data-san-type="violence">{{localize "CTHULHUETERNAL.Label.Violence"}}</button>
|
||||||
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="helplessness">{{localize "CTHULHUETERNAL.Label.Helplessness"}}</button>
|
<button class="san-type chat-action-button" data-san-value="{{sanLoss}}" data-san-type="helplessness">{{localize "CTHULHUETERNAL.Label.Helplessness"}}</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="san-type-buttons">
|
<li class="san-type-buttons">
|
||||||
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="unnatural">{{localize "CTHULHUETERNAL.Label.Unnatural"}}</button>
|
<button class="san-type chat-action-button" data-san-value="{{sanLoss}}" data-san-type="unnatural">{{localize "CTHULHUETERNAL.Label.Unnatural"}}</button>
|
||||||
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="none">{{localize "CTHULHUETERNAL.Label.None"}}</button>
|
<button class="san-type chat-action-button" data-san-value="{{sanLoss}}" data-san-type="none">{{localize "CTHULHUETERNAL.Label.None"}}</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{{localize "CTHULHUETERNAL.Label.description"}}</legend>
|
<legend>{{localize "CTHULHUETERNAL.Label.description"}}</legend>
|
||||||
{{formInput systemFields.description enriched=enrichedDescriptionw value=system.description name="system.description" toggled=true}}
|
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
{{#each weapons as |item|}}
|
{{#each weapons as |item|}}
|
||||||
{{!log 'weapon' this}}
|
{{!log 'weapon' this}}
|
||||||
<div class="weapon item" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true">
|
<div class="weapon item" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true">
|
||||||
|
<!-- content -->
|
||||||
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
|
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
|
||||||
<img src="systems/fvtt-cthulhu-eternal/assets/ui/d100.svg" class="d100" />
|
<img src="systems/fvtt-cthulhu-eternal/assets/ui/d100.svg" class="d100" />
|
||||||
<div class="name rollable" data-roll-type="weapon" data-tooltip="{{{item.system.description}}}">
|
<div class="name rollable" data-roll-type="weapon" data-tooltip="{{{item.system.description}}}">
|
||||||
@@ -81,6 +81,8 @@
|
|||||||
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="empty-state">{{localize "CTHULHUETERNAL.Label.noWeapons"}}</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -99,6 +101,13 @@
|
|||||||
{{item.name}}
|
{{item.name}}
|
||||||
</div>
|
</div>
|
||||||
<span class="protection">{{localize "CTHULHUETERNAL.Label.armor"}} : {{item.system.protection}}</span>
|
<span class="protection">{{localize "CTHULHUETERNAL.Label.armor"}} : {{item.system.protection}}</span>
|
||||||
|
<a class="equipped-toggle {{#if item.system.equipped}}active{{/if}}"
|
||||||
|
data-action="toggleEquipped"
|
||||||
|
data-item-id="{{item.id}}"
|
||||||
|
data-item-uuid="{{item.uuid}}"
|
||||||
|
data-tooltip="{{#if item.system.equipped}}{{localize 'CTHULHUETERNAL.Label.equipped'}}{{else}}{{localize 'CTHULHUETERNAL.Label.unequipped'}}{{/if}}">
|
||||||
|
<i class="fas {{#if item.system.equipped}}fa-shield-halved{{else}}fa-shield{{/if}}"></i>
|
||||||
|
</a>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<a data-tooltip="{{localize 'CTHULHUETERNAL.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
|
<a data-tooltip="{{localize 'CTHULHUETERNAL.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
|
||||||
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
|
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
|
||||||
@@ -106,6 +115,8 @@
|
|||||||
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="empty-state">{{localize "CTHULHUETERNAL.Label.noArmors"}}</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -130,6 +141,8 @@
|
|||||||
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="empty-state">{{localize "CTHULHUETERNAL.Label.noGears"}}</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
<a data-tooltip="{{localize 'CTHULHUETERNAL.Delete'}}" data-action="delete" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
<a data-tooltip="{{localize 'CTHULHUETERNAL.Delete'}}" data-action="delete" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="empty-state">{{localize "CTHULHUETERNAL.Label.noSkills"}}</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|||||||
+121
-20
@@ -9,25 +9,72 @@
|
|||||||
|
|
||||||
{{#if (eq rollType "resource")}}
|
{{#if (eq rollType "resource")}}
|
||||||
<legend>{{localize "CTHULHUETERNAL.Label.resourceRating"}}</legend>
|
<legend>{{localize "CTHULHUETERNAL.Label.resourceRating"}}</legend>
|
||||||
<div class="dialog-skill">{{rollItem.name}} : <span class="resource-score">{{initialScore}} ({{mul initialScore 5}}%)</span></div>
|
<div class="dialog-skill">{{rollItem.name}}
|
||||||
<div class="dialog-skill">{{localize "CTHULHUETERNAL.Label.Hand"}} : {{rollItem.hand}} <input type="checkbox" data-action="selectHand" {{checked rollItem.enableHand}}></div>
|
:
|
||||||
<div class="dialog-skill">{{localize "CTHULHUETERNAL.Label.Stowed"}} : {{rollItem.stowed}} <input type="checkbox" data-action="selectStowed" {{checked rollItem.enableStowed}}></div>
|
<span class="resource-score">{{initialScore}}
|
||||||
|
({{mul initialScore 5}}%)</span></div>
|
||||||
|
<div class="dialog-skill">{{localize "CTHULHUETERNAL.Label.Hand"}}
|
||||||
|
:
|
||||||
|
{{rollItem.hand}}
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
data-action="selectHand"
|
||||||
|
{{checked rollItem.enableHand}}
|
||||||
|
/></div>
|
||||||
|
<div class="dialog-skill">{{localize "CTHULHUETERNAL.Label.Stowed"}}
|
||||||
|
:
|
||||||
|
{{rollItem.stowed}}
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
data-action="selectStowed"
|
||||||
|
{{checked rollItem.enableStowed}}
|
||||||
|
/></div>
|
||||||
<div class="dialog-skill">
|
<div class="dialog-skill">
|
||||||
{{localize "CTHULHUETERNAL.Label.Storage"}} : {{rollItem.storage}}
|
{{localize "CTHULHUETERNAL.Label.Storage"}}
|
||||||
<input type="checkbox" data-action="selectStorage" {{checked rollItem.enableStorage}}>
|
:
|
||||||
|
{{rollItem.storage}}
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
data-action="selectStorage"
|
||||||
|
{{checked rollItem.enableStorage}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="dialog-skill">{{rollItem.name}} : {{initialScore}}%</div>
|
<div class="dialog-skill">{{rollItem.name}} : {{initialScore}}%</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if weapon}}
|
{{#if weapon}}
|
||||||
<div class="dialog-skill">{{localize "CTHULHUETERNAL.Label.Weapon"}} : {{weapon.name}}</div>
|
{{#if isJunk}}
|
||||||
|
<div class="dialog-skill red-warning">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.JunkWeapon"
|
||||||
|
}}</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if isWorn}}
|
||||||
|
<div class="dialog-skill orange-warning">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.WornWeapon"
|
||||||
|
}}</div>
|
||||||
|
{{/if}}
|
||||||
|
<div class="dialog-skill">{{localize "CTHULHUETERNAL.Label.Weapon"}}
|
||||||
|
:
|
||||||
|
{{weapon.name}}</div>
|
||||||
|
|
||||||
|
{{#if targetName}}
|
||||||
|
<div class="dialog-skill">{{localize "CTHULHUETERNAL.Label.Target"}}
|
||||||
|
:
|
||||||
|
{{targetName}}</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if (eq weapon.system.weaponType "melee")}}
|
{{#if (eq weapon.system.weaponType "melee")}}
|
||||||
<div class="dialog-skill">
|
<div class="dialog-skill">
|
||||||
{{localize "CTHULHUETERNAL.Label.targetMove"}}
|
{{localize "CTHULHUETERNAL.Label.targetMove"}}
|
||||||
<select name="meleeTargetMoveChoice" class="roll-skill-modifier">
|
<select name="meleeTargetMoveChoice" class="roll-skill-modifier">
|
||||||
{{selectOptions choiceMeleeTargetMove localize=true selected=meleeTargetMoveChoice valueAttr="id" labelAttr="label"}}
|
{{selectOptions
|
||||||
|
choiceMeleeTargetMove
|
||||||
|
localize=true
|
||||||
|
selected=meleeTargetMoveChoice
|
||||||
|
valueAttr="id"
|
||||||
|
labelAttr="label"
|
||||||
|
}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -35,24 +82,44 @@
|
|||||||
<div class="dialog-skill">
|
<div class="dialog-skill">
|
||||||
{{localize "CTHULHUETERNAL.Label.rangedRange"}}
|
{{localize "CTHULHUETERNAL.Label.rangedRange"}}
|
||||||
<select name="rangedRangeChoice" class="roll-skill-modifier">
|
<select name="rangedRangeChoice" class="roll-skill-modifier">
|
||||||
{{selectOptions choiceRangedRange localize=true selected=rangedRangeChoice valueAttr="id" labelAttr="label"}}
|
{{selectOptions
|
||||||
|
choiceRangedRange
|
||||||
|
localize=true
|
||||||
|
selected=rangedRangeChoice
|
||||||
|
valueAttr="id"
|
||||||
|
labelAttr="label"
|
||||||
|
}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-skill">
|
<div class="dialog-skill">
|
||||||
{{localize "CTHULHUETERNAL.Label.targetMove"}}
|
{{localize "CTHULHUETERNAL.Label.targetMove"}}
|
||||||
<select name="rangedTargetMoveChoice" class="roll-skill-modifier">
|
<select name="rangedTargetMoveChoice" class="roll-skill-modifier">
|
||||||
{{selectOptions choiceRangedTargetMove localize=true selected=rangedTargetMoveChoice valueAttr="id" labelAttr="label"}}
|
{{selectOptions
|
||||||
|
choiceRangedTargetMove
|
||||||
|
localize=true
|
||||||
|
selected=rangedTargetMoveChoice
|
||||||
|
valueAttr="id"
|
||||||
|
labelAttr="label"
|
||||||
|
}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-skill">
|
<div class="dialog-skill">
|
||||||
{{localize "CTHULHUETERNAL.Label.aimingLastRound"}}
|
{{localize "CTHULHUETERNAL.Label.aimingLastRound"}}
|
||||||
<input type="checkbox" name="aimingLastRound">
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="aimingLastRound"
|
||||||
|
name="aimingLastRound"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{{#if weapon.system.hasSight}}
|
{{#if weapon.system.hasSight}}
|
||||||
<div class="dialog-skill">
|
<div class="dialog-skill">
|
||||||
{{localize "CTHULHUETERNAL.Label.aimingWithSight"}}
|
{{localize "CTHULHUETERNAL.Label.aimingWithSight"}}
|
||||||
<input type="checkbox" name="aimingWithSight">
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="aimingWithSight"
|
||||||
|
name="aimingWithSight"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
@@ -61,26 +128,50 @@
|
|||||||
<div class="dialog-skill">
|
<div class="dialog-skill">
|
||||||
{{localize "CTHULHUETERNAL.Label.visibility"}}
|
{{localize "CTHULHUETERNAL.Label.visibility"}}
|
||||||
<select name="visibilityChoice" class="roll-skill-modifier">
|
<select name="visibilityChoice" class="roll-skill-modifier">
|
||||||
{{selectOptions choiceVisibility localize=true selected=visibilityChoice valueAttr="id" labelAttr="label"}}
|
{{selectOptions
|
||||||
|
choiceVisibility
|
||||||
|
localize=true
|
||||||
|
selected=visibilityChoice
|
||||||
|
valueAttr="id"
|
||||||
|
labelAttr="label"
|
||||||
|
}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-skill">
|
<div class="dialog-skill">
|
||||||
{{localize "CTHULHUETERNAL.Label.attackerState"}}
|
{{localize "CTHULHUETERNAL.Label.attackerState"}}
|
||||||
<select name="attackerStateChoice" class="roll-skill-modifier">
|
<select name="attackerStateChoice" class="roll-skill-modifier">
|
||||||
{{selectOptions choiceAttackerState localize=true selected=attackerStateChoice valueAttr="id" labelAttr="label"}}
|
{{selectOptions
|
||||||
|
choiceAttackerState
|
||||||
|
localize=true
|
||||||
|
selected=attackerStateChoice
|
||||||
|
valueAttr="id"
|
||||||
|
labelAttr="label"
|
||||||
|
}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-skill">
|
<div class="dialog-skill">
|
||||||
{{localize "CTHULHUETERNAL.Label.targetSize"}}
|
{{localize "CTHULHUETERNAL.Label.targetSize"}}
|
||||||
<select name="targetSizeChoice" class="roll-skill-modifier">
|
<select name="targetSizeChoice" class="roll-skill-modifier">
|
||||||
{{selectOptions choiceTargetSize localize=true selected=targetSizeChoice valueAttr="id" labelAttr="label"}}
|
{{selectOptions
|
||||||
|
choiceTargetSize
|
||||||
|
localize=true
|
||||||
|
selected=targetSizeChoice
|
||||||
|
valueAttr="id"
|
||||||
|
labelAttr="label"
|
||||||
|
}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if weapon.system.hasSelectiveFire}}
|
{{#if weapon.system.hasSelectiveFire}}
|
||||||
<div class="dialog-skill">Selective Fire :
|
<div class="dialog-skill">Selective Fire :
|
||||||
<select name="selectiveFireChoice" class="roll-skill-modifier">
|
<select name="selectiveFireChoice" class="roll-skill-modifier">
|
||||||
{{selectOptions choiceSelectiveFire localize=true selected=selectiveFireChoice valueAttr="id" labelAttr="label"}}
|
{{selectOptions
|
||||||
|
choiceSelectiveFire
|
||||||
|
localize=true
|
||||||
|
selected=selectiveFireChoice
|
||||||
|
valueAttr="id"
|
||||||
|
labelAttr="label"
|
||||||
|
}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -88,20 +179,27 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isZeroWP}}
|
{{#if isZeroWP}}
|
||||||
<div class="dialog-skill red-warning">{{localize "CTHULHUETERNAL.Label.ZeroWP"}}</div>
|
<div class="dialog-skill red-warning">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.ZeroWP"
|
||||||
|
}}</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if isLowWP}}
|
{{#if isLowWP}}
|
||||||
<div class="dialog-skill orange-warning">{{localize "CTHULHUETERNAL.Label.LowWP"}} : -20%</div>
|
<div class="dialog-skill orange-warning">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.LowWP"
|
||||||
|
}}
|
||||||
|
: -20%</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isExhausted}}
|
{{#if isExhausted}}
|
||||||
<div class="dialog-skill orange-warning">{{localize "CTHULHUETERNAL.Label.Exhausted"}} : -20%</div>
|
<div class="dialog-skill orange-warning">{{localize
|
||||||
|
"CTHULHUETERNAL.Label.Exhausted"
|
||||||
|
}}
|
||||||
|
: -20%</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
</fieldSet>
|
</fieldSet>
|
||||||
|
|
||||||
|
|
||||||
{{#if hasModifier}}
|
{{#if hasModifier}}
|
||||||
<fieldSet class="dialog-modifier">
|
<fieldSet class="dialog-modifier">
|
||||||
<legend>{{localize "CTHULHUETERNAL.Label.modifier"}}</legend>
|
<legend>{{localize "CTHULHUETERNAL.Label.modifier"}}</legend>
|
||||||
@@ -114,7 +212,10 @@
|
|||||||
{{#if hasMultiplier}}
|
{{#if hasMultiplier}}
|
||||||
<fieldSet class="dialog-modifier">
|
<fieldSet class="dialog-modifier">
|
||||||
<legend>{{localize "CTHULHUETERNAL.Label.multiplier"}}</legend>
|
<legend>{{localize "CTHULHUETERNAL.Label.multiplier"}}</legend>
|
||||||
<select name="multiplier" class="roll-skill-modifier roll-skill-multiplier">
|
<select
|
||||||
|
name="multiplier"
|
||||||
|
class="roll-skill-modifier roll-skill-multiplier"
|
||||||
|
>
|
||||||
{{selectOptions choiceMultiplier selected=multiplier}}
|
{{selectOptions choiceMultiplier selected=multiplier}}
|
||||||
</select>
|
</select>
|
||||||
</fieldSet>
|
</fieldSet>
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
def check_utf8_encoding(filenames):
|
||||||
|
"""
|
||||||
|
Checks a list of filenames to detect potential UTF-8 encoding errors.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filenames (list): A list of strings representing filenames to check.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A dictionary where keys are filenames with errors and values are the error messages.
|
||||||
|
"""
|
||||||
|
error_report = {}
|
||||||
|
print("--- Starting UTF-8 Encoding Check ---")
|
||||||
|
|
||||||
|
for filename in filenames:
|
||||||
|
try:
|
||||||
|
# Attempt to decode the filename as UTF-8
|
||||||
|
filename.encode('utf-8')
|
||||||
|
# If encode succeeds, it's likely valid UTF-8
|
||||||
|
print(f"SUCCESS: {filename}")
|
||||||
|
except UnicodeEncodeError as e:
|
||||||
|
# If encoding fails, it indicates invalid UTF-8 sequences
|
||||||
|
error_report[filename] = str(e)
|
||||||
|
print(f"ERROR: {filename} - Decoding failed: {e}")
|
||||||
|
|
||||||
|
print("--- Check Complete ---")
|
||||||
|
if error_report:
|
||||||
|
print("\n--- Summary of Errors ---")
|
||||||
|
for filename, error in error_report.items():
|
||||||
|
print(f"File: {filename}\n Error: {error}\n")
|
||||||
|
else:
|
||||||
|
print("\nAll checked filenames appear to be valid UTF-8.")
|
||||||
|
|
||||||
|
return error_report
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Example list of filenames to check. Replace these with your actual directory contents.
|
||||||
|
files_to_check = [
|
||||||
|
"valid_file.txt",
|
||||||
|
"file_with_bad_byte\x80.bin", # Example of a potentially bad filename
|
||||||
|
"another_valid_one.md",
|
||||||
|
"file_with_another_error\x99.dat"
|
||||||
|
]
|
||||||
|
|
||||||
|
check_utf8_encoding(files_to_check)
|
||||||
Reference in New Issue
Block a user