Nouveaux items Arme et Armure (DataModel + feuille + CSS)
Items: - CelestopolWeapon : degats (0/1/2/X), portee (contact/courte/longue), description - CelestopolArmure : protection (1-2), malus (0-2), description Config: - WEAPON_DAMAGE_TYPES et WEAPON_RANGE_TYPES ajoutés dans system.mjs - Enregistrement des DataModels, sheets et templates dans fvtt-celestopol.mjs - system.json : types weapon et armure avec htmlFields UI: - weapon.hbs : badge de dégâts avec hint, sélecteurs portée/dégâts - armure.hbs : blocs protection + malus art-déco - items.less : styles .weapon et .armure i18n: clés Weapon.*, Armure.*, Sheet.weapon, Sheet.armure Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -10,6 +10,8 @@ import {
|
||||
CelestopolAnomaly,
|
||||
CelestopolAspect,
|
||||
CelestopolEquipment,
|
||||
CelestopolWeapon,
|
||||
CelestopolArmure,
|
||||
} from "./module/models/_module.mjs"
|
||||
import {
|
||||
CelestopolActor,
|
||||
@@ -23,6 +25,8 @@ import {
|
||||
CelestopolAnomalySheet,
|
||||
CelestopolAspectSheet,
|
||||
CelestopolEquipmentSheet,
|
||||
CelestopolWeaponSheet,
|
||||
CelestopolArmureSheet,
|
||||
} from "./module/applications/_module.mjs"
|
||||
|
||||
/* ─── Init hook ──────────────────────────────────────────────────────────── */
|
||||
@@ -41,6 +45,8 @@ Hooks.once("init", () => {
|
||||
CONFIG.Item.dataModels.anomaly = CelestopolAnomaly
|
||||
CONFIG.Item.dataModels.aspect = CelestopolAspect
|
||||
CONFIG.Item.dataModels.equipment = CelestopolEquipment
|
||||
CONFIG.Item.dataModels.weapon = CelestopolWeapon
|
||||
CONFIG.Item.dataModels.armure = CelestopolArmure
|
||||
|
||||
// ── Document classes ────────────────────────────────────────────────────
|
||||
CONFIG.Actor.documentClass = CelestopolActor
|
||||
@@ -90,6 +96,16 @@ Hooks.once("init", () => {
|
||||
makeDefault: true,
|
||||
label: "CELESTOPOL.Sheet.equipment",
|
||||
})
|
||||
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolWeaponSheet, {
|
||||
types: ["weapon"],
|
||||
makeDefault: true,
|
||||
label: "CELESTOPOL.Sheet.weapon",
|
||||
})
|
||||
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolArmureSheet, {
|
||||
types: ["armure"],
|
||||
makeDefault: true,
|
||||
label: "CELESTOPOL.Sheet.armure",
|
||||
})
|
||||
|
||||
// ── Handlebars helpers ───────────────────────────────────────────────────
|
||||
_registerHandlebarsHelpers()
|
||||
@@ -197,6 +213,8 @@ function _preloadTemplates() {
|
||||
`${base}/anomaly.hbs`,
|
||||
`${base}/aspect.hbs`,
|
||||
`${base}/equipment.hbs`,
|
||||
`${base}/weapon.hbs`,
|
||||
`${base}/armure.hbs`,
|
||||
`${base}/roll-dialog.hbs`,
|
||||
`${base}/chat-message.hbs`,
|
||||
`${base}/partials/item-scores.hbs`,
|
||||
|
||||
31
lang/fr.json
31
lang/fr.json
@@ -184,7 +184,11 @@
|
||||
"protection": "Protection",
|
||||
"speed": "Vitesse",
|
||||
"crew": "Équipage",
|
||||
"weight": "Poids"
|
||||
"weight": "Poids",
|
||||
"weapons": "Armes",
|
||||
"armures": "Armures",
|
||||
"newWeapon": "Nouvelle arme",
|
||||
"newArmure": "Nouvelle armure"
|
||||
},
|
||||
"Equipment": {
|
||||
"autre": "Autre",
|
||||
@@ -195,7 +199,9 @@
|
||||
},
|
||||
"Sheet": {
|
||||
"editMode": "Mode édition",
|
||||
"playMode": "Mode jeu"
|
||||
"playMode": "Mode jeu",
|
||||
"weapon": "Fiche Arme",
|
||||
"armure": "Fiche Armure"
|
||||
},
|
||||
"Setting": {
|
||||
"autoWounds": {
|
||||
@@ -213,6 +219,27 @@
|
||||
},
|
||||
"ChatCard": {
|
||||
"rollFor": "Jet de {skill} ({stat})"
|
||||
},
|
||||
"Weapon": {
|
||||
"degats": "Dégâts",
|
||||
"degats0": "Dégâts 0",
|
||||
"degats0Hint": "Mains nues, arme improvisée, matraque, rasoir, arc, couteau",
|
||||
"degats1": "Dégâts 1",
|
||||
"degats1Hint": "Arbalète, épée, hachette, masse, rapière, fléau, hache, hallebarde",
|
||||
"degats2": "Dégâts 2",
|
||||
"degats2Hint": "Armes à feu",
|
||||
"degatsX": "Dégâts X",
|
||||
"degatsXHint": "Explosifs, sélénium, etc. (à l'appréciation du narrateur)",
|
||||
"portee": "Portée",
|
||||
"rangeContact": "Contact",
|
||||
"rangeCourte": "Courte portée",
|
||||
"rangeLongue": "Longue portée"
|
||||
},
|
||||
"Armure": {
|
||||
"protection": "Protection",
|
||||
"protectionHint": "Réduit les blessures subies de ce montant",
|
||||
"malus": "Malus",
|
||||
"malusHint": "Malus aux tests de Mobilité et Discrétion (ou Domaine Corps pour PNJ)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export { default as CelestopolCharacterSheet } from "./sheets/character-sheet.mjs"
|
||||
export { default as CelestopolNPCSheet } from "./sheets/npc-sheet.mjs"
|
||||
export { CelestopolAnomalySheet, CelestopolAspectSheet, CelestopolEquipmentSheet } from "./sheets/item-sheets.mjs"
|
||||
export { CelestopolAnomalySheet, CelestopolAspectSheet, CelestopolEquipmentSheet, CelestopolWeaponSheet, CelestopolArmureSheet } from "./sheets/item-sheets.mjs"
|
||||
|
||||
@@ -69,3 +69,37 @@ export class CelestopolEquipmentSheet extends CelestopolItemSheet {
|
||||
return ctx
|
||||
}
|
||||
}
|
||||
|
||||
export class CelestopolWeaponSheet extends CelestopolItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["weapon"],
|
||||
position: { width: 480, height: 460 },
|
||||
}
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-celestopol/templates/weapon.hbs" },
|
||||
}
|
||||
async _prepareContext() {
|
||||
const ctx = await super._prepareContext()
|
||||
ctx.damageTypes = SYSTEM.WEAPON_DAMAGE_TYPES
|
||||
ctx.rangeTypes = SYSTEM.WEAPON_RANGE_TYPES
|
||||
ctx.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.document.system.description, { async: true })
|
||||
return ctx
|
||||
}
|
||||
}
|
||||
|
||||
export class CelestopolArmureSheet extends CelestopolItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["armure"],
|
||||
position: { width: 440, height: 380 },
|
||||
}
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-celestopol/templates/armure.hbs" },
|
||||
}
|
||||
async _prepareContext() {
|
||||
const ctx = await super._prepareContext()
|
||||
ctx.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.document.system.description, { async: true })
|
||||
return ctx
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +140,21 @@ export const EQUIPMENT_TYPES = {
|
||||
vehicule: { id: "vehicule", label: "CELESTOPOL.Equipment.vehicule" },
|
||||
}
|
||||
|
||||
/** Niveaux de dégâts des armes (règles p. ?). */
|
||||
export const WEAPON_DAMAGE_TYPES = {
|
||||
"0": { id: "0", label: "CELESTOPOL.Weapon.degats0", hint: "CELESTOPOL.Weapon.degats0Hint" },
|
||||
"1": { id: "1", label: "CELESTOPOL.Weapon.degats1", hint: "CELESTOPOL.Weapon.degats1Hint" },
|
||||
"2": { id: "2", label: "CELESTOPOL.Weapon.degats2", hint: "CELESTOPOL.Weapon.degats2Hint" },
|
||||
"X": { id: "X", label: "CELESTOPOL.Weapon.degatsX", hint: "CELESTOPOL.Weapon.degatsXHint" },
|
||||
}
|
||||
|
||||
/** Portées des armes. */
|
||||
export const WEAPON_RANGE_TYPES = {
|
||||
contact: { id: "contact", label: "CELESTOPOL.Weapon.rangeContact" },
|
||||
courte: { id: "courte", label: "CELESTOPOL.Weapon.rangeCourte" },
|
||||
longue: { id: "longue", label: "CELESTOPOL.Weapon.rangeLongue" },
|
||||
}
|
||||
|
||||
export const SYSTEM = {
|
||||
id: SYSTEM_ID,
|
||||
ASCII,
|
||||
@@ -154,4 +169,6 @@ export const SYSTEM = {
|
||||
MOON_DIE_FACES,
|
||||
MOON_RESULT_TYPES,
|
||||
EQUIPMENT_TYPES,
|
||||
WEAPON_DAMAGE_TYPES,
|
||||
WEAPON_RANGE_TYPES,
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { default as CelestopolCharacter } from "./character.mjs"
|
||||
export { default as CelestopolNPC } from "./npc.mjs"
|
||||
export { CelestopolAnomaly, CelestopolAspect, CelestopolEquipment } from "./items.mjs"
|
||||
export { CelestopolAnomaly, CelestopolAspect, CelestopolEquipment, CelestopolWeapon, CelestopolArmure } from "./items.mjs"
|
||||
|
||||
@@ -76,3 +76,29 @@ export class CelestopolEquipment extends foundry.abstract.TypeDataModel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CelestopolWeapon extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields
|
||||
const reqInt = { required: true, nullable: false, integer: true }
|
||||
return {
|
||||
degats: new fields.StringField({ required: true, nullable: false, initial: "0",
|
||||
choices: Object.keys(SYSTEM.WEAPON_DAMAGE_TYPES) }),
|
||||
portee: new fields.StringField({ required: true, nullable: false, initial: "contact",
|
||||
choices: Object.keys(SYSTEM.WEAPON_RANGE_TYPES) }),
|
||||
description: new fields.HTMLField({ required: true, textSearch: true }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CelestopolArmure extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields
|
||||
const reqInt = { required: true, nullable: false, integer: true }
|
||||
return {
|
||||
protection: new fields.NumberField({ ...reqInt, initial: 1, min: 1, max: 2 }),
|
||||
malus: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 2 }),
|
||||
description: new fields.HTMLField({ required: true, textSearch: true }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,4 +333,61 @@
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
// Weapon-specific
|
||||
&.weapon {
|
||||
.weapon-meta {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
.form-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
label { font-size: 0.75em; color: var(--cel-orange-light); white-space: nowrap; }
|
||||
select { background: rgba(0,0,0,0.3); border: 1px solid var(--cel-orange); color: var(--cel-orange); font-family: var(--cel-font-title); border-radius: 3px; padding: 2px 4px; font-size: 0.85em; }
|
||||
}
|
||||
}
|
||||
.weapon-damage-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: var(--cel-green-dark);
|
||||
border: 1px solid var(--cel-orange);
|
||||
border-radius: 6px;
|
||||
padding: 6px 12px;
|
||||
margin: 8px 0;
|
||||
.damage-label { font-size: 0.72em; text-transform: uppercase; color: var(--cel-orange-light); letter-spacing: 0.05em; }
|
||||
.damage-value { font-family: var(--cel-font-title); font-size: 1.6em; font-weight: bold; color: var(--cel-orange); min-width: 28px; text-align: center; }
|
||||
.damage-hint { font-size: 0.78em; color: var(--cel-cream); font-style: italic; }
|
||||
}
|
||||
}
|
||||
|
||||
// Armure-specific
|
||||
&.armure {
|
||||
.armure-stats {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
justify-content: center;
|
||||
margin: 12px 0;
|
||||
}
|
||||
.armure-stat-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: var(--cel-green-dark);
|
||||
border: 1px solid var(--cel-orange);
|
||||
border-radius: 6px;
|
||||
padding: 8px 20px;
|
||||
min-width: 110px;
|
||||
label { font-size: 0.72em; text-transform: uppercase; color: var(--cel-orange-light); letter-spacing: 0.05em; }
|
||||
.armure-stat-value {
|
||||
input, span {
|
||||
font-family: var(--cel-font-title); font-size: 1.8em; font-weight: bold; color: var(--cel-orange);
|
||||
text-align: center; background: transparent; border: none; width: 40px;
|
||||
}
|
||||
}
|
||||
.armure-stat-hint { font-size: 0.7em; color: var(--cel-cream); font-style: italic; text-align: center; margin-top: 4px; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
67
system.json
67
system.json
@@ -15,16 +15,29 @@
|
||||
],
|
||||
"flags": {
|
||||
"hotReload": {
|
||||
"extensions": ["css", "html", "hbs", "json"],
|
||||
"paths": ["css/", "templates/", "lang/fr.json"]
|
||||
"extensions": [
|
||||
"css",
|
||||
"html",
|
||||
"hbs",
|
||||
"json"
|
||||
],
|
||||
"paths": [
|
||||
"css/",
|
||||
"templates/",
|
||||
"lang/fr.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
"minimum": "13",
|
||||
"verified": "13"
|
||||
},
|
||||
"esmodules": ["fvtt-celestopol.mjs"],
|
||||
"styles": ["css/fvtt-celestopol.css?v=1774698726"],
|
||||
"esmodules": [
|
||||
"fvtt-celestopol.mjs"
|
||||
],
|
||||
"styles": [
|
||||
"css/fvtt-celestopol.css?v=1774698726"
|
||||
],
|
||||
"languages": [
|
||||
{
|
||||
"lang": "fr",
|
||||
@@ -35,16 +48,50 @@
|
||||
"documentTypes": {
|
||||
"Actor": {
|
||||
"character": {
|
||||
"htmlFields": ["description", "notes"]
|
||||
"htmlFields": [
|
||||
"description",
|
||||
"notes"
|
||||
]
|
||||
},
|
||||
"npc": {
|
||||
"htmlFields": ["description", "notes"]
|
||||
"htmlFields": [
|
||||
"description",
|
||||
"notes"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Item": {
|
||||
"anomaly": { "htmlFields": ["technique", "narratif", "exemples"] },
|
||||
"aspect": { "htmlFields": ["description", "technique", "narratif", "notes"] },
|
||||
"equipment": { "htmlFields": ["description", "notes"] }
|
||||
"anomaly": {
|
||||
"htmlFields": [
|
||||
"technique",
|
||||
"narratif",
|
||||
"exemples"
|
||||
]
|
||||
},
|
||||
"aspect": {
|
||||
"htmlFields": [
|
||||
"description",
|
||||
"technique",
|
||||
"narratif",
|
||||
"notes"
|
||||
]
|
||||
},
|
||||
"equipment": {
|
||||
"htmlFields": [
|
||||
"description",
|
||||
"notes"
|
||||
]
|
||||
},
|
||||
"weapon": {
|
||||
"htmlFields": [
|
||||
"description"
|
||||
]
|
||||
},
|
||||
"armure": {
|
||||
"htmlFields": [
|
||||
"description"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"packs": [
|
||||
@@ -70,4 +117,4 @@
|
||||
"primaryTokenAttribute": "resource",
|
||||
"socket": true,
|
||||
"background": "systems/fvtt-celestopol/assets/ui/celestopol_background.webp"
|
||||
}
|
||||
}
|
||||
39
templates/armure.hbs
Normal file
39
templates/armure.hbs
Normal file
@@ -0,0 +1,39 @@
|
||||
<div class="item-sheet armure">
|
||||
<header class="item-header">
|
||||
<div class="item-portrait" data-action="editImage">
|
||||
<img src="{{item.img}}" alt="{{item.name}}">
|
||||
</div>
|
||||
<div class="item-header-fields">
|
||||
<input type="text" name="name" value="{{item.name}}" {{#unless isEditable}}disabled{{/unless}}>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="armure-stats">
|
||||
<div class="armure-stat-box">
|
||||
<label>{{localize "CELESTOPOL.Armure.protection"}}</label>
|
||||
<div class="armure-stat-value">
|
||||
{{#if isEditable}}
|
||||
<input type="number" name="system.protection" value="{{system.protection}}" min="1" max="2">
|
||||
{{else}}
|
||||
<span>{{system.protection}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="armure-stat-hint">{{localize "CELESTOPOL.Armure.protectionHint"}}</div>
|
||||
</div>
|
||||
<div class="armure-stat-box">
|
||||
<label>{{localize "CELESTOPOL.Armure.malus"}}</label>
|
||||
<div class="armure-stat-value">
|
||||
{{#if isEditable}}
|
||||
<input type="number" name="system.malus" value="{{system.malus}}" min="0" max="2">
|
||||
{{else}}
|
||||
<span>{{system.malus}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="armure-stat-hint">{{localize "CELESTOPOL.Armure.malusHint"}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group description-group">
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
42
templates/weapon.hbs
Normal file
42
templates/weapon.hbs
Normal file
@@ -0,0 +1,42 @@
|
||||
<div class="item-sheet weapon">
|
||||
<header class="item-header">
|
||||
<div class="item-portrait" data-action="editImage">
|
||||
<img src="{{item.img}}" alt="{{item.name}}">
|
||||
</div>
|
||||
<div class="item-header-fields">
|
||||
<input type="text" name="name" value="{{item.name}}" {{#unless isEditable}}disabled{{/unless}}>
|
||||
<div class="item-meta weapon-meta">
|
||||
<div class="form-group">
|
||||
<label>{{localize "CELESTOPOL.Weapon.degats"}}</label>
|
||||
<select name="system.degats" {{#unless isEditable}}disabled{{/unless}}>
|
||||
{{#each damageTypes as |dtype key|}}
|
||||
<option value="{{key}}" {{#if (eq key ../system.degats)}}selected{{/if}}>
|
||||
{{localize dtype.label}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "CELESTOPOL.Weapon.portee"}}</label>
|
||||
<select name="system.portee" {{#unless isEditable}}disabled{{/unless}}>
|
||||
{{#each rangeTypes as |rtype key|}}
|
||||
<option value="{{key}}" {{#if (eq key ../system.portee)}}selected{{/if}}>
|
||||
{{localize rtype.label}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="weapon-damage-badge">
|
||||
<span class="damage-label">{{localize "CELESTOPOL.Weapon.degats"}}</span>
|
||||
<span class="damage-value">{{system.degats}}</span>
|
||||
<span class="damage-hint">{{localize (lookup (lookup damageTypes system.degats) "hint")}}</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group description-group">
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user