1 Commits
Author SHA1 Message Date
uberwald cf905cdcfb feat: fiche PNJ (profil + niveaux) et passage du véhicule en acteur
Release Creation / build (release) Failing after 1m17s
2026-08-04 20:34:20 +02:00
36 changed files with 1288 additions and 707 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

+9 -2
View File
@@ -16,7 +16,7 @@
"TYPES.Item.item": "Item",
"TYPES.Item.weapon": "Weapon",
"TYPES.Item.defense": "Defense",
"TYPES.Item.vehicle": "Vehicle",
"TYPES.Actor.vehicle": "Vehicle",
"TYPES.Item.ability": "Ability",
"TYPES.Item.specialty": "Specialty",
"TYPES.Item.background": "Background",
@@ -145,6 +145,8 @@
},
"pool": "Reserve",
"pools": "Reserves",
"dice_pool": "Dice pool",
"auto_successes": "automatic successes",
"self_control": "Self-control",
"effort": "Effort",
"equip": "Equip",
@@ -174,6 +176,7 @@
"range": "Range",
"ranges": "Ranges",
"mobility": "Mobility",
"vehicle": "Vehicle",
"vehicle_specs": "Vehicle specifications",
"kind": "Kind",
"kind_vehicle": "Vehicle",
@@ -183,6 +186,8 @@
"speed_duration_ph": "top speed duration (e.g. 10 min)",
"effort_reserve": "Effort reserve",
"maintenance": "Maintenance",
"owner": "Owner",
"no_vehicles": "No vehicles",
"rarity": "Rarity",
"rarity_sm": "Rar.",
"reliability": "Reliability",
@@ -214,6 +219,7 @@
"type_npc": "NPC",
"type_creature": "Creature",
"type_community": "Community",
"type_vehicle": "Vehicle",
"type": "Type",
"group_members": "Members",
"encounters": "Encounters",
@@ -394,7 +400,8 @@
"npc": "NPC",
"group": "Group",
"creature": "Creature",
"community": "Community"
"community": "Community",
"vehicle": "Vehicle"
},
"base_values": "Base Values",
"computed_values": "Computed Values",
+9 -2
View File
@@ -8,7 +8,7 @@
"TYPES.Item.item": "Objet",
"TYPES.Item.weapon": "Arme",
"TYPES.Item.defense": "Protection",
"TYPES.Item.vehicle": "Véhicule",
"TYPES.Actor.vehicle": "Véhicule",
"TYPES.Item.ability": "Capacité",
"TYPES.Item.specialty": "Spécialité",
"TYPES.Item.background": "Historique",
@@ -152,6 +152,8 @@
"pool": "Réserve",
"pools": "Réserves",
"reserves": "Réserves",
"dice_pool": "Pool de dés",
"auto_successes": "réussites automatiques",
"self_control": "Sang-Froid",
"effort": "Effort",
"equip": "Équiper",
@@ -255,6 +257,7 @@
"range": "Portée",
"ranges": "Portées",
"mobility": "Mobilité",
"vehicle": "Véhicule",
"vehicle_specs": "Caractéristiques du véhicule",
"kind": "Type",
"kind_vehicle": "Véhicule",
@@ -264,6 +267,8 @@
"speed_duration_ph": "durée de pointe (ex: 10 min)",
"effort_reserve": "Réserve d'Effort",
"maintenance": "Entretien",
"owner": "Propriétaire",
"no_vehicles": "Aucun véhicule",
"rarity": "Rareté",
"rarity_sm": "Rar.",
"reliability": "Fiabilité",
@@ -405,6 +410,7 @@
"type_npc": "PNJ",
"type_creature": "Créature",
"type_community": "Communauté",
"type_vehicle": "Véhicule",
"type": "Type",
"group_members": "Membres",
"encounters": "Rencontres",
@@ -451,7 +457,8 @@
"npc": "PNJ",
"group": "Groupe",
"creature": "Créature",
"community": "Communauté"
"community": "Communauté",
"vehicle": "Véhicule"
}
},
"UI": {
+60 -89
View File
@@ -30,77 +30,70 @@
}
}
.skills-container {
display: flex;
flex-direction: column;
gap: 15px;
}
.skill-category {
.card-style();
padding: 10px;
h4 {
font-family: "DistressBlack", sans-serif;
font-size: @font-size-14;
text-transform: uppercase;
margin-top: 0;
margin-bottom: 8px;
background: 50% 0% / cover no-repeat url("@{ui-path}/scotch.webp");
}
}
.skill-item {
.npc-trait-list {
li {
display: flex;
align-items: center;
gap: 8px;
padding: 5px;
gap: 6px;
padding: 4px 0;
border-bottom: 1px solid @color-border-dark-3;
font-size: @font-size-12;
&:last-child { border-bottom: none; }
label {
flex: 1;
font-size: @font-size-12;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
strong { flex: 1; }
}
&:hover {
box-shadow: 0 0 10px @theme-color-highlight inset;
}
}
.skill-control {
display: flex;
align-items: center;
gap: 5px;
}
.skill-value {
min-width: 25px;
.trait-value {
min-width: 30px;
text-align: center;
font-weight: bold;
white-space: nowrap;
}
}
.rarity-badge {
display: inline-block;
padding: 1px 4px;
border-radius: 3px;
font-size: 10px;
.npc-roll-btn {
color: @color-text-light-primary;
opacity: 0.7;
transition: opacity 0.15s ease;
&:hover {
opacity: 1;
color: @color-text-light-1;
text-shadow: 0 0 6px @theme-color-highlight;
}
}
.npc-wounds {
flex-wrap: wrap;
.wound-cat {
display: inline-flex;
align-items: center;
gap: 4px;
font-weight: bold;
margin-left: 5px;
&.rarity-0 { .rarity-badge(@rarity-0-bg, @rarity-0-text); }
&.rarity-1 { .rarity-badge(@rarity-1-bg, @rarity-1-text); }
&.rarity-2 { .rarity-badge(@rarity-2-bg, @rarity-2-text); }
&.rarity-3 { .rarity-badge(@rarity-3-bg, @rarity-3-text); }
}
.skill-category-selector {
margin-bottom: 15px;
.card-style();
.wound-boxes {
display: inline-flex;
gap: 2px;
i {
font-size: 8px;
color: @color-border-dark-2;
}
}
}
.npc-free-skills {
margin-top: 15px;
.form-group-style();
label {
font-size: @font-size-12;
font-weight: bold;
white-space: nowrap;
}
}
.wounds-card {
@@ -133,39 +126,6 @@
}
}
.ability-section {
.card-style();
}
.ability-category-header {
font-family: "DistressBlack", sans-serif;
font-size: @font-size-14;
text-transform: uppercase;
margin-top: 0;
margin-bottom: 8px;
border-bottom: 1px solid @color-border-dark-3;
padding-bottom: 5px;
}
.ability-item {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 0;
label {
flex: 1;
font-size: @font-size-12;
white-space: nowrap;
}
}
.ability-control {
display: flex;
align-items: center;
gap: 8px;
}
.tab {
padding: 10px;
overflow-y: auto;
@@ -213,6 +173,17 @@
padding: 3px 5px;
min-width: 100px;
}
input[type="text"] {
font-size: @font-size-12;
padding: 3px 5px;
min-width: 0;
width: 100%;
}
}
.npc-profile-grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.editor {
+172
View File
@@ -0,0 +1,172 @@
@import "../variables";
@import "../utilities";
.system-vermine2047 .vermine2047.actor.vehicle {
display: grid !important;
grid-template-columns: 1fr !important;
grid-template-rows: auto 1fr auto !important;
> header.window-header {
grid-column: 1;
grid-row: 1;
}
> section.window-content {
grid-column: 1;
grid-row: 2;
}
> div.window-resize-handle {
grid-column: 1;
grid-row: 3;
}
.window-content {
display: grid;
grid-template-rows: auto auto 1fr;
grid-template-columns: 1fr;
overflow: hidden;
> .tab.main {
grid-column: 1;
grid-row: 1;
display: flex !important;
flex-direction: column;
overflow-y: auto;
padding: 8px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
> nav.tabs {
grid-column: 1;
grid-row: 2;
}
> .tab:not(.main) {
grid-column: 1;
grid-row: 3;
display: none;
overflow-y: auto;
padding: 8px;
&.active {
display: flex;
flex-direction: column;
}
}
}
.sheet-header {
.background-image(url("@{ui-path}/barre_haut.webp"), no-repeat, 100% 100%, right top);
padding: 10px;
max-height: 130px;
}
.header-fields { flex: 1; }
.resource {
.card-style();
}
.resource-label {
font-weight: bold;
margin-right: 8px;
min-width: 60px;
font-size: @font-size-12;
}
.resource-content {
display: flex;
align-items: center;
select {
margin-right: 8px;
min-width: 100px;
}
input[type="number"] {
width: 60px;
text-align: center;
}
}
.charname {
margin: 0;
input {
width: 100%;
font-size: @font-size-18;
font-weight: bold;
text-align: center;
}
}
.vehicle-owner {
display: flex;
gap: 6px;
margin-bottom: 8px;
padding: 6px 8px;
border: 1px dashed @color-hemolymph;
background: fade(@color-hemolymph, 8%);
.label {
font-family: "DistressBlack", sans-serif;
text-transform: uppercase;
font-size: @font-size-11;
color: @color-amber;
}
}
.vehicle-section {
padding: 8px;
border: 1px solid @color-border-dark-4;
background: fade(@color-chitin-dark, 20%);
margin-bottom: 8px;
h3 {
font-family: "DistressBlack", sans-serif;
text-transform: uppercase;
font-size: @font-size-13;
color: @theme-color-dark;
margin: 0 0 8px;
text-align: left;
border-bottom: none;
}
.resource-label {
display: block;
margin: 0 0 4px;
min-width: 0;
}
.unit {
font-size: @font-size-11;
color: @color-text-light-0;
opacity: 0.8;
}
.maintenance {
margin-top: 8px;
.flexrow label {
gap: 4px;
margin-right: 12px;
font-size: @font-size-12;
}
}
.form-group {
label {
font-family: "DistressBlack", sans-serif;
text-transform: uppercase;
font-size: @font-size-11;
color: @color-amber;
}
}
}
input[type="text"] {
width: 100%;
padding: 5px;
}
}
+1
View File
@@ -23,6 +23,7 @@
@import "actor/group";
@import "actor/creature";
@import "actor/community";
@import "actor/vehicle";
// 6. Styles des items
@import "items";
+1 -1
View File
@@ -5,11 +5,11 @@ export { default as VermineNpcSheetV2 } from "./npc-sheet.mjs"
export { default as VermineGroupSheetV2 } from "./group-sheet.mjs"
export { default as VermineCreatureSheetV2 } from "./creature-sheet.mjs"
export { default as VermineCommunitySheetV2 } from "./community-sheet.mjs"
export { default as VermineVehicleSheetV2 } from "./vehicle-sheet.mjs"
export {
VermineItemSheetV2,
VermineWeaponSheetV2,
VermineDefenseSheetV2,
VermineVehicleSheetV2,
VermineAbilitySheetV2,
VermineSpecialtySheetV2,
VermineBackgroundSheetV2,
@@ -32,6 +32,9 @@ export default class VermineBaseActorSheet extends HandlebarsApplicationMixin(fo
delete: VermineBaseActorSheet.#onItemDelete,
toggleEquip: VermineBaseActorSheet.#onToggleEquip,
create: VermineBaseActorSheet.#onItemCreate,
createVehicle: VermineBaseActorSheet.#onCreateVehicle,
editVehicle: VermineBaseActorSheet.#onEditVehicle,
deleteVehicle: VermineBaseActorSheet.#onDeleteVehicle,
roll: VermineBaseActorSheet.#onRollItem,
attack: VermineBaseActorSheet.#onAttack,
clickRadio: VermineBaseActorSheet.#onClickRadioHexa,
@@ -229,11 +232,46 @@ export default class VermineBaseActorSheet extends HandlebarsApplicationMixin(fo
await this.document.createEmbeddedDocuments("Item", [{ name, type }])
}
// ── Véhicules (acteurs) ──────────────────────────────────────────────
/** Crée un acteur "vehicle" rattaché à cet acteur (groupe ou personnage). */
static async #onCreateVehicle(event, target) {
const name = game.i18n.localize("ITEMS.new_vehicle")
const vehicle = await Actor.create({
name,
type: "vehicle",
system: { ownerId: this.document.id },
// Le véhicule est un bien partagé : reprendre la propriété du propriétaire
ownership: foundry.utils.deepClone(this.document.ownership)
})
vehicle?.sheet.render(true)
}
static async #onEditVehicle(event, target) {
const id = target.closest("[data-vehicle-id]")?.dataset?.vehicleId
const vehicle = game.actors.get(id)
vehicle?.sheet.render(true)
}
static async #onDeleteVehicle(event, target) {
const id = target.closest("[data-vehicle-id]")?.dataset?.vehicleId
const vehicle = game.actors.get(id)
await vehicle?.deleteDialog()
}
static async #onRollItem(event, target) {
const id = target.closest("[data-item-id]")?.dataset?.itemId
if (!id) return
const item = this.document.items.get(id)
if (!item) return
// Armes : PNJ → dialogue de combat (valeur d'Attaque) ; personnage →
// dialogue de compétence. Les PNJ n'ont ni caractéristiques ni compétences.
if (item.type === "weapon" && this.document.type === 'npc') {
const { default: CombatDialog } = await import("../../system/dialogs/combatDialog.mjs")
const dialog = await CombatDialog.create({ actorId: this.document.id, weapon: item })
if (dialog) dialog.render(true)
return
}
if (item.type === "weapon" && this.document.system.abilities && this.document.system.skills) {
const { default: RollDialog } = await import("../../system/dialogs/rollDialog.mjs")
const dialog = await RollDialog.create({
@@ -71,7 +71,7 @@ export default class VermineCharacterSheetV2 extends VermineBaseActorSheet {
context.gear = doc.itemTypes.item
context.weapons = doc.itemTypes.weapon
context.defenses = doc.itemTypes.defense
context.vehicles = doc.itemTypes.vehicle
context.vehicles = game.actors.filter(a => a.type === "vehicle" && a.system.ownerId === doc.id)
break
case "stories":
context.tab = context.tabs.stories
+7 -1
View File
@@ -80,10 +80,11 @@ export default class VermineGroupSheetV2 extends VermineBaseActorSheet {
context.gear = doc.itemTypes.item
context.weapons = doc.itemTypes.weapon
context.defenses = doc.itemTypes.defense
context.vehicles = this.#getOwnedVehicles()
break
case "road":
context.tab = context.tabs.road
context.vehicles = doc.itemTypes.vehicle
context.vehicles = this.#getOwnedVehicles()
break
case "reserve":
context.tab = context.tabs.reserve
@@ -92,6 +93,11 @@ export default class VermineGroupSheetV2 extends VermineBaseActorSheet {
return context
}
/** Véhicules (acteurs) appartenant à ce groupe via system.ownerId. */
#getOwnedVehicles() {
return game.actors.filter(a => a.type === "vehicle" && a.system.ownerId === this.document.id)
}
// Actions : délégation aux applications AppV1 existantes pour TotemPicker/ActorPicker
static async #onChooseTotem(event, target) {
const { TotemPicker } = await import("../../system/applications.mjs")
@@ -18,34 +18,6 @@ export class VermineDefenseSheetV2 extends VermineBaseItemSheet {
static PARTS = { main: { template: "systems/vermine2047/templates/item/item-defense-sheet.hbs", scrollable: [""] } }
}
// ── Véhicule ──────────────────────────────────────────────────────────
export class VermineVehicleSheetV2 extends VermineBaseItemSheet {
static DEFAULT_OPTIONS = { classes: ["vehicle"], position: { width: 520 } }
static PARTS = { main: { template: "systems/vermine2047/templates/item/item-vehicle-sheet.hbs", scrollable: [""] } }
async _prepareContext() {
const context = await super._prepareContext()
// Compétences d'Entretien possibles (mécanique, technologie, artisanat, animalisme…)
const maintenanceKeys = ["mecanical", "technology", "crafting", "animalism", "environment", "wildlife", "flora", "repulsion", "toxics"]
const current = this.document.system.maintenance || []
context.maintenanceSkills = maintenanceKeys.map(key => ({
key,
label: game.i18n.localize("SKILLS." + key + ".name"),
checked: current.includes(key)
}))
return context
}
/** @override - nettoie le tableau d'Entretien (retire les cases non cochées). */
_prepareSubmitData(event, form, formData, updateData) {
const result = super._prepareSubmitData(event, form, formData, updateData)
if (Array.isArray(result?.system?.maintenance)) {
result.system.maintenance = result.system.maintenance.filter(v => typeof v === "string" && v.trim() !== "")
}
return result
}
}
// ── Capacité ──────────────────────────────────────────────────────────
export class VermineAbilitySheetV2 extends VermineBaseItemSheet {
static DEFAULT_OPTIONS = { classes: ["ability"], position: { width: 560 } }
+31 -17
View File
@@ -2,8 +2,9 @@ import VermineBaseActorSheet from "./base-actor-sheet.mjs"
export default class VermineNpcSheetV2 extends VermineBaseActorSheet {
// La fiche PNJ s'ouvre en mode édition : caractéristiques, compétences et
// niveaux se règlent dès l'ouverture (le mode jeu reste accessible via le toggle).
// La fiche PNJ s'ouvre en mode édition : profil et niveaux (Menace,
// Expérience, Rôle) se règlent dès l'ouverture (le mode jeu reste accessible
// via le toggle).
_sheetMode = this.constructor.SHEET_MODES.EDIT
static DEFAULT_OPTIONS = {
@@ -15,22 +16,18 @@ export default class VermineNpcSheetV2 extends VermineBaseActorSheet {
static PARTS = {
main: { template: "systems/vermine2047/templates/actor/appv2/npc-main.hbs", scrollable: [""] },
tabs: { template: "templates/generic/tab-navigation.hbs" },
characteristics: { template: "systems/vermine2047/templates/actor/appv2/npc-characteristics.hbs", scrollable: [""] },
skills: { template: "systems/vermine2047/templates/actor/appv2/npc-skills.hbs", scrollable: [""] },
traits: { template: "systems/vermine2047/templates/actor/appv2/npc-traits.hbs", scrollable: [""] },
equipment: { template: "systems/vermine2047/templates/actor/appv2/npc-equipment.hbs", scrollable: [""] },
threat: { template: "systems/vermine2047/templates/actor/appv2/npc-threat.hbs", scrollable: [""] },
combat: { template: "systems/vermine2047/templates/actor/appv2/npc-combat.hbs", scrollable: [""] },
notes: { template: "systems/vermine2047/templates/actor/appv2/npc-notes.hbs", scrollable: [""] }
}
tabGroups = { sheet: "characteristics" }
tabGroups = { sheet: "traits" }
#getTabs() {
const tabs = {
characteristics: { id: "characteristics", group: "sheet", icon: "fas fa-dice", label: "VERMINE.abilities" },
skills: { id: "skills", group: "sheet", icon: "fas fa-brain", label: "VERMINE.skills" },
traits: { id: "traits", group: "sheet", icon: "fas fa-dice", label: "VERMINE.traits" },
equipment: { id: "equipment", group: "sheet", icon: "fas fa-hammer", label: "VERMINE.tabs.equipment" },
threat: { id: "threat", group: "sheet", icon: "fas fa-exclamation-triangle", label: "ADVERSITY.threat" },
combat: { id: "combat", group: "sheet", icon: "fas fa-sword", label: "VERMINE.combat" },
notes: { id: "notes", group: "sheet", icon: "fas fa-sticky-note", label: "IDENTITY.notes" }
}
@@ -57,20 +54,14 @@ export default class VermineNpcSheetV2 extends VermineBaseActorSheet {
context.totemOptions = CONFIG.VERMINE.totems
context.originOptions = CONFIG.VERMINE.origins
break
case "characteristics":
context.tab = context.tabs.characteristics
break
case "skills":
context.tab = context.tabs.skills
case "traits":
context.tab = context.tabs.traits
break
case "equipment":
context.tab = context.tabs.equipment
context.weapons = doc.itemTypes.weapon
context.defenses = doc.itemTypes.defense
break
case "threat":
context.tab = context.tabs.threat
break
case "combat":
context.tab = context.tabs.combat
context.equippedWeapons = doc.itemTypes.weapon.filter(i => i.system.equipped)
@@ -84,4 +75,27 @@ export default class VermineNpcSheetV2 extends VermineBaseActorSheet {
}
return context
}
/** @override - Le PNJ n'a ni caractéristiques ni compétences : les jets
* rapides utilisent la valeur d'Attaque (dialogue de combat) ou les valeurs
* dérivées Action / Spécialité / Réaction (dialogue PNJ simplifié). */
async _onRoll(event) {
event.preventDefault()
const el = event.currentTarget
const type = el.dataset.type
if (!type) return
if (type === "attack") {
const { default: CombatDialog } = await import("../../system/dialogs/combatDialog.mjs")
const dialog = await CombatDialog.create({ actorId: this.document.id })
if (dialog) dialog.render(true)
return
}
if (["action", "specialty", "reaction"].includes(type)) {
const { default: NpcRollDialog } = await import("../../system/dialogs/npcRollDialog.mjs")
const dialog = await NpcRollDialog.create({ actorId: this.document.id, rolltype: type })
if (dialog) dialog.render(true)
return
}
return super._onRoll(event)
}
}
@@ -0,0 +1,76 @@
import VermineBaseActorSheet from "./base-actor-sheet.mjs"
export default class VermineVehicleSheetV2 extends VermineBaseActorSheet {
static DEFAULT_OPTIONS = {
classes: ["vehicle"],
position: { width: 640, height: 580 },
window: { contentClasses: ["vehicle-content"] }
}
static PARTS = {
main: { template: "systems/vermine2047/templates/actor/appv2/vehicle-main.hbs", scrollable: [""] },
tabs: { template: "templates/generic/tab-navigation.hbs" },
specs: { template: "systems/vermine2047/templates/actor/appv2/vehicle-specs.hbs", scrollable: [""] }
}
tabGroups = { sheet: "specs" }
#getTabs() {
const tabs = {
specs: { id: "specs", group: "sheet", icon: "fas fa-car-side", label: "VERMINE.vehicle_specs" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
// Compétences d'Entretien possibles (mécanique, technologie, artisanat, animalisme…)
const maintenanceKeys = ["mecanical", "technology", "crafting", "animalism", "environment", "wildlife", "flora", "repulsion", "toxics"]
const current = this.document.system.maintenance || []
context.maintenanceSkills = maintenanceKeys.map(key => ({
key,
label: game.i18n.localize("SKILLS." + key + ".name"),
checked: current.includes(key)
}))
// Propriétaire (groupe ou personnage) résolu
context.resolvedOwner = null
if (this.document.system.ownerId) {
const owner = game.actors.get(this.document.system.ownerId)
if (owner) context.resolvedOwner = { id: owner.id, name: owner.name }
}
return context
}
async _preparePartContext(partId, context) {
switch (partId) {
case "main": break
case "specs":
context.tab = context.tabs.specs
break
}
return context
}
changeTab(tab, group, options = {}) {
super.changeTab(tab, group, options)
if (group === "sheet") {
const main = this.element?.querySelector('[data-group="sheet"][data-tab="main"]')
if (main) main.classList.add("active")
}
}
/** @override - nettoie le tableau d'Entretien (retire les cases non cochées). */
_prepareSubmitData(event, form, formData, updateData) {
const result = super._prepareSubmitData(event, form, formData, updateData)
if (Array.isArray(result?.system?.maintenance)) {
result.system.maintenance = result.system.maintenance.filter(v => typeof v === "string" && v.trim() !== "")
}
return result
}
}
+75 -62
View File
@@ -2,17 +2,22 @@
* DataModel pour les acteurs de type "npc" (PNJ).
* Étend foundry.abstract.TypeDataModel.
*
* Note : le champ libre de compétences (texte descriptif) est nommé "freeSkills"
* pour éviter le conflit avec le SchemaField "skills" qui contient les 30 compétences.
* Conforme aux règles des PNJ (regles/regles_pnj.txt) : un PNJ n'a ni
* caractéristiques chiffrées, ni réserves détaillées, ni liste de compétences.
* Il est défini uniquement par son Profil (identité) et trois critères :
* Menace, Expérience et Rôle. Toutes les valeurs utiles en jeu sont dérivées
* des tableaux de référence (CONFIG.VERMINE.npc*Levels) et exposées dans
* "system.computed".
*
* Note : le champ libre de description des Spécialités est nommé "freeSkills"
* pour éviter le conflit avec le champ "skills" que le template.json historique
* définissait comme texte libre pour les PNJ.
*/
import {
woundSchema,
combatStatusSchema,
equipmentSchema,
attributeSchema,
abilitiesSchema,
skillCategoriesSchema,
skillsSchema
attributeSchema
} from "./_shared.mjs"
export default class VermineNpcData extends foundry.abstract.TypeDataModel {
@@ -23,8 +28,7 @@ export default class VermineNpcData extends foundry.abstract.TypeDataModel {
/**
* Migration des données avant traitement par le schéma.
* Avant DataModel, template.json définissait "skills" comme un champ texte libre
* pour les PNJ. Le DataModel réserve "skills" pour les 30 compétences individuelles
* (SchemaField) et utilise "freeSkills" pour le texte libre.
* pour les PNJ. Le DataModel utilise "freeSkills" pour cette description.
* @param {Object} source Données brutes avant validation du schéma
* @returns {Object} Données migrées
*/
@@ -48,9 +52,10 @@ export default class VermineNpcData extends foundry.abstract.TypeDataModel {
// Statut de combat (base, difficulté par défaut 9 pour PNJ)
combatStatus: combatStatusSchema("9"),
// Identité
// Identité (Profil du PNJ)
identity: new fields.SchemaField({
name: new fields.StringField({ required: true, nullable: false, initial: "" }),
age: new fields.StringField({ required: true, nullable: false, initial: "" }),
profile: new fields.StringField({ required: true, nullable: false, initial: "" }),
origin: new fields.StringField({ required: true, nullable: false, initial: "" }),
totem: new fields.StringField({ required: true, nullable: false, initial: "" }),
@@ -58,30 +63,29 @@ export default class VermineNpcData extends foundry.abstract.TypeDataModel {
notes: new fields.HTMLField({ required: true, initial: "" })
}),
// Attributs (XP, réputation, sang-froid, effort)
attributes: new fields.SchemaField({
xp: attributeSchema(0, 0, 10),
reputation: attributeSchema(0, 0, 10),
self_control: attributeSchema(0, 0, 5),
effort: attributeSchema(0, 0, 5)
}),
// Niveaux PNJ (menace, expérience, rôle)
// Critères de définition du PNJ (Menace, Expérience, Rôle)
threat: attributeSchema(1, 1, 4),
experience: attributeSchema(1, 1, 4),
role: attributeSchema(1, 1, 4),
// Compétences (les 30 compétences individuelles)
skills: skillsSchema(),
// Description libre des compétences (champ texte PNJ)
// Description libre des Spécialités (champ texte PNJ)
freeSkills: new fields.StringField({ required: true, nullable: false, initial: "" }),
// Catégories de compétences
skill_categories: skillCategoriesSchema(),
// Caractéristiques (8)
abilities: abilitiesSchema(),
// Valeurs calculées (dérivées des niveaux de menace / expérience / rôle)
computed: new fields.SchemaField({
attack: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
vigor: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
action: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 3 }),
specialties: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 4 }),
rerolls: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
contact: new fields.StringField({ required: true, nullable: false, initial: "7" }),
reaction: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 3 }),
reactionBonus: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
pools: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
gear: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 9 }),
gearHindrance: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
protection: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1 })
}),
// Équipement
equipment: equipmentSchema()
@@ -92,58 +96,67 @@ export default class VermineNpcData extends foundry.abstract.TypeDataModel {
prepareDerivedData() {
super.prepareDerivedData()
// 1. Calculer les seuils de blessures selon le niveau de menace
// 1. Calculer les valeurs dérivées selon les niveaux de menace/expérience/rôle
this._setNpcComputedValues()
// 2. Calculer les seuils de blessures selon le niveau de menace
this._setNpcWoundThresholds()
// 2. Calculer les réserves selon le niveau de rôle
this._setNpcAttributes()
// 3. Définir les libellés des caractéristiques
this._setAbilityLabels()
// 4. Mettre à jour le statut de combat
// 3. Mettre à jour le statut de combat
this._updateCombatStatus()
}
/**
* Calcule les seuils de blessures à partir du niveau de menace.
* Utilise CONFIG.VERMINE.npcThreatLevels.
* Calcule les valeurs dérivées à partir des niveaux de Menace, Expérience
* et Rôle. Utilise CONFIG.VERMINE.npcThreatLevels, .npcExperienceLevels
* et .npcRoleLevels.
*/
_setNpcComputedValues() {
const threatLevel = this.threat?.value || 1
const expLevel = this.experience?.value || 1
const roleLevel = this.role?.value || 1
const threatConfig = CONFIG.VERMINE.npcThreatLevels[threatLevel] || {}
const expConfig = CONFIG.VERMINE.npcExperienceLevels[expLevel] || {}
const roleConfig = CONFIG.VERMINE.npcRoleLevels[roleLevel] || {}
// Menace
this.computed.attack = threatConfig.attack || 0
this.computed.vigor = threatConfig.vigor || 0
// Expérience
this.computed.action = expConfig.action || 3
this.computed.specialties = expConfig.specialties || 4
this.computed.rerolls = expConfig.rerolls || 0
this.computed.contact = expConfig.contact || "7"
// Rôle
this.computed.reaction = roleConfig.reaction || 0
this.computed.reactionBonus = roleConfig.reaction_bonus || 0
this.computed.pools = roleConfig.pools || 0
this.computed.gear = roleConfig.gear || 9
this.computed.gearHindrance = roleConfig.gear_hindrance || 0
this.computed.protection = roleConfig.protection || 1
}
/**
* Calcule les seuils et le nombre de boîtes de blessures à partir du
* niveau de menace (tableau "Niveau de Menace"). Utilise
* CONFIG.VERMINE.npcThreatLevels.
*/
_setNpcWoundThresholds() {
const health = this.abilities?.health?.value || 1
const threatLevel = this.threat?.value || 1
const threatConfig = CONFIG.VERMINE.npcThreatLevels[threatLevel] || {}
this.minorWound.threshold = health
this.majorWound.threshold = health + 3
this.deadlyWound.threshold = Math.min(health + 7, 10)
this.minorWound.threshold = threatConfig.minorThreshold ?? 1
this.majorWound.threshold = threatConfig.majorThreshold ?? 4
this.deadlyWound.threshold = threatConfig.deadlyThreshold ?? 6
this.minorWound.max = threatConfig.minorWound || 1
this.majorWound.max = threatConfig.majorWound || 1
this.deadlyWound.max = threatConfig.deadlyWound || 1
}
/**
* Définit les attributs dérivés (effort, sang-froid) selon le niveau de rôle.
* Utilise CONFIG.VERMINE.npcRoleLevels.
*/
_setNpcAttributes() {
const roleLevel = this.role?.value || 1
const roleConfig = CONFIG.VERMINE.npcRoleLevels[roleLevel] || {}
this.attributes.effort.max = roleConfig.pools || 0
this.attributes.self_control.max = roleConfig.reaction_bonus || 0
}
/**
* Définit les libellés localisés des caractéristiques.
*/
_setAbilityLabels() {
for (const [k, v] of Object.entries(this.abilities)) {
v.label = game.i18n.localize(CONFIG.VERMINE.abilities[k]) ?? k
}
}
/**
* Met à jour le label du statut de combat en fonction de la difficulté.
*/
+34 -10
View File
@@ -1,43 +1,67 @@
import { baseItemSchema } from "./_shared.mjs"
/**
* DataModel pour les items de type "vehicle" (véhicules et montures).
* Caractéristiques : Rareté, Fiabilité, Distance, Vitesse (croisière/pointe),
* Entretien (compétences) et Réserve d'Effort (montures).
* @augments {foundry.abstract.TypeDataModel}
* DataModel pour les acteurs de type "vehicle" (véhicules et montures).
* Un véhicule est désormais un acteur (et non un objet) : il possède sa propre
* fiche, peut être posé sur la scène comme un jeton et appartient à un groupe
* ou un personnage via `ownerId`.
*
* Caractéristiques (regles_vehicules.txt) : Rareté, Fiabilité (véhicules),
* Réserve d'Effort (montures), Distance, Vitesse (croisière/pointe) et Entretien.
* Pas de statistiques de combat (les règles ne définissent pas de points de vie).
*/
import { raritySchema } from "./_shared.mjs"
export default class VermineVehicleData extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["VERMINE.item.vehicle"]
static LOCALIZATION_PREFIXES = ["VERMINE.vehicle"]
/** @override */
static defineSchema() {
const fields = foundry.data.fields
const reqInt = { required: true, nullable: false, integer: true }
return {
...baseItemSchema(),
// Identité
identity: new fields.SchemaField({
profile: new fields.StringField({ required: true, nullable: false, initial: "" }),
origin: new fields.StringField({ required: true, nullable: false, initial: "" }),
theme: new fields.StringField({ required: true, nullable: false, initial: "" }),
notes: new fields.HTMLField({ required: true, initial: "", textSearch: true })
}),
// Rareté (valeur 1-10 + handicap)
rarity: raritySchema(),
// Fiabilité étendue (1-10) pour les véhicules
reliability: new fields.NumberField({ ...reqInt, initial: 5, min: 1, max: 10 }),
// Type : "vehicle" (véhicule) ou "mount" (monture)
kind: new fields.StringField({ required: true, nullable: false, initial: "vehicle" }),
// Distance : km parcourus avec un "plein"
distance: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
// Vitesse : croisière / pointe (durée de la pointe)
speed: new fields.SchemaField({
cruise: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
top: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
topDuration: new fields.StringField({ required: true, nullable: false, initial: "10 min" })
}),
// Entretien : compétences nécessaires (clés de CONFIG.VERMINE.skills)
maintenance: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })),
// Réserve d'Effort (montures)
effortReserve: new fields.SchemaField({
value: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
min: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
max: new fields.NumberField({ ...reqInt, initial: 0, min: 0 })
}),
// Mobilité (héritée)
mobility: new fields.NumberField({ ...reqInt, initial: 3, min: 0 })
// Mobilité
mobility: new fields.NumberField({ ...reqInt, initial: 3, min: 0 }),
// Propriétaire (id d'acteur : groupe ou personnage)
ownerId: new fields.StringField({ required: true, nullable: false, initial: "" })
}
}
}
+4 -4
View File
@@ -99,10 +99,10 @@ VERMINE.totemDomains = {
* NPC Threat Levels configuration
*/
VERMINE.npcThreatLevels = {
1: { "label": "THREAT_LEVELS.minor", "attack": 3, "vigor": 1, "minorWound": 1, "majorWound": 1, "deadlyWound": 1 },
2: { "label": "THREAT_LEVELS.serious", "attack": 4, "vigor": 2, "minorWound": 2, "majorWound": 1, "deadlyWound": 1 },
3: { "label": "THREAT_LEVELS.major", "attack": 5, "vigor": 3, "minorWound": 2, "majorWound": 1, "deadlyWound": 1 },
4: { "label": "THREAT_LEVELS.deadly", "attack": 6, "vigor": 4, "minorWound": 2, "majorWound": 2, "deadlyWound": 2 }
1: { "label": "THREAT_LEVELS.minor", "attack": 3, "vigor": 1, "minorThreshold": 1, "majorThreshold": 4, "deadlyThreshold": 6, "minorWound": 1, "majorWound": 1, "deadlyWound": 1 },
2: { "label": "THREAT_LEVELS.serious", "attack": 4, "vigor": 2, "minorThreshold": 2, "majorThreshold": 5, "deadlyThreshold": 8, "minorWound": 2, "majorWound": 1, "deadlyWound": 1 },
3: { "label": "THREAT_LEVELS.major", "attack": 5, "vigor": 3, "minorThreshold": 2, "majorThreshold": 5, "deadlyThreshold": 9, "minorWound": 2, "majorWound": 2, "deadlyWound": 1 },
4: { "label": "THREAT_LEVELS.deadly", "attack": 6, "vigor": 4, "minorThreshold": 3, "majorThreshold": 6, "deadlyThreshold": 9, "minorWound": 2, "majorWound": 2, "deadlyWound": 2 }
}
/**
+208
View File
@@ -0,0 +1,208 @@
import { VermineUtils } from "../roll.mjs"
const { HandlebarsApplicationMixin } = foundry.applications.api
/**
* Dialogue de jet simplifié pour les PNJ.
*
* Un PNJ n'a ni caractéristiques ni compétences : toutes ses actions sont
* résolues par une valeur unique dérivée de son Profil (Menace / Expérience /
* Rôle), exposée dans system.computed. Ce dialogue fixe le pool selon le type
* de jet (Action, Spécialité, Attaque ou Réaction) et n'offre que la difficulté,
* le handicap et les bonus simples.
*
* Utilisé par :
* - la fiche PNJ (boutons de jet rapide Action / Spécialité / Réaction) ;
* - la défense des PNJ dans les échanges de coups (mode verrouillé).
*/
export default class NpcRollDialog extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {
#actor
get title() {
return game.i18n.localize("VERMINE.roll")
}
static DEFAULT_OPTIONS = {
classes: ["vermine-roll"],
tag: "form",
window: {
icon: "fas fa-dice-d10",
resizable: false
},
position: {
width: 520,
height: 600
},
actions: {
roll: NpcRollDialog.#onRoll,
cancel: NpcRollDialog.#onCancel
}
}
static PARTS = {
main: { template: "systems/vermine2047/templates/dialogs/npc-roll-dialog.hbs" }
}
static async create(data = {}) {
const actor = data.actor instanceof Actor ? data.actor : await game.actors.get(data.actorId)
if (!actor) {
ui.notifications.warn(game.i18n.localize("VERMINE.error_no_actor_selected"))
return null
}
return new NpcRollDialog({
actor,
rolltype: data.rolltype ?? "action",
locks: data.locks ?? null,
defense: data.defense ?? null
})
}
constructor(options = {}) {
super(options)
this.#actor = options.actor
this.rolltype = options.rolltype ?? "action"
this.locks = options.locks ?? null
this.defense = options.defense ?? null
}
// ── Getters ──────────────────────────────────────────────────────────
get computed() { return this.#actor.system?.computed || {} }
/** Pool de dés selon le type de jet. */
get pool() {
const c = this.computed
switch (this.rolltype) {
case "specialty": return c.specialties || 0
case "attack": return c.attack || 0
case "reaction": return c.reaction || 0
default: return c.action || 3
}
}
/** Réussites automatiques (bonus de Réaction du rôle). */
get freeSuccesses() {
return this.rolltype === "reaction" ? (this.computed.reactionBonus || 0) : 0
}
/** Relances : utilisables uniquement sur les attaques ou les Spécialités. */
get rerolls() {
if (this.rolltype !== "specialty" && this.rolltype !== "attack") return 0
return this.computed.rerolls || 0
}
get label() {
const keys = {
action: "ADVERSITY.action",
specialty: "ADVERSITY.specialties",
attack: "ADVERSITY.attack",
reaction: "ADVERSITY.reaction"
}
return game.i18n.localize(keys[this.rolltype] || "VERMINE.roll")
}
// ── Rendu ────────────────────────────────────────────────────────────
async _prepareContext() {
const difficultyOptions = []
for (let d = 3; d <= 10; d++) {
difficultyOptions.push({ difficulty: d, label: String(d) })
}
let lockedDifficulty = null
let defaultDifficulty = 7
if (this.locks?.difficulty !== undefined && this.locks?.difficulty !== null) {
lockedDifficulty = parseInt(this.locks.difficulty, 10)
defaultDifficulty = lockedDifficulty
}
return {
actor: this.#actor,
system: this.#actor.system,
config: CONFIG.VERMINE,
rollLabel: this.label,
rolltype: this.rolltype,
pool: this.pool,
freeSuccesses: this.freeSuccesses,
rerolls: this.rerolls,
isDefense: Boolean(this.defense),
lockedDifficulty,
difficultyOptions,
defaultDifficulty,
speakerId: this.#actor.id,
availableItems: this.#actor.items.filter(i => i.type === "item")
}
}
async _onRender(context, options) {
this.element.dataset.actorId = this.#actor.id
for (const inp of this.element.querySelectorAll("[data-roll]")) {
inp.addEventListener("change", this.#onInputChange.bind(this))
}
this.element.querySelector("#handicap")?.addEventListener("change", () => this.#updateUI())
this.#updateUI()
}
get #el() { return this.element }
#getHandicap() {
const sel = this.#el.querySelector("#handicap")
return Math.max(0, (parseInt(sel?.value, 10) || 1) - 1)
}
#getDifficulty() {
const sel = this.#el.querySelector("#difficulty")
return parseInt(sel?.value, 10) || 7
}
#getHelped() { return this.#el.querySelector("#helped")?.checked ? 1 : 0 }
#getGroup() { return parseInt(this.#el.querySelector("#group")?.value, 10) || 0 }
getPoolBreakdown() {
const help = this.#getHelped()
const group = this.#getGroup()
return { pool: this.pool, help, group, total: this.pool + help + group }
}
#updateUI() {
const b = this.getPoolBreakdown()
const totalEl = this.#el.querySelector("#dice-pool-total")
if (totalEl) totalEl.textContent = `${b.total}D`
const bonusEl = this.#el.querySelector("#total-bonus")
if (bonusEl) bonusEl.textContent = b.help + b.group
const freeEl = this.#el.querySelector("#free-successes")
if (freeEl) freeEl.textContent = String(this.freeSuccesses)
}
#onInputChange() {
this.#updateUI()
}
// ── Actions ──────────────────────────────────────────────────────────
static async #onCancel(event, target) {
this.close()
}
static async #onRoll(event, target) {
const label = this.defense
? (this.defense.type === "parade"
? game.i18n.localize("VERMINE.defense_parade")
: game.i18n.localize("VERMINE.defense_esquive"))
: this.label
await VermineUtils.roll({
actor: this.#actor,
NoD: this.getPoolBreakdown().total,
Reroll: this.rerolls,
difficulty: this.#getDifficulty(),
handicap: this.#getHandicap(),
rollLabel: label,
bonusSuccesses: this.freeSuccesses,
poolBreakdown: this.getPoolBreakdown(),
messageFlags: this.defense ? { "vermine-defense": this.defense } : null
})
this.close()
}
}
+22 -8
View File
@@ -359,14 +359,7 @@ export class VermineExchange {
ui.notifications.warn(game.i18n.localize('VERMINE.error_creature_no_defense'))
return
}
const { default: RollDialog } = await import('./dialogs/rollDialog.mjs')
const locks = this.#defenseLocks(actor, exchange, type)
const dialog = await RollDialog.create({
actor,
rolltype: 'skill',
label: locks.skill,
locks,
defense: {
const defense = {
exchangeId: exchange.id,
type,
actorId: actor.id,
@@ -375,6 +368,27 @@ export class VermineExchange {
difficulty: exchange.difficulty,
parryLevel: type === 'parade' ? this.#parryLevel(actor) : null
}
// PNJ : pas de caractéristiques/compétences — la défense utilise la valeur
// d'Action (Expérience), difficulté verrouillée.
if (actor.type === 'npc') {
const { default: NpcRollDialog } = await import('./dialogs/npcRollDialog.mjs')
const dialog = await NpcRollDialog.create({
actor,
rolltype: 'action',
locks: { difficulty: exchange.difficulty },
defense
})
if (dialog) dialog.render(true)
return
}
const { default: RollDialog } = await import('./dialogs/rollDialog.mjs')
const locks = this.#defenseLocks(actor, exchange, type)
const dialog = await RollDialog.create({
actor,
rolltype: 'skill',
label: locks.skill,
locks,
defense
})
if (dialog) dialog.render(true)
}
+11 -4
View File
@@ -30,6 +30,7 @@ export class VermineUtils {
skillLevel = null,
hasSpecialty = false,
handicap = 0,
bonusSuccesses = 0,
poolBreakdown = null,
specialtyName = null,
weapon = null,
@@ -147,9 +148,9 @@ export class VermineUtils {
totemBonuses: { ...totemBonus },
baseNoD: NoD,
rerolls: Reroll,
selfControl: self_control
selfControl: self_control,
bonusSuccesses: bonusSuccesses
};
// Evaluate the roll
await roll.evaluate();
@@ -171,6 +172,7 @@ export class VermineUtils {
skillLevel,
hasSpecialty,
handicap,
bonusSuccesses,
poolBreakdown,
specialtyName,
weapon,
@@ -321,6 +323,9 @@ export class VermineUtils {
const isTotem = die.classList.contains('human') || die.classList.contains('adapted');
total += isTotem ? 2 : 1;
});
// Réussites automatiques (ex. bonus de Réaction) : ajoutées au total.
const freeSuccesses = parseInt(rollMessage.dataset?.freeSuccesses ?? '0', 10) || 0;
total += freeSuccesses;
totalEl.innerText = total;
}
@@ -488,11 +493,13 @@ export class VermineUtils {
}
// Verdict
const required = 1 + (param.handicap ?? 0);
const total = roll._total ?? 0;
const bonusSuccesses = param.bonusSuccesses ?? 0;
const total = (roll._total ?? 0) + bonusSuccesses;
const verdict = {
success: total >= required,
total,
required
required,
bonusSuccesses
};
const content = await foundry.applications.handlebars.renderTemplate("systems/vermine2047/templates/roll-message.hbs", { roll, param, diceStats, verdict });
+12 -10
View File
@@ -38,18 +38,20 @@ Hooks.once('init', async function () {
CONFIG.Actor.dataModels.group = models.VermineGroupData;
CONFIG.Actor.dataModels.creature = models.VermineCreatureData;
CONFIG.Actor.dataModels.community = models.VermineCommunityData;
CONFIG.Actor.dataModels.vehicle = models.VermineVehicleData;
// Libellé du type "community" (TYPES.Actor.* du core ne le connaît pas)
// Libellés des types "community"/"vehicle" (TYPES.Actor.* du core ne les connaît pas)
CONFIG.Actor.typeLabels ??= {};
CONFIG.Actor.typeLabels.community = "VERMINE.type_community";
CONFIG.Actor.typeLabels.vehicle = "VERMINE.type_vehicle";
Hooks.once('i18nInit', () => {
CONFIG.Actor.typeLabels.community = "VERMINE.type_community";
CONFIG.Actor.typeLabels.vehicle = "VERMINE.type_vehicle";
});
CONFIG.Item.dataModels.item = models.VermineItemData;
CONFIG.Item.dataModels.weapon = models.VermineWeaponData;
CONFIG.Item.dataModels.defense = models.VermineDefenseData;
CONFIG.Item.dataModels.vehicle = models.VermineVehicleData;
CONFIG.Item.dataModels.ability = models.VermineAbilityData;
CONFIG.Item.dataModels.specialty = models.VermineSpecialtyData;
CONFIG.Item.dataModels.background = models.VermineBackgroundData;
@@ -80,7 +82,7 @@ Hooks.once('init', async function () {
// Register sheet application classes (ApplicationV2)
// Unregister core sheets
foundry.documents.collections.Actors.unregisterSheet("core", foundry.applications.sheets.ActorSheetV2, {
types: ["character", "npc", "group", "creature"]
types: ["character", "npc", "group", "creature", "community", "vehicle"]
})
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1?.sheets?.ItemSheet)
@@ -100,6 +102,9 @@ Hooks.once('init', async function () {
foundry.documents.collections.Actors.registerSheet("vermine2047", sheets.VermineCommunitySheetV2, {
types: ["community"], makeDefault: true, label: "VERMINE.Sheet.community"
})
foundry.documents.collections.Actors.registerSheet("vermine2047", sheets.VermineVehicleSheetV2, {
types: ["vehicle"], makeDefault: true, label: "VERMINE.Sheet.vehicle"
})
// Item sheets — un par type
foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineItemSheetV2, {
@@ -111,9 +116,6 @@ Hooks.once('init', async function () {
foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineDefenseSheetV2, {
types: ["defense"], makeDefault: true
})
foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineVehicleSheetV2, {
types: ["vehicle"], makeDefault: true
})
foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineAbilitySheetV2, {
types: ["ability"], makeDefault: true
})
@@ -189,10 +191,8 @@ Hooks.once('init', async function () {
"systems/vermine2047/templates/actor/appv2/character-stories.hbs",
"systems/vermine2047/templates/actor/appv2/character-combat.hbs",
"systems/vermine2047/templates/actor/appv2/npc-main.hbs",
"systems/vermine2047/templates/actor/appv2/npc-characteristics.hbs",
"systems/vermine2047/templates/actor/appv2/npc-skills.hbs",
"systems/vermine2047/templates/actor/appv2/npc-traits.hbs",
"systems/vermine2047/templates/actor/appv2/npc-equipment.hbs",
"systems/vermine2047/templates/actor/appv2/npc-threat.hbs",
"systems/vermine2047/templates/actor/appv2/npc-combat.hbs",
"systems/vermine2047/templates/actor/appv2/npc-notes.hbs",
"systems/vermine2047/templates/actor/appv2/group-main.hbs",
@@ -208,7 +208,9 @@ Hooks.once('init', async function () {
"systems/vermine2047/templates/actor/appv2/community-main.hbs",
"systems/vermine2047/templates/actor/appv2/community-identity.hbs",
"systems/vermine2047/templates/actor/appv2/community-domains.hbs",
"systems/vermine2047/templates/actor/appv2/community-resources.hbs"
"systems/vermine2047/templates/actor/appv2/community-resources.hbs",
"systems/vermine2047/templates/actor/appv2/vehicle-main.hbs",
"systems/vermine2047/templates/actor/appv2/vehicle-specs.hbs"
]);
});
+2 -2
View File
@@ -35,13 +35,13 @@
"npc": { "htmlFields": ["equipment.description", "identity.notes"] },
"group": { "htmlFields": ["equipment.description", "identity.notes"] },
"creature": { "htmlFields": ["equipment.description", "identity.notes", "identity.biography"] },
"community": { "htmlFields": ["identity.notes", "location.landmarks", "location.territory", "history.events", "history.relations"] }
"community": { "htmlFields": ["identity.notes", "location.landmarks", "location.territory", "history.events", "history.relations"] },
"vehicle": { "htmlFields": ["identity.notes"] }
},
"Item": {
"item": { "htmlFields": ["description"] },
"weapon": { "htmlFields": ["description"] },
"defense": { "htmlFields": ["description"] },
"vehicle": { "htmlFields": ["description"] },
"ability": { "htmlFields": ["description"] },
"specialty": {},
"background": { "htmlFields": ["description"] },
@@ -86,6 +86,47 @@
{{/each}}
</ol>
</div>
<div class="vehicles">
<h4>{{localize 'ITEMS.vehicles'}}</h4>
<ol class="items-list">
<li class="item flexrow items-header">
<div class="item-name" style="flex:4;">{{ localize 'IDENTITY.name'}}</div>
<div class="item-mobility">{{ localize 'VERMINE.mobility'}}</div>
<div class="item-rarity">{{ localize 'VERMINE.rarity'}}</div>
<div class="item-reliability">{{ localize 'VERMINE.reliability'}}</div>
<div class="item-controls">
{{#if isEditMode}}
<a class="item-control item-create" data-action="createVehicle" title="Create vehicle"><i class="fas fa-plus"></i></a>
{{/if}}
</div>
</li>
{{#each vehicles as |vehicle|}}
<li class="item flexrow flex-group-center" data-vehicle-id="{{vehicle.id}}">
<div class="item-name" style="flex:4;">
<div class="item-image">
<a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}"><img
src="{{vehicle.img}}" title="{{vehicle.name}}" width="24"
height="24" /></a>
</div>
<a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.name}}</a>
</div>
<div><a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.system.mobility}}</a></div>
<div><a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.system.rarity.value}}</a></div>
<div><a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.system.reliability}}</a></div>
<div class="item-controls">
{{#if @root.isEditMode}}
<a class="item-control item-delete" data-action="deleteVehicle" data-vehicle-id="{{vehicle.id}}" title="Delete Vehicle"><i
class="fas fa-trash"></i></a>
{{/if}}
</div>
</li>
{{else}}
<li class="item flexrow flex-group-center">
<span class="empty">{{ localize 'VERMINE.no_vehicles' }}</span>
</li>
{{/each}}
</ol>
</div>
<div class="equipment-notes">
<h4>{{ localize 'IDENTITY.notes'}}</h4>
{{formInput systemFields.equipment.fields.description enriched=enrichedEquipmentNotes value=system.equipment.description name="system.equipment.description" toggled=true}}
+14 -11
View File
@@ -7,31 +7,34 @@
<div class="item-reliability">{{ localize 'VERMINE.reliability'}}</div>
<div class="item-controls">
{{#if isEditMode}}
<a class="item-control item-create" data-action="create" title="Create item"
data-type="vehicle"><i class="fas fa-plus"></i></a>
<a class="item-control item-create" data-action="createVehicle" title="Create vehicle"><i class="fas fa-plus"></i></a>
{{/if}}
</div>
</li>
{{#each vehicles as |item id|}}
<li class="item flexrow flex-group-center" data-item-id="{{item._id}}">
{{#each vehicles as |vehicle|}}
<li class="item flexrow flex-group-center" data-vehicle-id="{{vehicle.id}}">
<div class="item-name" style="flex:4;">
<div class="item-image">
<a class="item-control item-edit" data-action="edit" data-item-id="{{item._id}}" data-roll-type="item"><img
src="{{item.img}}" title="{{item.name}}" width="24"
<a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}"><img
src="{{vehicle.img}}" title="{{vehicle.name}}" width="24"
height="24" /></a>
</div>
<a class="item-control item-edit" data-action="edit" data-item-id="{{item._id}}" title="Edit Item">{{item.name}}</a>
<a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.name}}</a>
</div>
<div><a class="item-control item-edit" data-action="edit" data-item-id="{{item._id}}" title="Edit Item">{{item.system.mobility}}</a></div>
<div><a class="item-control item-edit" data-action="edit" data-item-id="{{item._id}}" title="Edit Item">{{item.system.rarity.value}}</a></div>
<div><a class="item-control item-edit" data-action="edit" data-item-id="{{item._id}}" title="Edit Item">{{item.system.reliability}}</a></div>
<div><a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.system.mobility}}</a></div>
<div><a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.system.rarity.value}}</a></div>
<div><a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.system.reliability}}</a></div>
<div class="item-controls">
{{#if @root.isEditMode}}
<a class="item-control item-delete" data-action="delete" data-item-id="{{item._id}}" title="Delete Item"><i
<a class="item-control item-delete" data-action="deleteVehicle" data-vehicle-id="{{vehicle.id}}" title="Delete Vehicle"><i
class="fas fa-trash"></i></a>
{{/if}}
</div>
</li>
{{else}}
<li class="item flexrow flex-group-center">
<span class="empty">{{ localize 'VERMINE.no_vehicles' }}</span>
</li>
{{/each}}
</ol>
</div>
@@ -1,116 +0,0 @@
<div class="tab characteristics sheet-part {{#if @root.tabs.characteristics.active}}active{{/if}}" data-group="sheet" data-tab="characteristics">
<h3>{{ localize 'VERMINE.abilities' }}</h3>
{{!-- Caracteristiques physiques --}}
<div class="ability-section">
<h4 class="ability-category-header">{{ localize 'VERMINE.ability_category.physical' }}</h4>
<div class="grid grid-2col">
<div class="ability-item">
<label for="system.abilities.vigor.value" class="resource-label rollable" data-type="ability" data-label="vigor">{{ localize 'ABILITIES.vigor.name' }}</label>
<div class="ability-control">
{{#if isEditMode}}
<input type="range" name="system.abilities.vigor.value" value="{{system.abilities.vigor.value}}" min="{{system.abilities.vigor.min}}" max="{{system.abilities.vigor.max}}" data-dtype="Number"/>
{{/if}}
<span class="ability-value">{{system.abilities.vigor.value}}D</span>
</div>
</div>
<div class="ability-item">
<label for="system.abilities.health.value" class="resource-label rollable" data-type="ability" data-label="health">{{ localize 'ABILITIES.health.name' }}</label>
<div class="ability-control">
{{#if isEditMode}}
<input type="range" name="system.abilities.health.value" value="{{system.abilities.health.value}}" min="{{system.abilities.health.min}}" max="{{system.abilities.health.max}}" data-dtype="Number"/>
{{/if}}
<span class="ability-value">{{system.abilities.health.value}}D</span>
</div>
</div>
</div>
</div>
{{!-- Caracteristiques manuelles --}}
<div class="ability-section">
<h4 class="ability-category-header">{{ localize 'VERMINE.ability_category.manual' }}</h4>
<div class="grid grid-2col">
<div class="ability-item">
<label for="system.abilities.precision.value" class="resource-label rollable" data-type="ability" data-label="precision">{{ localize 'ABILITIES.precision.name' }}</label>
<div class="ability-control">
{{#if isEditMode}}
<input type="range" name="system.abilities.precision.value" value="{{system.abilities.precision.value}}" min="{{system.abilities.precision.min}}" max="{{system.abilities.precision.max}}" data-dtype="Number"/>
{{/if}}
<span class="ability-value">{{system.abilities.precision.value}}D</span>
</div>
</div>
<div class="ability-item">
<label for="system.abilities.reflexes.value" class="resource-label rollable" data-type="ability" data-label="reflexes">{{ localize 'ABILITIES.reflexes.name' }}</label>
<div class="ability-control">
{{#if isEditMode}}
<input type="range" name="system.abilities.reflexes.value" value="{{system.abilities.reflexes.value}}" min="{{system.abilities.reflexes.min}}" max="{{system.abilities.reflexes.max}}" data-dtype="Number"/>
{{/if}}
<span class="ability-value">{{system.abilities.reflexes.value}}D</span>
</div>
</div>
</div>
</div>
{{!-- Caracteristiques mentales --}}
<div class="ability-section">
<h4 class="ability-category-header">{{ localize 'VERMINE.ability_category.mental' }}</h4>
<div class="grid grid-2col">
<div class="ability-item">
<label for="system.abilities.knowledge.value" class="resource-label rollable" data-type="ability" data-label="knowledge">{{ localize 'ABILITIES.knowledge.name' }}</label>
<div class="ability-control">
{{#if isEditMode}}
<input type="range" name="system.abilities.knowledge.value" value="{{system.abilities.knowledge.value}}" min="{{system.abilities.knowledge.min}}" max="{{system.abilities.knowledge.max}}" data-dtype="Number"/>
{{/if}}
<span class="ability-value">{{system.abilities.knowledge.value}}D</span>
</div>
</div>
<div class="ability-item">
<label for="system.abilities.perception.value" class="resource-label rollable" data-type="ability" data-label="perception">{{ localize 'ABILITIES.perception.name' }}</label>
<div class="ability-control">
{{#if isEditMode}}
<input type="range" name="system.abilities.perception.value" value="{{system.abilities.perception.value}}" min="{{system.abilities.perception.min}}" max="{{system.abilities.perception.max}}" data-dtype="Number"/>
{{/if}}
<span class="ability-value">{{system.abilities.perception.value}}D</span>
</div>
</div>
</div>
</div>
{{!-- Caracteristiques sociales --}}
<div class="ability-section">
<h4 class="ability-category-header">{{ localize 'VERMINE.ability_category.social' }}</h4>
<div class="grid grid-2col">
<div class="ability-item">
<label for="system.abilities.will.value" class="resource-label rollable" data-type="ability" data-label="will">{{ localize 'ABILITIES.will.name' }}</label>
<div class="ability-control">
{{#if isEditMode}}
<input type="range" name="system.abilities.will.value" value="{{system.abilities.will.value}}" min="{{system.abilities.will.min}}" max="{{system.abilities.will.max}}" data-dtype="Number"/>
{{/if}}
<span class="ability-value">{{system.abilities.will.value}}D</span>
</div>
</div>
<div class="ability-item">
<label for="system.abilities.empathy.value" class="resource-label rollable" data-type="ability" data-label="empathy">{{ localize 'ABILITIES.empathy.name' }}</label>
<div class="ability-control">
{{#if isEditMode}}
<input type="range" name="system.abilities.empathy.value" value="{{system.abilities.empathy.value}}" min="{{system.abilities.empathy.min}}" max="{{system.abilities.empathy.max}}" data-dtype="Number"/>
{{/if}}
<span class="ability-value">{{system.abilities.empathy.value}}D</span>
</div>
</div>
</div>
</div>
{{!-- Reserves --}}
<div class="ability-section">
<h4 class="ability-category-header">{{ localize 'VERMINE.reserves' }}</h4>
<div class="grid grid-2col">
<div class="ability-item">
<span>{{ localize 'VERMINE.effort' }} : {{system.attributes.effort.value}}/{{system.attributes.effort.max}}D</span>
</div>
<div class="ability-item">
<span>{{ localize 'VERMINE.self_control' }} : {{system.attributes.self_control.value}}/{{system.attributes.self_control.max}}D</span>
</div>
</div>
</div>
</div>
+27 -40
View File
@@ -1,56 +1,43 @@
<div class="tab combat sheet-part {{#if @root.tabs.combat.active}}active{{/if}}" data-group="sheet" data-tab="combat">
<div class="grid grid-2col">
<div>
<h4 class="align-center">{{ localize "VERMINE.self_control"}}</h4>
<p class="align-center">
{{#if isEditMode}}
<input type="number" name="system.attributes.self_control.value"
value="{{ system.attributes.self_control.value }}" data-dtype="Number"
min="{{ system.attributes.self_control.min }}"
max="{{ system.attributes.self_control.max }}" />
{{/if}}
{{ system.attributes.self_control.value }} / {{
system.attributes.self_control.max }}
</p>
</div>
<div>
<h4 class="align-center">{{ localize "VERMINE.effort"}}</h4>
<p class="align-center">
{{#if isEditMode}}
<input type="number" name="system.attributes.effort.value"
value="{{ system.attributes.effort.value }}" data-dtype="Number"
min="{{ system.attributes.effort.min }}"
max="{{ system.attributes.effort.max }}" />
{{/if}}
{{ system.attributes.effort.value }} / {{
system.attributes.effort.max }}
</p>
</div>
</div>
{{> "systems/vermine2047/templates/actor/parts/actor-equipped-gear.hbs"}}
<h4 class="item-name effect-name flexrow">{{ localize
"VERMINE.wounds.name"}}</h4>
<h4 class="item-name effect-name flexrow">{{ localize "VERMINE.wounds.name"}}</h4>
<ul class="unstyled">
<li class="row mdb">{{ localize 'VERMINE.wounds.light'}} ({{
system.minorWound.threshold }})
{{#repeat system.minorWound.max }}
<div class="flexrow align-center">
{{#repeat system.minorWound.max 1 "minorwoundmax"}}
<div class="hexa ability{{#iflteq @minorwoundmax @root.system.minorWound.value }} checked{{/iflteq}}">
<input type="radio" data-dtype="Number" name="system.minorWound.value"
value="{{this}}" data-action="clickRadio" data-wound="minorWound"
{{#ife @root.system.minorWound.value this }}checked="checked"{{/ife}} />
{{/repeat}}</li>
value="{{@minorwoundmax}}" data-action="clickRadio" data-wound="minorWound"
class="{{#iflteq @minorwoundmax @root.system.minorWound.value }}checked{{/iflteq}}"
{{#ife @minorwoundmax @root.system.minorWound.value }}checked="checked"{{/ife}} />
</div>
{{/repeat}}
</div>
</li>
<li class="row mdb">{{ localize 'VERMINE.wounds.heavy'}} ({{
system.majorWound.threshold }})
{{#repeat system.majorWound.max }}
<input type="radio" name="system.majorWound.value" value="{{this}}" data-action="clickRadio" data-wound="majorWound"
{{#ife @root.system.majorWound.value this }}checked="checked"{{/ife}} />
<div class="flexrow align-center">
{{#repeat system.majorWound.max 1 "majorwoundmax"}}
<div class="hexa ability{{#iflteq @majorwoundmax @root.system.majorWound.value }} checked{{/iflteq}}">
<input type="radio" name="system.majorWound.value" value="{{@majorwoundmax}}" data-action="clickRadio" data-wound="majorWound"
class="{{#iflteq @majorwoundmax @root.system.majorWound.value }}checked{{/iflteq}}"
{{#ife @majorwoundmax @root.system.majorWound.value }}checked="checked"{{/ife}} />
</div>
{{/repeat}}
</div>
</li>
<li class="row mdb">{{ localize 'VERMINE.wounds.deadly'}} ({{
system.deadlyWound.threshold }})
{{#repeat system.deadlyWound.max }}
<input type="radio" name="system.deadlyWound.value" value="{{this}}" data-action="clickRadio" data-wound="deadlyWound"
{{#ife @root.system.deadlyWound.value this }}checked="checked"{{/ife}} />
<div class="flexrow align-center">
{{#repeat system.deadlyWound.max 1 "deadlywoundmax"}}
<div class="hexa ability{{#iflteq @deadlywoundmax @root.system.deadlyWound.value }} checked{{/iflteq}}">
<input type="radio" name="system.deadlyWound.value" value="{{@deadlywoundmax}}" data-action="clickRadio" data-wound="deadlyWound"
class="{{#iflteq @deadlywoundmax @root.system.deadlyWound.value }}checked{{/iflteq}}"
{{#ife @deadlywoundmax @root.system.deadlyWound.value }}checked="checked"{{/ife}} />
</div>
{{/repeat}}
</div>
</li>
</ul>
<h4 class="item-name effect-name flexrow">{{ localize "UI.effects.name"}}</h4>
+26 -6
View File
@@ -20,7 +20,7 @@
{{/if}}
</h1>
{{!-- Niveaux de Menace, Experience, Role --}}
{{!-- Critères : Menace, Expérience, Rôle --}}
<div class="resources grid grid-3col">
<div class="resource flex-group-center">
<label for="system.threat.value" class="resource-label">{{ localize 'ADVERSITY.threat' }}</label>
@@ -29,7 +29,7 @@
<select name="system.threat.value" id="system.threat.value" data-dtype="Number">
{{#each npcThreatOptions}}
<option value="{{@key}}" {{#ife @key @root.system.threat.value}}selected{{/ife}}>
{{localize label}} ({{@key}})
{{localize label}}
</option>
{{/each}}
</select>
@@ -46,7 +46,7 @@
<select name="system.experience.value" id="system.experience.value" data-dtype="Number">
{{#each npcExperienceOptions}}
<option value="{{@key}}" {{#ife @key @root.system.experience.value}}selected{{/ife}}>
{{localize label}} ({{@key}})
{{localize label}}
</option>
{{/each}}
</select>
@@ -63,7 +63,7 @@
<select name="system.role.value" id="system.role.value" data-dtype="Number">
{{#each npcRoleOptions}}
<option value="{{@key}}" {{#ife @key @root.system.role.value}}selected{{/ife}}>
{{localize label}} ({{@key}})
{{localize label}}
</option>
{{/each}}
</select>
@@ -74,8 +74,28 @@
</div>
</div>
{{!-- Totem et Origine --}}
<div class="resources grid grid-2col">
{{!-- Profil : âge, activité, totem, origine --}}
<div class="resources grid grid-4col npc-profile-grid">
<div class="resource flex-group-center">
<label for="system.identity.age" class="resource-label">{{ localize 'IDENTITY.age' }}</label>
<div class="resource-content">
{{#if isEditMode}}
<input type="text" name="system.identity.age" id="system.identity.age" value="{{system.identity.age}}" data-dtype="String"/>
{{else}}
<span>{{system.identity.age}}</span>
{{/if}}
</div>
</div>
<div class="resource flex-group-center">
<label for="system.identity.profile" class="resource-label">{{ localize 'IDENTITY.profile' }}</label>
<div class="resource-content">
{{#if isEditMode}}
<input type="text" name="system.identity.profile" id="system.identity.profile" value="{{system.identity.profile}}" data-dtype="String" placeholder="{{ localize 'PROFILE' }}"/>
{{else}}
<span>{{system.identity.profile}}</span>
{{/if}}
</div>
</div>
<div class="resource flex-group-center">
<label for="system.identity.totem" class="resource-label">{{ localize 'TOTEM' }}</label>
<div class="resource-content">
-45
View File
@@ -1,45 +0,0 @@
<div class="tab skills sheet-part {{#if @root.tabs.skills.active}}active{{/if}}" data-group="sheet" data-tab="skills">
<h3>{{ localize 'VERMINE.skills' }}</h3>
{{!-- Category Selector --}}
<div class="skill-category-selector">
<label>{{ localize 'VERMINE.preferred_category' }}:</label>
{{#if isEditMode}}
<select name="system.skill_categories.preferred" data-dtype="String">
<option value="">{{ localize 'NONE' }}</option>
{{#each system.skill_categories as |category key|}}
{{#ife key "preferred"}}{{else}}
<option value="{{key}}" {{#ife key ../system.skill_categories.preferred}}selected{{/ife}}>
{{localize category.label}}
</option>
{{/ife}}
{{/each}}
</select>
{{else}}
<span>{{system.skill_categories.preferred}}</span>
{{/if}}
</div>
{{!-- Skills Grid by Category --}}
<div class="skills-container">
{{#each config.skillCategories as |category key|}}
{{#ife key "preferred"}}{{else}}
{{> "systems/vermine2047/templates/actor/parts/npc-skill-category.hbs"
categoryKey=key
categoryLabel=(concat "VERMINE.skill_category." key)
skills=@root.system.skills
}}
{{/ife}}
{{/each}}
</div>
{{!-- Free Skills text field --}}
<div class="form-group">
<label for="system.freeSkills">{{ localize 'ADVERSITY.skills' }}</label>
{{#if isEditMode}}
<input type="text" name="system.freeSkills" value="{{system.freeSkills}}" data-dtype="String" placeholder="{{ localize 'ADVERSITY.skills_placeholder' }}"/>
{{else}}
<span>{{system.freeSkills}}</span>
{{/if}}
</div>
</div>
-75
View File
@@ -1,75 +0,0 @@
<div class="tab threat sheet-part {{#if @root.tabs.threat.active}}active{{/if}}" data-group="sheet" data-tab="threat">
<h3>{{ localize 'ADVERSITY.threat_details' }}</h3>
<div class="grid grid-3col">
{{!-- Menace --}}
<div class="card npc-card">
<h4 class="align-center"><i class="fas fa-exclamation-triangle"></i> {{ localize 'ADVERSITY.threat' }}</h4>
<ul class="unstyled">
<li>
<strong>{{ localize 'ADVERSITY.attack' }}:</strong>
{{ npcThreatLevel "attack" system.threat.value }}
</li>
<li>
<strong>{{ localize 'ADVERSITY.vigor' }}:</strong>
{{ npcThreatLevel "vigor" system.threat.value }}
</li>
<li>
<strong>{{ localize 'ADVERSITY.wounds' }}:</strong>
{{ npcThreatLevel "minorWound" system.threat.value }} /
{{ npcThreatLevel "majorWound" system.threat.value }} /
{{ npcThreatLevel "deadlyWound" system.threat.value }}
</li>
</ul>
</div>
{{!-- Experience --}}
<div class="card npc-card">
<h4 class="align-center"><i class="fas fa-star"></i> {{ localize 'ADVERSITY.experience' }}</h4>
<ul class="unstyled">
<li>
<strong>{{ localize 'ADVERSITY.action' }}:</strong>
{{ npcExperienceLevel "action" system.experience.value }}D
</li>
<li>
<strong>{{ localize 'ADVERSITY.specialties' }}:</strong>
{{ npcExperienceLevel "specialties" system.experience.value }}
</li>
<li>
<strong>{{ localize 'ADVERSITY.rerolls' }}:</strong>
{{ npcExperienceLevel "rerolls" system.experience.value }}D
</li>
<li>
<strong>{{ localize 'ADVERSITY.contact' }}:</strong>
{{ npcExperienceLevel "contact" system.experience.value }}
</li>
</ul>
</div>
{{!-- Role --}}
<div class="card npc-card">
<h4 class="align-center"><i class="fas fa-users"></i> {{ localize 'ADVERSITY.role' }}</h4>
<ul class="unstyled">
<li>
<strong>{{ localize 'ADVERSITY.reaction' }}:</strong>
{{ npcRoleLevel "reaction" system.role.value }} + {{ npcRoleLevel "reaction_bonus" system.role.value }}
</li>
<li>
<strong>{{ localize 'ADVERSITY.pools' }}:</strong>
{{ npcRoleLevel "pools" system.role.value }}D
</li>
<li>
<strong>{{ localize 'ADVERSITY.gear' }}:</strong>
{{ npcRoleLevel "gear" system.role.value }}
</li>
<li>
<strong>{{ localize 'ADVERSITY.gear_hindrance' }}:</strong>
{{ npcRoleLevel "gear_hindrance" system.role.value }}
</li>
<li>
<strong>{{ localize 'ADVERSITY.protection' }}:</strong>
{{ npcRoleLevel "protection" system.role.value }}
</li>
</ul>
</div>
</div>
</div>
+86
View File
@@ -0,0 +1,86 @@
<div class="tab traits sheet-part {{#if @root.tabs.traits.active}}active{{/if}}" data-group="sheet" data-tab="traits">
<h3>{{ localize 'VERMINE.traits' }}</h3>
<div class="grid grid-3col">
{{!-- Menace --}}
<div class="card npc-card">
<h4 class="align-center"><i class="fas fa-exclamation-triangle"></i> {{ localize 'ADVERSITY.threat' }}</h4>
<ul class="unstyled npc-trait-list">
<li>
<strong>{{ localize 'ADVERSITY.attack' }}:</strong>
<span class="trait-value">{{ system.computed.attack }}D</span>
<a class="rollable npc-roll-btn" data-type="attack" title="{{ localize 'VERMINE.roll' }}"><i class="fas fa-dice-d10"></i></a>
</li>
<li>
<strong>{{ localize 'ADVERSITY.vigor' }}:</strong>
<span class="trait-value">{{ system.computed.vigor }}D</span>
</li>
<li class="npc-wounds">
<strong>{{ localize 'ADVERSITY.wounds' }}:</strong>
<span class="wound-cat"><span class="wound-boxes">{{#repeat system.minorWound.max}}<i class="fas fa-circle"></i>{{/repeat}}</span> {{ system.minorWound.threshold }}</span>
<span class="wound-cat"><span class="wound-boxes">{{#repeat system.majorWound.max}}<i class="fas fa-circle"></i>{{/repeat}}</span> {{ system.majorWound.threshold }}</span>
<span class="wound-cat"><span class="wound-boxes">{{#repeat system.deadlyWound.max}}<i class="fas fa-circle"></i>{{/repeat}}</span> {{ system.deadlyWound.threshold }}</span>
</li>
</ul>
</div>
{{!-- Expérience --}}
<div class="card npc-card">
<h4 class="align-center"><i class="fas fa-star"></i> {{ localize 'ADVERSITY.experience' }}</h4>
<ul class="unstyled npc-trait-list">
<li>
<strong>{{ localize 'ADVERSITY.action' }}:</strong>
<span class="trait-value">{{ system.computed.action }}D</span>
<a class="rollable npc-roll-btn" data-type="action" title="{{ localize 'VERMINE.roll' }}"><i class="fas fa-dice-d10"></i></a>
</li>
<li>
<strong>{{ localize 'ADVERSITY.specialties' }}:</strong>
<span class="trait-value">{{ system.computed.specialties }}D</span>
<a class="rollable npc-roll-btn" data-type="specialty" title="{{ localize 'VERMINE.roll' }}"><i class="fas fa-dice-d10"></i></a>
</li>
<li>
<strong>{{ localize 'ADVERSITY.rerolls' }}:</strong>
<span class="trait-value">{{ system.computed.rerolls }}D</span>
</li>
<li>
<strong>{{ localize 'ADVERSITY.contact' }}:</strong>
<span class="trait-value">{{ system.computed.contact }}</span>
</li>
</ul>
</div>
{{!-- Rôle --}}
<div class="card npc-card">
<h4 class="align-center"><i class="fas fa-users"></i> {{ localize 'ADVERSITY.role' }}</h4>
<ul class="unstyled npc-trait-list">
<li>
<strong>{{ localize 'ADVERSITY.reaction' }}:</strong>
<span class="trait-value">{{ system.computed.reaction }}D{{#if system.computed.reactionBonus}} +{{ system.computed.reactionBonus }}{{/if}}</span>
<a class="rollable npc-roll-btn" data-type="reaction" title="{{ localize 'VERMINE.roll' }}"><i class="fas fa-dice-d10"></i></a>
</li>
<li>
<strong>{{ localize 'ADVERSITY.pools' }}:</strong>
<span class="trait-value">{{ system.computed.pools }}D</span>
</li>
<li>
<strong>{{ localize 'ADVERSITY.gear' }}:</strong>
<span class="trait-value">{{ system.computed.gear }}{{#if system.computed.gearHindrance}} ({{ system.computed.gearHindrance }}){{/if}}</span>
</li>
<li>
<strong>{{ localize 'ADVERSITY.protection' }}:</strong>
<span class="trait-value">{{ system.computed.protection }}</span>
</li>
</ul>
</div>
</div>
{{!-- Spécialités (description libre) --}}
<div class="form-group npc-free-skills">
<label for="system.freeSkills">{{ localize 'ADVERSITY.skills' }}</label>
{{#if isEditMode}}
<input type="text" name="system.freeSkills" id="system.freeSkills" value="{{system.freeSkills}}" data-dtype="String" placeholder="{{ localize 'ADVERSITY.skills_placeholder' }}"/>
{{else}}
<span>{{system.freeSkills}}</span>
{{/if}}
</div>
</div>
+94
View File
@@ -0,0 +1,94 @@
<div class="tab main sheet-part" data-group="sheet" data-tab="main">
{{!-- Toggle Edit/Play --}}
<div class="sheet-header-toggle">
{{#if isEditMode}}
<button type="button" data-action="toggleSheet">{{localize "VERMINE.playMode"}}</button>
{{else}}
<button type="button" data-action="toggleSheet">{{localize "VERMINE.editMode"}}</button>
{{/if}}
</div>
{{!-- HEADER --}}
<header class="sheet-header">
<img class="profile-img" src="{{actor.img}}" data-action="editImage" data-edit="img" title="{{actor.name}}"
height="100" width="100" />
<div class="header-fields">
<div class="resources grid grid-4col">
<h1 class="charname grid-span-2">
{{#if isEditMode}}
<input name="name" type="text" value="{{actor.name}}" placeholder="{{ localize 'VERMINE.vehicle' }}" />
{{else}}
<span>{{actor.name}}</span>
{{/if}}
</h1>
<div class="resource flex-group-center grid-span-1">
<label for="system.kind" class="resource-label">{{ localize 'VERMINE.kind' }}</label>
<div class="resource-content">
{{#if isEditMode}}
<select name="system.kind" id="system.kind" data-dtype="String">
<option value="vehicle" {{#ife system.kind "vehicle"}}selected{{/ife}}>{{ localize "VERMINE.kind_vehicle" }}</option>
<option value="mount" {{#ife system.kind "mount"}}selected{{/ife}}>{{ localize "VERMINE.kind_mount" }}</option>
</select>
{{else}}
<span>{{#ife system.kind "mount"}}{{ localize "VERMINE.kind_mount" }}{{else}}{{ localize "VERMINE.kind_vehicle" }}{{/ife}}</span>
{{/if}}
</div>
</div>
<div class="resource flex-group-center grid-span-1">
<label for="system.reliability" class="resource-label">{{ localize 'VERMINE.reliability' }}</label>
<div class="resource-content">
{{#if isEditMode}}
<input type="number" name="system.reliability" id="system.reliability" value="{{system.reliability}}"
data-dtype="Number" min="1" max="10" />
{{else}}
<span>{{system.reliability}}</span>
{{/if}}
</div>
</div>
</div>
<div class="resources grid grid-4col">
<div class="resource flex-group-center grid-span-1">
<label for="system.rarity.value" class="resource-label">{{ localize 'VERMINE.rarity' }}</label>
<div class="resource-content">
{{#if isEditMode}}
<input type="number" name="system.rarity.value" id="system.rarity.value" value="{{system.rarity.value}}"
data-dtype="Number" min="1" max="10" />
{{else}}
<span>{{system.rarity.value}}</span>
{{/if}}
</div>
</div>
<div class="resource flex-group-center grid-span-1">
<label for="system.mobility" class="resource-label">{{ localize 'VERMINE.mobility' }}</label>
<div class="resource-content">
{{#if isEditMode}}
<input type="number" name="system.mobility" id="system.mobility" value="{{system.mobility}}"
data-dtype="Number" min="0" />
{{else}}
<span>{{system.mobility}}</span>
{{/if}}
</div>
</div>
<div class="resource flex-group-center grid-span-2">
<label for="system.distance" class="resource-label">{{ localize 'VERMINE.distance' }}</label>
<div class="resource-content">
{{#if isEditMode}}
<input type="number" name="system.distance" id="system.distance" value="{{system.distance}}"
data-dtype="Number" min="0" />
{{else}}
<span>{{system.distance}}</span>
{{/if}}
<span> km</span>
</div>
</div>
</div>
</div>
</header>
{{#if resolvedOwner}}
<div class="vehicle-owner">
<span class="label">{{ localize 'VERMINE.owner' }}:</span>
<span class="value">{{ resolvedOwner.name }}</span>
</div>
{{/if}}
</div>
+83
View File
@@ -0,0 +1,83 @@
<div class="tab specs sheet-part {{#if @root.tabs.specs.active}}active{{/if}}" data-group="sheet" data-tab="specs">
<section class="vehicle-section">
<h3>{{ localize 'VERMINE.vehicle_specs' }}</h3>
<div class="grid grid-2col">
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize 'VERMINE.distance' }}</label>
<div class="flexrow align-center">
<div class="hexa"><input type="number" name="system.distance" value="{{system.distance}}" data-dtype="Number" /></div>
<span class="unit">km</span>
</div>
</div>
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize 'VERMINE.speed' }}</label>
<div class="flexrow align-center">
<div class="hexa"><input type="number" name="system.speed.cruise" value="{{system.speed.cruise}}" data-dtype="Number" /></div>
<span>/</span>
<div class="hexa"><input type="number" name="system.speed.top" value="{{system.speed.top}}" data-dtype="Number" /></div>
</div>
<input type="text" name="system.speed.topDuration" value="{{system.speed.topDuration}}"
placeholder="{{ localize 'VERMINE.speed_duration_ph' }}" />
</div>
</div>
<div class="grid grid-2col">
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize 'VERMINE.effort_reserve' }}</label>
<div class="flexrow align-center">
<div class="hexa"><input type="number" name="system.effortReserve.value" value="{{system.effortReserve.value}}" data-dtype="Number" /></div>
<span>/</span>
<div class="hexa"><input type="number" name="system.effortReserve.max" value="{{system.effortReserve.max}}" data-dtype="Number" /></div>
</div>
</div>
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize 'VERMINE.mobility' }}</label>
<div class="hexa"><input type="number" name="system.mobility" value="{{system.mobility}}" data-dtype="Number" /></div>
</div>
</div>
<div class="maintenance">
<label class="resource-label">{{ localize 'VERMINE.maintenance' }}</label>
<div class="flexrow flex-wrap">
{{#each maintenanceSkills}}
<label class="flexrow align-center">
<input type="checkbox" name="system.maintenance" value="{{ this.key }}" {{#if this.checked}}checked{{/if}} />
<span>{{ this.label }}</span>
</label>
{{/each}}
</div>
</div>
</section>
<section class="vehicle-section">
<h3>{{ localize 'IDENTITY.profile' }}</h3>
<div class="grid grid-2col">
<div class="form-group">
<label for="system.identity.profile">{{ localize 'IDENTITY.profile' }}</label>
{{#if isEditMode}}
<input type="text" name="system.identity.profile" value="{{system.identity.profile}}" data-dtype="String" />
{{else}}
<span>{{system.identity.profile}}</span>
{{/if}}
</div>
<div class="form-group">
<label for="system.identity.origin">{{ localize 'IDENTITY.origin' }}</label>
{{#if isEditMode}}
<input type="text" name="system.identity.origin" value="{{system.identity.origin}}" data-dtype="String" />
{{else}}
<span>{{system.identity.origin}}</span>
{{/if}}
</div>
<div class="form-group">
<label for="system.identity.theme">{{ localize 'IDENTITY.theme' }}</label>
{{#if isEditMode}}
<input type="text" name="system.identity.theme" value="{{system.identity.theme}}" data-dtype="String" />
{{else}}
<span>{{system.identity.theme}}</span>
{{/if}}
</div>
</div>
<div class="form-group">
<label for="system.identity.notes">{{ localize 'IDENTITY.notes' }}</label>
{{formInput systemFields.identity.fields.notes enriched=enrichedNotes value=system.identity.notes name="system.identity.notes" toggled=true}}
</div>
</section>
</div>
+18 -11
View File
@@ -5,27 +5,34 @@
<div class="item-rarity">{{ localize 'VERMINE.rarity'}}</div>
<div class="item-reliability">{{ localize 'VERMINE.reliability'}}</div>
<div class="item-controls">
<a class="item-control item-create" title="Create item"
data-type="vehicle" data-action="create"><i class="fas fa-plus"></i></a>
{{#if @root.isEditMode}}
<a class="item-control item-create" data-action="createVehicle" title="Create vehicle"><i class="fas fa-plus"></i></a>
{{/if}}
</div>
</li>
{{#each vehicles as |item id|}}
<li class="item flexrow flex-group-center" data-item-id="{{item._id}}">
{{#each vehicles as |vehicle|}}
<li class="item flexrow flex-group-center" data-vehicle-id="{{vehicle.id}}">
<div class="item-name" style="flex:4;">
<div class="item-image">
<a data-action="edit"><img
src="{{item.img}}" title="{{item.name}}" width="24"
<a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}"><img
src="{{vehicle.img}}" title="{{vehicle.name}}" width="24"
height="24" /></a>
</div>
<a data-action="edit" title="Edit Item">{{item.name}}</a>
<a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.name}}</a>
</div>
<div><a data-action="edit" title="Edit Item">{{item.system.mobility}}</a></div>
<div><a data-action="edit" title="Edit Item">{{item.system.rarity.value}}</a></div>
<div><a data-action="edit" title="Edit Item">{{item.system.reliability}}</a></div>
<div><a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.system.mobility}}</a></div>
<div><a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.system.rarity.value}}</a></div>
<div><a class="item-control item-edit" data-action="editVehicle" data-vehicle-id="{{vehicle.id}}" title="Edit Vehicle">{{vehicle.system.reliability}}</a></div>
<div class="item-controls">
<a data-action="delete" title="Delete Item"><i
{{#if @root.isEditMode}}
<a class="item-control item-delete" data-action="deleteVehicle" data-vehicle-id="{{vehicle.id}}" title="Delete Vehicle"><i
class="fas fa-trash"></i></a>
{{/if}}
</div>
</li>
{{else}}
<li class="item flexrow flex-group-center">
<span class="empty">{{ localize 'VERMINE.no_vehicles' }}</span>
</li>
{{/each}}
</ol>
+105
View File
@@ -0,0 +1,105 @@
<div class="roll-dialog-content">
<!-- HIDDEN DATA -->
<input type="hidden" name="speakerId" value="{{ speakerId }}" />
<h4 class="npc-roll-label">{{ rollLabel }}</h4>
<div class="npc-roll-pool">
<span class="label">{{localize 'VERMINE.dice_pool'}}:</span>
<span class="pool-value">{{ pool }}D</span>
{{#if freeSuccesses}}
<span class="free-successes-badge"><i class="fas fa-star"></i> +{{ freeSuccesses }} {{localize 'VERMINE.auto_successes'}}</span>
{{/if}}
{{#if rerolls}}
<span class="rerolls-badge">{{localize 'ADVERSITY.rerolls'}}: {{ rerolls }}D</span>
{{/if}}
</div>
<div class="dice-pool">
<!-- DIFFICULTY -->
<div class="flexcol difficulty-section">
<label class="label" for="difficulty">{{localize 'VERMINE.difficulty'}}</label>
<select
class="info-value"
data-roll="true"
data-dtype="String"
type="number"
name="difficulty"
id="difficulty"
min="3"
max="10"
{{#if lockedDifficulty}}disabled{{/if}}
>
{{#each difficultyOptions}}
<option value="{{ this.difficulty }}" {{#ife @root.defaultDifficulty this.difficulty}}selected{{/ife}}>
{{ this.label }} ({{ this.difficulty }})
</option>
{{/each}}
</select>
</div>
<!-- HANDICAP -->
<div class="flexcol handicap-section">
<label class="label" for="handicap">{{localize 'VERMINE.handicap'}}</label>
<select
class="info-value"
data-roll="true"
data-dtype="String"
type="number"
name="handicap"
id="handicap"
min="0"
max="2"
>
<option value="1" selected>{{localize 'VERMINE.none'}}</option>
<option value="2">(I)</option>
<option value="3">(II)</option>
</select>
</div>
<!-- BONUSES -->
<details class="bonuses-section">
<summary class="flexrow">
<span class="label">{{localize 'VERMINE.bonuses'}}</span>
<span class="bonus-count">+<span id="total-bonus">0</span>D</span>
</summary>
<div class="grid grid-2col bonus-grid">
<div class="flexrow bonus-item">
<input type="checkbox" data-roll="true" name="helped" id="helped" value="1" />
<label class="label" for="helped">{{localize 'VERMINE.help'}} (+1D)</label>
</div>
<div class="flexrow bonus-item">
<label class="label" for="group">{{localize 'VERMINE.group'}}</label>
<input type="number" data-roll="true" class="numeric-entry" style="text-align: center;"
name="group" id="group" min="0" max="5" value="0" />
<span>D</span>
</div>
</div>
</details>
<!-- TOTAL -->
<div class="flexrow total-section">
<label class="label">{{localize 'VERMINE.total'}}:</label>
<span id="dice-pool-total" class="total-value">0D</span>
{{#if freeSuccesses}}
<span class="totem-selector">
(+<span id="free-successes">{{ freeSuccesses }}</span> {{localize 'VERMINE.auto_successes'}})
</span>
{{/if}}
</div>
</div>
<footer class="sheet-footer flexrow">
<button type="button" data-action="cancel">
<i class="fas fa-times"></i> {{localize "VERMINE.cancel"}}
</button>
<button type="button" data-action="roll">
<i class="fas fa-dice-d10"></i> {{localize "VERMINE.roll"}}
</button>
</footer>
</div>
-83
View File
@@ -1,83 +0,0 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Vehicle specific content --}}
<div class="grid grid-2col">
{{#if item.system.mobility}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.mobility' }}:</span>
<span>{{item.system.mobility}}</span>
</div>
{{/if}}
{{#if item.system.quantity}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.qty' }}:</span>
<span>{{item.system.quantity}}</span>
</div>
{{/if}}
{{#if item.system.weight}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.weight' }}:</span>
<span>{{item.system.weight}}</span>
</div>
{{/if}}
{{#if item.system.rarity.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.rarity' }}:</span>
<span>
{{#repeat item.system.rarity.value 1 "i"}}
{{romanNumber i}}
{{/repeat}}
{{#ifgt item.system.rarity.handicap 0}}
({{romanNumber item.system.rarity.handicap}})
{{/ifgt}}
</span>
</div>
{{/if}}
{{#if item.system.damages.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.damages' }}:</span>
<span class="damage-indicator">
{{getDamagesData item.system.damages "state"}}
{{#ifgt item.system.damages.value 1}}
({{item.system.damages.value}})
{{/ifgt}}
</span>
</div>
{{/if}}
{{#if item.system.reliability}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.reliability' }}:</span>
<span>{{item.system.reliability}}</span>
</div>
{{/if}}
{{#if item.system.traits}}
<div class="resource flexcol">
<span class="resource-label">{{ localize 'VERMINE.traits' }}:</span>
<ul class="unstyled">
{{#each item.system.traits as |trait key|}}
{{#if trait}}
<li>
<strong>{{localize (lookup (lookup ../config.traits key) "name")}}</strong>:
{{localize (lookup (lookup ../config.traits key) "description")}}
</li>
{{/if}}
{{/each}}
</ul>
</div>
{{/if}}
{{#if item.system.description}}
<div class="resource flexcol full-width">
<p>{{{item.system.description}}}</p>
</div>
{{/if}}
</div>
</form>
-64
View File
@@ -1,64 +0,0 @@
<div class="{{cssClass}}">
{{> "systems/vermine2047/templates/item/partials/header.hbs"}}
<section class="sheet-body">
{{> "systems/vermine2047/templates/item/partials/traits.hbs"}}
<section class="vehicle-fields">
<h3>{{ localize "VERMINE.vehicle_specs" }}</h3>
<div class="flexrow">
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize "VERMINE.kind" }}</label>
<select name="system.kind" data-dtype="String">
<option value="vehicle" {{#ife system.kind "vehicle"}}selected{{/ife}}>{{ localize "VERMINE.kind_vehicle" }}</option>
<option value="mount" {{#ife system.kind "mount"}}selected{{/ife}}>{{ localize "VERMINE.kind_mount" }}</option>
</select>
</div>
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize "VERMINE.distance" }}</label>
<div class="hexa"><input type="number" name="system.distance" value="{{system.distance}}" data-dtype="Number" /></div>
<span class="unit">km</span>
</div>
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize "VERMINE.speed" }}</label>
<div class="flexrow align-center">
<input type="number" name="system.speed.cruise" value="{{system.speed.cruise}}" data-dtype="Number" />
<span>/</span>
<input type="number" name="system.speed.top" value="{{system.speed.top}}" data-dtype="Number" />
</div>
<input type="text" name="system.speed.topDuration" value="{{system.speed.topDuration}}" placeholder="{{ localize 'VERMINE.speed_duration_ph' }}" />
</div>
</div>
<div class="flexrow">
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize "VERMINE.effort_reserve" }}</label>
<div class="flexrow align-center">
<div class="hexa"><input type="number" name="system.effortReserve.value" value="{{system.effortReserve.value}}" data-dtype="Number" /></div>
<span>/</span>
<div class="hexa"><input type="number" name="system.effortReserve.max" value="{{system.effortReserve.max}}" data-dtype="Number" /></div>
</div>
</div>
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize "VERMINE.mobility" }}</label>
<div class="hexa"><input type="number" name="system.mobility" value="{{system.mobility}}" data-dtype="Number" /></div>
</div>
</div>
<div class="maintenance">
<label class="resource-label">{{ localize "VERMINE.maintenance" }}</label>
<div class="flexrow flex-wrap">
{{#each maintenanceSkills}}
<label class="flexrow align-center">
<input type="checkbox" name="system.maintenance" value="{{ this.key }}" {{#if this.checked}}checked{{/if}} />
<span>{{ this.label }}</span>
</label>
{{/each}}
</div>
</div>
</section>
{{> "systems/vermine2047/templates/item/partials/physicalItems.hbs"}}
</section>
</div>
+8 -2
View File
@@ -1,4 +1,4 @@
<div class="vermine-roll-message">
<div class="vermine-roll-message" data-free-successes="{{verdict.bonusSuccesses}}">
{{!-- Verdict --}}
<div class="roll-verdict {{#if verdict.success}}success{{else}}failure{{/if}}">
{{#if verdict.success}}
@@ -75,6 +75,12 @@
<span class="context-value">+{{param.handicap}}</span>
</div>
{{/if}}
{{#if verdict.bonusSuccesses}}
<div class="context-item">
<span class="context-label">{{localize "VERMINE.auto_successes"}}</span>
<span class="context-value">+{{verdict.bonusSuccesses}}</span>
</div>
{{/if}}
{{#if param.specialtyName}}
<div class="context-item">
<span class="context-label">{{localize "VERMINE.specialty"}}</span>
@@ -208,7 +214,7 @@
<div class="roll-total flexrow">
<div class="flexcol">
<h4>{{localize "VERMINE.success_count"}}:</h4>
<span id="total">{{roll._total}}</span>
<span id="total">{{verdict.total}}</span>
</div>
<div class="flexcol">
<h4>{{localize "VERMINE.success_required"}}:</h4>