Compare commits

...

11 Commits

Author SHA1 Message Date
55d7e401c1 Finalize aappv2 data models migration
All checks were successful
Release Creation / build (release) Successful in 1m3s
2026-02-27 14:38:33 +01:00
5e2916202e Finalize aappv2 data models migration 2026-02-27 14:37:22 +01:00
c45837ea31 Finalize aappv2 data models migration 2026-02-27 14:36:54 +01:00
8735b7e4a4 Fix basic system stuff 2026-02-26 21:25:05 +01:00
34db8695d7 Fix tirage de cartes 2025-09-29 19:51:39 +02:00
a109fd6acb Fix tirage de cartes 2025-09-29 19:49:02 +02:00
d647fcc35e Foundry v13 migration 2025-05-01 08:48:02 +02:00
342f9c2342 Fix waring about grid 2025-02-14 13:37:51 +01:00
cd14db85cc Fix v11/v12 2024-05-02 09:29:42 +02:00
ec06f0fdcb Update pour v11/v12 et correction d'un bug sur les defenses 2024-04-26 18:35:39 +02:00
234bd44742 Enhance stats 2024-02-08 12:51:06 +01:00
117 changed files with 11003 additions and 1589 deletions

View File

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

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules/

27
gulpfile.js Normal file
View File

@@ -0,0 +1,27 @@
const gulp = require('gulp');
const less = require('gulp-less');
const sourcemaps = require('gulp-sourcemaps');
const rename = require('gulp-rename');
// Compile LESS to CSS
function styles() {
return gulp.src('less/malefices.less')
.pipe(sourcemaps.init())
.pipe(less())
.pipe(rename('simple.css'))
.pipe(sourcemaps.write('.', { mapFile: () => 'simple.css.map' }))
.pipe(gulp.dest('styles/'));
}
// Watch files
function watchFiles() {
gulp.watch('less/**/*.less', styles);
}
const build = gulp.series(styles);
const watch = gulp.series(build, watchFiles);
exports.styles = styles;
exports.build = build;
exports.watch = watch;
exports.default = build;

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

View File

@@ -1,13 +1,15 @@
{ {
"ACTOR": { "TYPES": {
"TypePersonnage": "Personnage" "Actor": {
}, "personnage" : "Personnage"
"ITEM": { },
"TypeArme": "Arme", "Item": {
"TypeEquipement": "Equipement", "arme" : "Arme",
"TypeTarot": "Tarot", "equipement" : "Equipement",
"TypeElementbio": "Element Biographique", "tarot" : "Tarot",
"TypeArchetype": "Archetype", "elementbio" : "Elément Biographique",
"TypeSortilege": "Sortilège" "archetype" : "Archetype",
"sortilege" : "Sortilège"
}
} }
} }

235
less/actor-sheet.less Normal file
View File

@@ -0,0 +1,235 @@
/* ===================================================================
AppV2 Actor Sheets
=================================================================== */
/* NOTE: DocumentSheetV2 uses tag:"form" — the APPLICATION ROOT is the <form>.
The DOM is: form.application > section.window-content > section.editable > ...
There is NO <form> child inside window-content. */
.fvtt-malefices.actor {
/* window-content: background and remove Foundry's default padding.
Foundry AppV2 already sets: display:flex flex-flow:column overflow:hidden */
.window-content {
padding: 0;
background: @bg-sheet;
color: @color-text;
font-size: 0.8rem;
}
/* All sections inside actor fill their flex container and form a flex column.
This covers: section.editable (template root) and section.sheet-body. */
section {
height: 100%;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
overflow: hidden;
}
/* sheet-body: scroll container instead of clipping */
section.sheet-body {
overflow-y: auto;
overflow-x: hidden;
padding: 0.25rem 0.5rem;
}
/* Override legacy fixed 210px height — shrink to fit content */
.sheet-header {
flex: 0 0 auto;
overflow: visible;
}
/* Override legacy "height: 100%" on .tab divs so their content
can overflow sheet-body and trigger the scrollbar;
hide all tabs, show only the active one */
.tab[data-tab] {
height: auto;
display: none;
&.active {
display: block;
}
}
/* Tab navigation */
nav.tabs {
display: flex;
flex: 0 0 auto;
font-family: @font-rivanna;
font-size: @tab-font-size;
font-weight: bold;
height: @tab-height;
margin: 0;
padding: 0 0 0 0.25rem;
text-align: center;
text-transform: uppercase;
line-height: @tab-height;
border-top: 0 none;
border-bottom: 0 none;
background-color: @color-tab-bg;
color: @color-tab-text;
gap: 0.25rem;
a.item {
position: relative;
padding: 0 0.5rem;
color: @color-tab-text;
font-family: @font-rivanna;
font-size: @tab-font-size;
text-decoration: none;
line-height: @tab-height;
&:hover {
text-shadow: 1px 0px 0px @color-accent;
}
&.active {
text-shadow: 1px 0px 0px @color-accent;
text-decoration: underline;
}
}
}
/* Force dark text on section titles and labels inside actor sheet body
(overrides Foundry core heading/label colors from the default dark theme) */
section.sheet-body {
h1, h2, h3, h4, label, span, a {
color: @color-text;
}
}
/* ── Belle Époque aesthetic improvements ─────────────────────────── */
@be-bordeaux: #5a0a14;
@be-gold: #8b6914;
@be-gold-border: rgba(139, 105, 20, 0.45);
@be-sepia: #3d2b1f;
// Sheet header — portrait with gold border, charname input styled
.sheet-header {
.profile-img {
border: 2px solid @be-gold-border;
border-radius: 2px;
box-shadow: 1px 1px 4px rgba(0,0,0,0.25);
}
h1.charname input {
font-family: @font-rivanna, serif;
font-size: 1.3rem;
color: @be-bordeaux;
border-bottom: 1px solid @be-gold-border;
border-top: none;
border-left: none;
border-right: none;
background: transparent;
text-shadow: 0 1px 0 rgba(255,255,255,0.4);
}
input[type="text"] {
border-bottom: 1px solid @be-gold-border;
border-top: none;
border-left: none;
border-right: none;
background: transparent;
&:focus {
outline: none;
border-bottom-color: @be-bordeaux;
box-shadow: none;
}
}
}
// Section title rows (Attributs, Armes, Équipements, etc.)
.items-title-bg {
background: linear-gradient(to right, rgba(90,10,20,0.15), rgba(139,105,20,0.10)) !important;
border-bottom: 1px solid @be-gold-border;
border-top: 1px solid @be-gold-border;
margin-top: 6px;
.items-title-text {
font-family: @font-rivanna, serif;
font-size: 0.9rem;
color: @be-bordeaux !important;
letter-spacing: 0.03em;
}
}
// Roll links — attributs and weapons
a.roll-attribut, a.roll-arme {
color: @be-sepia !important;
font-weight: 600;
transition: color 0.12s;
i { color: fade(@be-gold, 80%); margin-left: 0.2rem; font-size: 0.75em; }
&:hover {
color: @be-bordeaux !important;
text-decoration: none;
i { color: @be-bordeaux; }
}
}
// Attribute value inputs
input.item-field-label-short, input.item-field-label-medium {
text-align: center;
font-weight: 700;
font-size: 0.9rem;
color: @be-bordeaux;
border: 1px solid @be-gold-border;
border-radius: 2px;
background: rgba(255,252,240,0.6);
&:focus {
outline: none;
border-color: @be-bordeaux;
box-shadow: 0 0 3px rgba(90,10,20,0.2);
}
}
// Item rows — zebra effect with Belle Époque tones
.alternate-list {
.list-item:nth-child(even) {
background: rgba(139,105,20,0.07);
}
.list-item:nth-child(odd) {
background: rgba(61,43,31,0.04);
}
.list-item:hover {
background: rgba(139,105,20,0.15) !important;
}
}
// Item icon — small gold border
.sheet-competence-img {
border: 1px solid @be-gold-border;
border-radius: 1px;
}
// Item control icons (edit/delete)
.item-controls a.item-control {
color: fade(@be-sepia, 60%);
&:hover { color: @be-bordeaux; }
}
// Biography tab inputs
.tab.biodata {
input[type="text"] {
border-bottom: 1px solid @be-gold-border;
border-top: none;
border-left: none;
border-right: none;
background: transparent;
color: @be-sepia;
&:focus {
outline: none;
border-bottom-color: @be-bordeaux;
}
}
label.item-name-label-medium {
font-weight: 600;
color: @be-sepia !important;
}
}
}

202
less/base.less Normal file
View File

@@ -0,0 +1,202 @@
/* ==================== (A) Fonts ==================== */
@font-face {
font-family: "Rivanna";
src: url('../fonts/rivanna.ttf') format("truetype");
}
:root {
/* =================== 1. ACTOR SHEET FONT STYLES =========== */
--window-header-title-font-size: 1.3rem;
--window-header-title-font-weight: normal;
--window-header-title-color: #f5f5f5;
--major-button-font-size: 1.05rem;
--major-button-font-weight: normal;
--major-button-color: #dadada;
--tab-header-font-size: 1.0rem;
--tab-header-font-weight: 700;
--tab-header-color: #403f3e;
--tab-header-color-active: #4a0404;
--actor-input-font-size: 0.8rem;
--actor-input-font-weight: 500;
--actor-input-color: black;
--actor-label-font-size: 0.8rem;
--actor-label-font-weight: 700;
--actor-label-color: #464331c4;
/* =================== 2. DEBUGGING HIGHLIGHTERS ============ */
--debug-background-color-red: #ff000054;
--debug-background-color-blue: #1d00ff54;
--debug-background-color-green: #54ff0054;
--debug-box-shadow-red: inset 0 0 2px red;
--debug-box-shadow-blue: inset 0 0 2px blue;
--debug-box-shadow-green: inset 0 0 2px green;
}
/*@import url("https://fonts.googleapis.com/css2?family=Martel:wght@400;800&family=Roboto:wght@300;400;500&display=swap");*/
/* Global styles & Font */
.fvtt-malefices .window-app {
text-align: justify;
font-size: 16px;
letter-spacing: 1px;
}
/* Fonts */
.sheet header.sheet-header h1 input, .window-app .window-header, #actors .directory-list, #navigation #scene-list .scene.nav-item {
font-size: 1.0rem;
} /* For title, sidebar character and scene */
.fvtt-malefices .sheet nav.sheet-tabs {
font-size: 0.8rem;
} /* For nav and title */
.window-app input, .fvtt-malefices .item-form, .sheet header.sheet-header .flex-group-center.flex-compteurs, .sheet header.sheet-header .flex-group-center.flex-fatigue, select, button, .item-checkbox, #sidebar, #players, #navigation #nav-toggle {
font-size: 0.8rem;
}
.window-header{
background: rgba(0,0,0,0.75);
}
.window-app.sheet .window-content {
margin: 0;
padding: 0;
}
.strong-text{
font-weight: bold;
}
.tabs .item.active, .blessures-list li ul li:first-child:hover, a:hover {
text-shadow: 1px 0px 0px @color-accent;
}
.rollable:hover, .rollable:focus {
color: #000;
text-shadow: 0 0 10px red;
cursor: pointer;
}
input:hover, select:hover {
border-width: 4px;
border-color: rgb(85, 65, 130);
}
input:disabled {
color:#1c2058;
}
select:disabled {
color:#1c2058;
}
table {border: 1px solid #7a7971;}
.grid, .grid-2col {
display: grid;
grid-column: span 2 / span 2;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
margin: 10px 0;
padding: 0;
}
.grid-3col {
grid-column: span 3 / span 3;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.grid-4col {
grid-column: span 4 / span 4;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.grid-5col {
grid-column: span 5 / span 5;
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.grid-6col {
grid-column: span 5 / span 5;
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.grid-7col {
grid-column: span 7 / span 7;
grid-template-columns: repeat(7, minmax(0, 1fr));
}
.grid-8col {
grid-column: span 8 / span 8;
grid-template-columns: repeat(8, minmax(0, 1fr));
}
.grid-9col {
grid-column: span 9 / span 9;
grid-template-columns: repeat(9, minmax(0, 1fr));
}
.grid-10col {
grid-column: span 10 / span 10;
grid-template-columns: repeat(10, minmax(0, 1fr));
}
.grid-11col {
grid-column: span 11 / span 11;
grid-template-columns: repeat(11, minmax(0, 1fr));
}
.grid-12col {
grid-column: span 12 / span 12;
grid-template-columns: repeat(12, minmax(0, 1fr));
}
.flex-group-center,
.flex-group-left,
.flex-group-right {
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-align: center;
padding: 5px;
}
.flex-group-left {
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
text-align: left;
}
.flex-group-right {
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: flex-end;
text-align: right;
}
.flex-center {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
text-align: center;
}
.table-create-actor {
font-size: 0.8rem;
}
.flex-between {
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
}
.flex-shrink {
flex: 'flex-shrink' ;
}

325
less/chat.less Normal file
View File

@@ -0,0 +1,325 @@
// ============================================================
// Chat messages Belle Époque theme
// ============================================================
@be-bordeaux: #5a0a14;
@be-gold: #8b6914;
@be-gold-light: #c8a84b;
@be-gold-border: rgba(139, 105, 20, 0.45);
@be-sepia: #3d2b1f;
@be-sepia-light: rgba(61, 43, 31, 0.06);
.malefices-chat-card {
font-size: 0.85rem;
color: @be-sepia;
border: 1px solid @be-gold-border;
border-radius: 3px;
overflow: hidden;
background: rgb(218, 218, 208); // opaque pour masquer le fond parchemin du sidebar
// ── Header ──────────────────────────────────────────────
.chat-card-header {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.2rem 0.4rem;
background: linear-gradient(135deg, rgba(90,10,20,0.12), rgba(139,105,20,0.12));
border-bottom: 1px solid @be-gold-border;
.chat-actor-img {
width: 32px;
height: 32px;
border: 1px solid @be-gold-border;
border-radius: 2px;
object-fit: cover;
flex-shrink: 0;
}
.chat-actor-name {
font-family: @font-rivanna, serif;
font-size: 1.1rem;
font-weight: normal;
color: @be-bordeaux;
line-height: 1.1;
text-shadow: 0 1px 0 rgba(255,255,255,0.5);
flex: 1;
}
.chat-header-roll {
display: flex;
align-items: center;
gap: 0.3rem;
padding-left: 0.4rem;
border-left: 1px solid @be-gold-border;
.chat-roll-icon {
width: 24px;
height: 24px;
border: 1px solid @be-gold-border;
border-radius: 2px;
object-fit: cover;
flex-shrink: 0;
}
.chat-roll-name {
font-size: 0.75rem;
color: fade(@be-sepia, 80%);
font-style: italic;
max-width: 80px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
// ── Decorative separator ─────────────────────────────────
.chat-card-separator {
height: 14px;
margin: 0.15rem 0.4rem 0;
background: url("../images/ui/separator_01.webp") center/auto 100% no-repeat;
opacity: 0.55;
}
// ── Roll detail rows ─────────────────────────────────────
.chat-roll-details {
margin: 0;
padding: 0 0.4rem 0.15rem;
.chat-detail-row {
display: flex;
justify-content: space-between;
align-items: baseline;
padding: 0.05rem 0;
border-bottom: 1px dotted rgba(139, 105, 20, 0.2);
&:last-child { border-bottom: none; }
dt {
color: fade(@be-sepia, 75%);
font-weight: normal;
font-size: 0.8rem;
}
dd {
font-weight: 600;
color: @be-sepia;
font-size: 0.85rem;
margin-left: 0.5rem;
}
}
// Target and roll value stand out slightly
.chat-detail-target dd,
.chat-detail-roll dd {
font-size: 1rem;
color: @be-bordeaux;
}
}
// ── Result banner ────────────────────────────────────────
.chat-card-result {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.1rem;
margin: 0.2rem 0.4rem 0.25rem;
padding: 0.25rem 0.5rem;
border-radius: 2px;
font-family: @font-rivanna, serif;
font-size: 1.15rem;
text-align: center;
border: 1px solid transparent;
&.result-success {
background: linear-gradient(135deg, rgba(20, 80, 20, 0.12), rgba(40, 100, 40, 0.08));
border-color: rgba(30, 100, 30, 0.35);
color: #1a5c1a;
}
&.result-failure {
background: linear-gradient(135deg, rgba(90, 10, 20, 0.12), rgba(120, 20, 20, 0.08));
border-color: rgba(90, 10, 20, 0.35);
color: @be-bordeaux;
}
i { margin-right: 0.3rem; }
.chat-result-damage {
font-family: sans-serif;
font-size: 0.85rem;
opacity: 0.85;
font-style: normal;
}
}
// ── Action buttons (Relancer, etc.) ──────────────────────
.chat-card-actions {
padding: 0.15rem 0.4rem 0.25rem;
display: flex;
justify-content: center;
.chat-card-button {
padding: 0.3rem 1rem;
font-size: 0.82rem;
border: 1px solid @be-gold-border;
border-radius: 2px;
background: linear-gradient(to bottom, rgba(255,252,240,0.9), rgba(240,230,200,0.9));
color: @be-sepia;
cursor: pointer;
transition: all 0.15s ease;
&:hover {
background: linear-gradient(to bottom, rgba(240,220,170,0.95), rgba(220,195,140,0.95));
border-color: @be-gold;
color: @be-bordeaux;
}
}
}
// ── Tarot card display ──────────────────────────────────
.tarot-card-display {
display: flex;
gap: 0.6rem;
align-items: flex-start;
padding: 0.3rem 0.5rem 0.4rem;
position: relative;
&:hover {
z-index: 100;
.tarot-card-img {
transform: scale(2.5);
box-shadow: 3px 3px 12px rgba(0,0,0,0.4);
}
}
.tarot-card-img {
width: 126px;
flex-shrink: 0;
border: 1px solid @be-gold-border;
border-radius: 2px;
box-shadow: 1px 1px 4px rgba(0,0,0,0.2);
transition: transform 0.2s ease, box-shadow 0.2s ease;
transform-origin: left center;
cursor: zoom-in;
}
.tarot-card-info {
display: flex;
flex-direction: column;
gap: 0.25rem;
padding-top: 0.2rem;
.tarot-card-name {
font-family: @font-rivanna, serif;
font-size: 1rem;
color: @be-bordeaux;
text-shadow: 0 1px 0 rgba(255,255,255,0.4);
}
.tarot-card-side {
font-size: 0.8rem;
font-weight: 600;
&.tarot-positif { color: #1a5c1a; }
&.tarot-negatif { color: @be-bordeaux; }
}
.tarot-card-value {
font-size: 0.82rem;
color: @be-sepia;
strong { color: @be-bordeaux; font-size: 1rem; }
}
}
}
}
// ── Item post message ────────────────────────────────────
.malefices-chat-card.malefices-chat-item {
.item-type-label {
font-family: "Cinzel Decorative", "Cinzel", serif;
font-size: 0.65rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: @be-bordeaux;
opacity: 0.8;
margin-left: auto;
align-self: center;
}
.item-damage {
font-weight: 700;
color: @be-bordeaux;
font-size: 1rem;
&.item-damage-crit {
color: #8b0000;
}
}
.chat-item-description {
margin: 0.4rem 0.5rem 0.3rem;
font-size: 0.82rem;
color: @be-sepia;
line-height: 1.5;
border-top: 1px solid fade(@be-gold-border, 50%);
padding-top: 0.4rem;
p { margin: 0 0 0.3rem; &:last-child { margin: 0; } }
em { color: @be-bordeaux; font-style: italic; }
strong { color: @be-sepia; }
}
}
// ── Welcome message ─────────────────────────────────────
.malefices-chat-card.malefices-welcome {
.welcome-body {
padding: 0.3rem 0.6rem 0.4rem;
font-size: 0.82rem;
color: @be-sepia;
line-height: 1.4;
p { margin: 0.2rem 0; }
.welcome-title {
font-family: @font-rivanna, serif;
font-size: 0.95rem;
color: @be-bordeaux;
font-weight: normal;
margin-bottom: 0.35rem;
}
a { color: @be-gold; &:hover { color: @be-bordeaux; } }
}
.welcome-commands {
margin-top: 0.4rem;
border-top: 1px solid @be-gold-border;
padding-top: 0.3rem;
.welcome-commands-title {
font-weight: 700;
font-size: 0.8rem;
color: @be-sepia;
margin-bottom: 0.2rem;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.welcome-command-row {
font-size: 0.8rem;
color: @be-sepia;
margin: 0.1rem 0;
code {
background: rgba(139,105,20,0.12);
border: 1px solid @be-gold-border;
border-radius: 2px;
padding: 0 3px;
font-size: 0.78rem;
color: @be-bordeaux;
}
}
}
}

616
less/components.less Normal file
View File

@@ -0,0 +1,616 @@
/* ======================================== */
/* Global UI elements */
/* ======================================== */
h1, h2, h3, h4 {
font-weight: bold;
}
ul, ol {
margin: 0;
padding: 0;
}
ul, li {
list-style-type: none;
}
.sheet li {
margin: 0.010rem;
padding: 0.25rem;
}
.header-fields li {
margin: 0;
padding: 0;
}
.alterne-list > .list-item:hover {
background: rgba(100, 100, 50, 0.25);
}
.alterne-list > .list-item:nth-child(even) {
background: rgba(80, 60, 0, 0.10);
}
.alterne-list > .list-item:nth-child(odd) {
background: rgb(160, 130, 100, 0.05);
}
.specialisation-label {
font-size: 0.8rem;
}
.carac-label,
.attr-label {
font-weight: bold;
}
.list-item {
margin: 0.125rem;
/*box-shadow: inset 0px 0px 1px #00000096;
border-radius: 0.25rem;*/
padding: 0.125rem;
flex: 1 1 5rem;
display: flex !important;
color: @color-text;
}
.list-item-shadow {
background:rgba(87, 60, 32, 0.35);
flex-grow: 0;
flex-wrap: nowrap;
justify-content: flex-start;
}
.list-item-shadow2 {
background:rgba(87, 60, 32, 0.25);
flex-grow: 0;
flex-wrap: nowrap;
justify-content: flex-start;
}
.item-display-show {
display: block;
}
.item-display-hide {
display: none;
}
.item-quantite {
margin-left: 0.5rem;
}
.list-item-margin1 {
margin-left: 1rem;
}
.list-item-margin2 {
margin-left: 2rem;
}
.list-item-margin3 {
margin-left: 3rem;
}
.list-item-margin4 {
margin-left: 4rem;
}
.sheet-competence-img {
width: 24px;
max-width: 24px;
height: 24px;
max-height: 24px;
flex-grow: 0;
margin-right: 0.25rem;
}
.competence-column {
flex-direction: column;
align-content: flex-start;
justify-content: flex-start;
flex-grow: 0;
flex-basis: 1;
}
.competence-header {
align-content: flex-start;
justify-content: flex-start;
font-weight: bold;
flex-grow: 0;
}
.description-label {
flex-grow: 2;
margin-left: 4px;
}
.status-header-label {
margin-left: 2px;
}
.roll-dialog-label {
margin: 4px 0;
min-width: 96px;
}
.short-label {
flex-grow: 1;
}
.keyword-label {
font-size: 0.85rem;
}
.item-sheet-label {
flex-grow: 1;
}
.item-text-long-line {
flex-grow: 3;
}
.score-label {
flex-grow: 2;
align-content: center;
}
.attribut-value,
.carac-value {
flex-grow: 0;
flex-basis: 64px;
margin-right: 4px;
margin-left: 4px;
}
.sante-value,
.competence-value {
flex-grow: 0;
flex-basis: 2rem;
margin-right: 0.25rem;
margin-left: 0.25rem;
}
.description-value {
flex-grow: 0;
flex-basis: 4rem;
margin-right: 0.25rem;
margin-left: 0.25rem;
}
.small-label {
margin-top: 5px;
}
.padd-right {
margin-right: 8px;
}
.padd-left {
margin-left: 8px;
}
.stack-left {
align-items:center;
flex-shrink: 1;
flex-grow: 0;
}
.packed-left {
white-space: nowrap;
flex-grow: 0;
}
.input-numeric-short {
width: 40px;
max-width: 40px;
flex-grow: 0;
flex-shrink: 0;
flex-basis: 40px;
margin-right: 0.25rem;
margin-left: 0.25rem;
}
.abilities-table {
align-content: flex-start;
}
/* ======================================== */
.tokenhudext {
display: flex;
flex: 0 !important;
font-weight: 600;
}
.tokenhudext.left {
justify-content: flex-start;
flex-direction: column;
position: absolute;
top: 2.75rem;
right: 4rem;
}
.tokenhudext.right {
justify-content: flex-start;
flex-direction: column;
position: absolute;
top: 2.75rem;
left: 4rem;
}
.control-icon.tokenhudicon {
width: fit-content;
height: fit-content;
min-width: 6rem;
flex-basis: auto;
padding: 0;
line-height: 1rem;
margin: 0.25rem;
}
.control-icon.tokenhudicon.right {
margin-left: 8px;
}
#token-hud .status-effects.active{
z-index: 2;
}
.token-sheet .window-content .flexcol .sheet-tabs {
font-size: 0.8rem;
}
/* ======================================== */
.item-checkbox {
height: 25px;
border: 1px solid #736953a6;
border-left: none;
font-weight: 500;
font-size: 1rem;
color: black;
padding-top: 5px;
margin-right: 0px;
width: 45px;
position: relative;
left: 0px;
text-align: center;
}
.skill-label {
font-size: 0.7rem;
}
.skill-good-checkbox {
max-height: 10px;
max-width: 10px;
}
.flex-actions-bar {
flex-grow: 2;
}
/* ======================================== */
/* Sidebar CSS */
#sidebar {
font-size: 1rem;
/*background-position: 100%;*/
background-color: @color-sidebar-bg;
background-position: 0px 35px;
background-repeat: no-repeat;
background-image: @bg-sheet;
color: @color-text;
}
#sidebar .scene {
color: rgba(237, 240, 199, 0.95);
}
/* background: rgb(105,85,65) url("../images/ui/texture_feuille_perso_onglets.webp") no-repeat right bottom;*/
#sidebar.collapsed {
height: 470px !important;
}
#sidebar-tabs > .collapsed, #chat-controls .chat-control-icon {
color: @color-text;
text-shadow: 1px 1px 0 rgba(0,0,0,0.75);
}
.sidebar-tab .directory-list .entity {
border-top: 1px dashed rgba(0,0,0,0.25);
border-bottom: 0 none;
padding: 0.25rem 0;
}
.sidebar-tab .directory-list .entity:hover {
background: rgba(0,0,0,0.05);
cursor: pointer;
}
.chat-message-header {
background: rgba(220,220,210,0.5);
font-size: 1.1rem;
height: 48px;
text-align: center;
vertical-align: middle;
display: flex;
align-items: center;
}
.chat-message .message-header .flavor-text, .chat-message .message-header .whisper-to {
font-size: 0.9rem;
}
.chat-result-text,
.chat-actor-name {
font-weight: bold;
font-family: Rivanna;
font-size: 1.2rem;
padding: 4px;
}
.chat-result-success {
color:darkgreen;
}
.chat-result-failure {
color:darkred;
}
.chat-img {
width: 64px;
height: 64px;
}
.roll-dialog-header {
height: 52px;
}
.actor-icon {
float: left;
width: 48px;
height: 48px;
padding: 2px 6px 2px 2px;
}
.padding-dice {
padding-top: .2rem;
padding-bottom: .2rem;
}
.dice-image {
box-sizing: border-box;
border: none;
border-radius: 0;
max-width: 100%;
}
.dice-image-reroll {
background-color:rgba(115, 224, 115, 0.25);
border-color: #011d33;
box-sizing: border-box;
border: 1px;
border-radius: 0%;
max-width: 100%;
}
.chat-dice {
width: 15%;
height: 15%;
font-size: 15px;
padding: 10px;
padding-bottom: 20px;
padding-top: .2rem;
padding-bottom: .2rem;
}
.div-center {
align-self: center;
}
.chat-message {
background: rgba(220,220,210,0.5);
font-size: 0.9rem;
}
.chat-message.whisper {
background: rgba(220,220,210,0.75);
border: 2px solid #545469;
}
.chat-message .chat-icon {
border: 0;
padding: 2px 6px 2px 2px;
float: left;
width: 64px;
height: 64px;
}
.ability-icon {
border: 0;
padding: 2px 2px 2px 2px;
max-width:32px;
max-height:32px;
width: auto;
height: auto;
}
.small-ability-icon {
border: 0;
padding: 2px 2px 2px 2px;
max-width:16px;
max-height:16px;
width: auto;
height: auto;
}
.combat-icon {
border: 0;
padding: 2px 2px 2px 2px;
max-width:24px;
max-height:24px;
width: auto;
height: auto;
}
#sidebar-tabs {
flex: 0 0 32px;
box-sizing: border-box;
margin: 0 0 5px;
border-bottom: 1px solid rgba(0,0,0,0);
box-shadow: inset 0 0 2rem rgba(0,0,0,0.5);
}
#sidebar-tabs > .item.active {
border: 1px solid rgba(114,98,72,1);
background: rgba(30, 25, 20, 0.75);
box-shadow: 0 0 6px inset rgba(114,98,72,1);
}
#sidebar #sidebar-tabs i{
display: inline-block;
background-position:center;
background-size:cover;
text-shadow: 1px 1px 0 rgba(0,0,0,0.75);
}
/*--------------------------------------------------------------------------*/
/* Control, Tool, hotbar & navigation */
#controls .scene-control, #controls .control-tool {
box-shadow: 0 0 3px #000;
margin: 0 0 8px;
border-radius: 0;
background: rgba(30, 25, 20, 1);
background-origin: padding-box;
border-image: url(img/ui/footer-button.png) 10 repeat;
border-image-width: 4px;
border-image-outset: 0px;
}
#controls .scene-control.active, #controls .control-tool.active, #controls .scene-control:hover, #controls .control-tool:hover {
background: rgba(72, 46, 28, 1);
background-origin: padding-box;
border-image: url(img/ui/footer-button.png) 10 repeat;
border-image-width: 4px;
border-image-outset: 0px;
box-shadow: 0 0 3px #ff6400;
}
#hotbar #action-bar #macro-list {
border: 1px solid rgba(72, 46, 28, 1);
box-shadow: 2px 2px 5px #000000;
}
#hotbar #action-bar .macro {
border-image: url(img/ui/bg_control.jpg) 21 repeat;
border-image-slice: 6 6 6 6 fill;
border-image-width: 6px 6px 6px 6px;
border-image-outset: 0px 0px 0px 0px;
border-radius: 0px;
}
#hotbar .bar-controls {
background: rgba(30, 25, 20, 1);
border: 1px solid rgba(72, 46, 28, 1);
}
#players {
border-image: url(img/ui/footer-button.png) 10 repeat;
border-image-width: 4px;
border-image-outset: 0px;
background: rgba(30, 25, 20, 1);
}
#navigation #scene-list .scene.nav-item.active {
background: rgba(72, 46, 28, 1);
}
#navigation #scene-list .scene.nav-item {
background: rgba(30, 25, 20, 1);
background-origin: padding-box;
border-image: url(img/ui/footer-button.png) 10 repeat;
border-image-width: 4px;
border-image-outset: 0px;
}
#navigation #scene-list .scene.view, #navigation #scene-list .scene.context {
background: rgba(72, 46, 28, 1);
background-origin: padding-box;
border-image: url(img/ui/footer-button.png) 10 repeat;
border-image-width: 4px;
border-image-outset: 0px;
box-shadow: 0 0 3px #ff6400;
}
#navigation #nav-toggle {
background: rgba(30, 25, 20, 1);
background-origin: padding-box;
border-image: url(img/ui/footer-button.png) 10 repeat;
border-image-width: 4px;
border-image-outset: 0px;
}
/* Tooltip container */
.tooltip {
position: relative;
display: inline-block;
/*border-bottom: 1px dotted black; /* If you want dots under the hoverable text */
}
/* Tooltip text */
.tooltip .tooltiptext {
text-align: left;
background: rgba(231, 229, 226, 0.9);
width: 150px;
padding: 3px 0;
font-size: 0.9rem;
/* Position the tooltip text */
top: 1px;
position: absolute;
z-index: 1;
/* Fade in tooltip */
visibility: hidden;
opacity: 0;
transition: opacity 0.3s;
}
.tooltip-nobottom {
border-bottom: unset; /* If you want dots under the hoverable text */
}
/* Show the tooltip text when you mouse over the tooltip container */
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
.chat-card-button {
box-shadow: inset 0px 1px 0px 0px #a6827e;
background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%);
background-color: #7d5d3b00;
border-radius: 3px;
border: 2px ridge #846109;
display: inline-block;
cursor: pointer;
color: #ffffff;
font-size: 0.8rem;
padding: 4px 12px 0px 12px;
text-decoration: none;
text-shadow: 0px 1px 0px #4d3534;
position: relative;
margin:2px;
}
.chat-card-button:hover {
background: linear-gradient(to bottom, #800000 5%, #3e0101 100%);
background-color: red;
}
.chat-card-button:active {
position:relative;
top:1px;
}
.plus-minus-button {
box-shadow: inset 0px 1px 0px 0px #a6827e;
background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%);
background-color: #7d5d3b00;
border-radius: 2px;
border: 1px ridge #846109;
display: inline-block;
cursor: pointer;
color: #ffffff;
margin: 2px 2px 2px 2px;
padding: 2px 2px 2px 2px;
text-decoration: none;
text-shadow: 0px 1px 0px #4d3534;
position: relative;
margin:0px;
}
.plus-minus-button:hover,
.chat-card-button:hover {
background: linear-gradient(to bottom, #800000 5%, #3e0101 100%);
background-color: red;
}
.plus-minus-button:active,
.chat-card-button:active {
position:relative;
top:1px;
}
.plus-minus {
font-size: 0.9rem;
font-weight: bold;
}
.ul-level1 {
padding-left: 2rem;
}

469
less/dialogs.less Normal file
View File

@@ -0,0 +1,469 @@
/* ===================================================================
AppV2 Dialogs — Style Belle Époque (France, ~1900)
Palette : bordeaux, or antique, sépia, fond parchemin clair
=================================================================== */
// Couleurs Belle Époque
@be-bordeaux: #5a0a14;
@be-gold: #8b6914;
@be-gold-light: rgba(139, 105, 20, 0.25);
@be-gold-border: rgba(139, 105, 20, 0.55);
@be-sepia: #3d2b1f;
@be-sepia-light: rgba(61, 43, 31, 0.08);
.malefices-roll-dialog {
.window-content {
padding: 0;
background: @bg-sheet;
color: @be-sepia;
font-size: 0.85rem;
}
.skill-roll-dialog {
display: flex;
flex-direction: column;
// ── En-tête ──────────────────────────────────────────────
header.roll-dialog-header {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.75rem;
padding: 0.6rem 0.75rem 0.5rem;
background: linear-gradient(to bottom, rgba(90, 10, 20, 0.12), rgba(90, 10, 20, 0.04));
border-bottom: 2px solid @be-gold-border;
}
.actor-icon {
width: 52px;
height: 52px;
object-fit: cover;
border: 2px solid @be-gold-border;
border-radius: 2px;
box-shadow: 0 1px 4px rgba(0,0,0,0.35);
flex-shrink: 0;
}
// Custom title div — no h1 to avoid Foundry theme overrides
.dialog-roll-title {
color: @be-bordeaux;
font-family: @font-rivanna;
font-size: 2rem;
font-weight: bold;
flex: 1;
line-height: 1.1;
text-shadow: 1px 1px 2px rgba(255,255,255,0.6);
}
// ── Séparateur décoratif ──────────────────────────────────
.dialog-separator {
width: 100%;
height: 18px;
background: url("../images/ui/separator_01.webp") center/auto 100% no-repeat;
opacity: 0.7;
margin: 0.1rem 0;
}
// ── Corps du dialog ───────────────────────────────────────
.dialog-body {
display: flex;
flex-direction: column;
gap: 0.4rem;
padding: 0.5rem 0.75rem 0.75rem;
}
// Ligne attribut principal
.dialog-attribute-row {
display: flex;
flex-direction: row;
align-items: baseline;
gap: 0.5rem;
padding: 0.3rem 0.5rem;
background: @be-gold-light;
border: 1px solid @be-gold-border;
border-radius: 2px;
.dialog-attr-label {
font-family: @font-rivanna;
font-size: 1.1rem;
color: @be-bordeaux;
font-weight: bold;
flex: 1;
}
.dialog-attr-value {
font-family: @font-rivanna;
font-size: 1.3rem;
font-weight: bold;
color: @be-sepia;
min-width: 2rem;
text-align: right;
}
}
// Titre de section (éléments biographiques)
.dialog-section-title {
font-family: @font-rivanna;
font-size: 0.95rem;
color: @be-gold;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid @be-gold-border;
padding-bottom: 0.15rem;
margin-top: 0.25rem;
}
// Liste des éléments biographiques
.dialog-bio-list {
list-style: none;
margin: 0 0 0.25rem 0;
padding: 0 0 0 0.5rem;
li {
color: @be-sepia;
font-size: 0.8rem;
padding: 0.1rem 0;
border-bottom: 1px dashed rgba(139, 105, 20, 0.2);
&:last-child { border-bottom: none; }
&::before {
content: "✦ ";
color: @be-gold;
font-size: 0.65rem;
}
}
}
// Zone des modificateurs
.dialog-modifiers {
display: flex;
flex-direction: column;
gap: 0.3rem;
margin-top: 0.2rem;
}
.dialog-modifier-row {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
padding: 0.25rem 0.5rem;
border-bottom: 1px solid rgba(139, 105, 20, 0.18);
&:hover {
background: @be-sepia-light;
}
.dialog-modifier-label {
flex: 1;
color: @be-sepia;
font-size: 0.82rem;
font-weight: 600;
cursor: default;
}
select {
flex: 0 0 140px;
width: 140px;
border: 1px solid @be-gold-border;
background: rgba(255, 252, 245, 0.85);
color: @be-sepia;
font-size: 0.82rem;
padding: 1px 4px;
border-radius: 2px;
&:hover {
border-color: @be-gold;
border-width: 2px;
}
&:focus {
outline: none;
border-color: @be-bordeaux;
box-shadow: 0 0 3px rgba(90, 10, 20, 0.3);
}
}
}
}
footer.form-footer {
display: flex;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
background: linear-gradient(to bottom, rgba(139, 105, 20, 0.08), rgba(139, 105, 20, 0.16));
border-top: 1px solid @be-gold-border;
button {
flex: 1;
padding: 0.4rem 0.75rem;
font-size: 0.9rem;
font-weight: 600;
border: 1px solid @be-gold-border;
border-radius: 2px;
background: linear-gradient(to bottom, rgba(255, 252, 240, 0.9), rgba(240, 230, 200, 0.9));
color: @be-sepia;
cursor: pointer;
transition: all 0.15s ease;
&:hover {
background: linear-gradient(to bottom, rgba(240, 220, 170, 0.95), rgba(220, 195, 140, 0.95));
border-color: @be-gold;
color: @be-bordeaux;
}
&[data-action="roll"],
&[type="submit"] {
background: linear-gradient(to bottom, @be-bordeaux, darken(@be-bordeaux, 8%));
color: rgba(255, 245, 220, 0.95);
border-color: darken(@be-bordeaux, 10%);
font-size: 0.95rem;
&:hover {
background: linear-gradient(to bottom, lighten(@be-bordeaux, 5%), @be-bordeaux);
border-color: @be-bordeaux;
color: #fff;
}
}
}
}
}
/* ===================================================================
Dialogs AppV2 partagés (class: MaleficesDialog)
— utilisé par Tirage Tarot et Résumé des Personnages
=================================================================== */
.MaleficesDialog {
.window-content {
background: @bg-sheet;
padding: 0;
overflow-y: auto;
overflow-x: hidden;
}
// ── Résumé des Personnages ─────────────────────────────
.character-summary-container {
padding: 0.5rem 0.75rem;
color: @be-sepia;
.items-list {
list-style: none;
margin: 0;
padding: 0;
.item {
display: flex;
align-items: center;
padding: 0.2rem 0.4rem;
border-bottom: 1px solid fade(@be-gold-border, 40%);
&:last-child { border-bottom: none; }
&.item-header {
background: linear-gradient(135deg, rgba(90,10,20,0.10), rgba(139,105,20,0.10));
border: 1px solid @be-gold-border;
border-radius: 2px;
margin-bottom: 0.25rem;
font-family: "Cinzel Decorative", "Cinzel", serif;
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.04em;
color: @be-bordeaux;
text-transform: uppercase;
&:not(:first-child) { margin-top: 0.5rem; }
}
&:hover:not(.item-header) {
background: rgba(139, 105, 20, 0.08);
}
}
.item-field {
flex: 1;
text-align: center;
font-size: 0.85rem;
&.item-name { flex: 3; text-align: left; font-weight: 600; }
&.item-name-label-long { flex: 3; text-align: left; }
&.item-name-label-short { flex: 1; text-align: center; }
&.right { text-align: right; }
}
a.summary-roll, a.actor-open {
cursor: pointer;
color: @be-sepia;
&:hover { color: @be-bordeaux; text-decoration: underline; }
}
.actor-delete {
color: fade(@be-sepia, 50%);
font-size: 0.75rem;
cursor: pointer;
&:hover { color: @be-bordeaux; }
}
}
}
.tirage-tarot-dialog {
display: block;
padding: 0.6rem 0.75rem;
color: @be-sepia;
// ── Sélection joueur / attribution ────────────────────
.tirage-select-row {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.4rem 0.6rem;
margin-bottom: 0.5rem;
background: linear-gradient(135deg, rgba(90,10,20,0.08), rgba(139,105,20,0.08));
border: 1px solid @be-gold-border;
border-radius: 2px;
.tirage-select-label {
flex: 1;
font-size: 0.85rem;
font-weight: 600;
color: @be-sepia;
}
.tirage-select {
flex: 0 0 180px;
width: 180px;
border: 1px solid @be-gold-border;
background: rgba(255,252,240,0.85);
color: @be-sepia;
font-size: 0.85rem;
padding: 2px 4px;
border-radius: 2px;
&:focus { outline: none; border-color: @be-bordeaux; }
}
}
.tirage-attribute-row {
background: linear-gradient(135deg, rgba(20,80,20,0.08), rgba(30,100,30,0.05));
border-color: rgba(30,100,30,0.35);
}
// ── Section (main joueur / main secrète) ──────────────
.tirage-section {
display: block;
margin-bottom: 0.5rem;
.tirage-section-title {
display: block;
font-family: @font-rivanna, serif;
font-size: 1rem;
color: @be-bordeaux;
border-bottom: 1px solid @be-gold-border;
padding-bottom: 0.2rem;
margin-bottom: 0.4rem;
text-shadow: 0 1px 0 rgba(255,255,255,0.4);
i { margin-right: 0.35rem; font-size: 0.85em; opacity: 0.7; }
.tirage-player-name {
font-size: 0.85rem;
font-style: italic;
color: @be-sepia;
opacity: 0.8;
}
}
.tirage-secret-title { color: fade(@be-sepia, 70%); }
}
// ── Grille de cartes ──────────────────────────────────
.tirage-cards-row {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
margin-bottom: 0.5rem;
}
.tirage-card {
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 0.2rem;
width: 100px;
vertical-align: top;
&:hover {
position: relative;
z-index: 100;
.tirage-card-img {
transform: scale(2.2);
transform-origin: center top;
box-shadow: 3px 3px 12px rgba(0,0,0,0.4);
}
}
.tirage-card-img {
width: 100px;
border: 1px solid @be-gold-border;
border-radius: 2px;
box-shadow: 1px 1px 4px rgba(0,0,0,0.2);
transition: transform 0.2s ease, box-shadow 0.2s ease;
transform-origin: center center;
cursor: zoom-in;
}
.tirage-card-name {
font-family: @font-rivanna, serif;
font-size: 0.75rem;
color: @be-bordeaux;
text-align: center;
line-height: 1.2;
}
.tirage-card-side {
font-size: 0.7rem;
font-weight: 600;
&.tirage-positif { color: #1a5c1a; }
&.tirage-negatif { color: @be-bordeaux; }
}
&.tirage-card-secret {
opacity: 0.85;
.tirage-card-name { color: fade(@be-sepia, 70%); }
}
}
// ── Séparateur décoratif ──────────────────────────────
.tirage-separator {
display: block;
height: 18px;
background: url("../images/ui/separator_01.webp") center/auto 100% no-repeat;
opacity: 0.5;
margin: 0.25rem 0;
}
}
// ── Footer avec bouton Fermer ─────────────────────────
.tirage-footer {
display: flex;
justify-content: flex-end;
padding: 0.4rem 0.75rem;
border-top: 1px solid @be-gold-border;
background: linear-gradient(to bottom, rgba(139,105,20,0.08), rgba(139,105,20,0.16));
.tirage-close-btn {
padding: 0.3rem 1rem;
font-size: 0.85rem;
border: 1px solid @be-gold-border;
border-radius: 2px;
background: linear-gradient(to bottom, rgba(255,252,240,0.9), rgba(240,230,200,0.9));
color: @be-sepia;
cursor: pointer;
&:hover {
background: linear-gradient(to bottom, rgba(240,220,170,0.95), rgba(220,195,140,0.95));
border-color: @be-gold;
color: @be-bordeaux;
}
}
}
}

136
less/item-sheet.less Normal file
View File

@@ -0,0 +1,136 @@
/* ===================================================================
AppV2 Item Sheets
Selectors use .fvtt-malefices.item (both classes on the same root
element, no space) to target only AppV2 item sheet windows.
=================================================================== */
.fvtt-malefices.item {
/* window-content: background and remove padding.
Foundry AppV2 already sets: display:flex flex-flow:column overflow:hidden */
.window-content {
padding: 0;
background: @bg-sheet;
color: @color-text;
font-size: 0.8rem;
}
/* All sections inside item fill their flex container */
section {
height: 100%;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
overflow: hidden;
}
/* Fixed header bar */
.header {
flex: 0 0 auto;
border-bottom: 1px solid #999;
}
/* sheet-body: scroll container */
section.sheet-body {
overflow-y: auto;
overflow-x: hidden;
padding: 0.25rem 0.5rem;
}
/* Override legacy height: 100% on tab divs; hide all tabs, show only the active one */
.tab[data-tab] {
height: auto;
display: none;
&.active {
display: block;
}
}
/* Header layout override for item sheets (smaller than actor sheet header) */
.sheet-header {
flex: 0 0 auto;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
margin-bottom: 0;
}
.item-sheet-img {
flex: 0 0 64px;
width: 64px;
height: 64px;
object-fit: cover;
border: 1px solid #999;
cursor: pointer;
}
.item-sheet-title {
flex: 1;
h1 {
margin: 0;
padding: 0;
border-bottom: none;
input {
background: transparent;
border: none;
font-family: @font-rivanna;
font-size: 1.8rem;
width: 100%;
height: auto;
}
}
}
.header-actions {
flex: 0 0 auto;
display: flex;
gap: 0.25rem;
align-items: center;
}
/* Tab navigation: AppV2 uses nav.tabs (not nav.sheet-tabs) */
nav.tabs {
display: flex;
flex: 0 0 auto;
font-family: @font-rivanna;
font-size: @tab-font-size;
font-weight: bold;
height: @tab-height;
margin: 0;
padding: 0 0 0 0.25rem;
text-align: center;
text-transform: uppercase;
line-height: @tab-height;
border-top: 0 none;
border-bottom: 0 none;
background-color: @color-tab-bg;
color: @color-tab-text;
gap: 0.25rem;
a.item {
position: relative;
padding: 0 0.5rem;
color: @color-tab-text;
font-family: @font-rivanna;
font-size: @tab-font-size;
text-decoration: none;
line-height: @tab-height;
&:hover {
text-shadow: 1px 0px 0px @color-accent;
}
&.active {
text-shadow: 1px 0px 0px @color-accent;
text-decoration: underline;
}
}
}
}

340
less/legacy-sheets.less Normal file
View File

@@ -0,0 +1,340 @@
/* Styles limited to sheets */
.fvtt-malefices .sheet-header {
-webkit-box-flex: 0;
-ms-flex: 0 0 210px;
flex: 0 0 210px;
overflow: hidden;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
margin-bottom: 10px;
}
.fvtt-malefices .sheet-header .profile-img {
-webkit-box-flex: 0;
-ms-flex: 0 0 128px;
flex: 0 0 128px;
width: 128px;
height: auto;
max-height:128px;
margin-top: 0px;
margin-right: 10px;
object-fit: cover;
object-position: 50% 0;
border-width: 0px;
}
.profile-img-container {
margin-right: 0.2rem;
max-width: 140px;
width: 140px;
}
.button-img {
vertical-align: baseline;
width: 8%;
height: 8%;
max-height: 48px;
border-width: 0px;
border: 1px solid rgba(0, 0, 0, 0);
}
.button-img:hover {
color: rgba(255, 255, 128, 0.7);
border: 1px solid rgba(255, 128, 0, 0.8);
cursor: pointer;
}
.button-effect-img {
vertical-align: baseline;
width: 16px;
max-height: 16px;
height: 16;
border-width: 0;
}
.small-button-container {
height: 16px;
width: 16px;
border: 0;
vertical-align: bottom;
}
.fvtt-malefices .sheet-header .header-fields {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
}
.fvtt-malefices .sheet-header h1.charname {
height: 50px;
padding: 0px;
margin: 5px 0;
border-bottom: 0;
}
.fvtt-malefices .sheet-header h1.charname input {
font-family: Rivanna;
font-size: 3rem;
width: 100%;
height: 100%;
margin: 0;
}
.fvtt-malefices .sheet-tabs {
-webkit-box-flex: 0;
-ms-flex: 0;
flex: 0;
font-family: Rivanna;
font-size: 2.2rem;
}
.fvtt-malefices .sheet-body,
.fvtt-malefices .sheet-body .tab,
.fvtt-malefices .sheet-body .tab .editor {
height: 100%;
font-size: 0.8rem;
}
.editor {
border: 2;
height: 100%;
padding: 0 3px;
}
.medium-editor {
border: 2;
height: 240px;
padding: 0 3px;
}
.small-editor {
border: 2;
height: 120px;
padding: 0 3px;
}
.fvtt-malefices .tox .tox-editor-container {
background: #fff;
}
.fvtt-malefices .tox .tox-edit-area {
padding: 0 8px;
}
.fvtt-malefices .resource-label {
font-weight: bold;
text-transform: uppercase;
}
.fvtt-malefices .tabs {
height: 40px;
border-top: 1px solid #AAA;
border-bottom: 1px solid #AAA;
color: #000000;
}
.fvtt-malefices .tabs .item {
line-height: 40px;
font-weight: bold;
}
.fvtt-malefices .tabs .item.active {
text-decoration: underline;
text-shadow: none;
}
.fvtt-malefices .items-list {
list-style: none;
margin: 1px 0;
padding: 0;
overflow-y: auto;
}
.fvtt-malefices .items-list .item-header {
font-weight: bold;
}
.fvtt-malefices .items-list .item {
height: 30px;
line-height: 24px;
padding: 1px 0;
border-bottom: 1px solid #BBB;
}
.fvtt-malefices .items-list .item .item-image {
-webkit-box-flex: 0;
-ms-flex: 0 0 24px;
flex: 0 0 24px;
margin-right: 5px;
}
.fvtt-malefices .items-list .item img {
display: block;
}
.fvtt-malefices .items-list .item-name {
margin: 0;
}
.fvtt-malefices .items-list .item-controls {
-webkit-box-flex: 0;
-ms-flex: 0 0 86px;
flex: 0 0 86px;
text-align: right;
}
li.folder > .folder-header h3 {
color: @color-text;
}
/* ======================================== */
/* Sheet */
.fvtt-malefices .window-app.sheet .window-content .sheet-header{
color: @color-text;
background: @bg-sheet;
/*background: #494e6b;*/
}
input[type="text"], select[type="text"] {
background:white;
color: @color-input-text;
}
select {
background:white;
color: @color-input-text;
}
/* background: #011d33 url("../images/ui/fond1.webp") repeat left top;*/
/*color: rgba(168, 139, 139, 0.5);*/
.window-app.sheet .window-content .sheet-header select[type="text"], .window-app.sheet .window-content .sheet-header input[type="text"], .window-app.sheet .window-content .sheet-header input[type="number"], .window-app.sheet .window-content .sheet-body input[type="text"], .window-app.sheet .window-content .sheet-body input[type="number"], .window-app.sheet .window-content .sheet-body select[type="text"] {
color: @color-text;
/*color: #494e6b;*/
}
.window-app.sheet .window-content .sheet-header input[type="password"], .window-app.sheet .window-content .sheet-header input[type="date"], .window-app.sheet .window-content .sheet-header input[type="time"] {
color: @color-text;
background: @bg-sheet;
border: 1 none;
margin-bottom: 0.25rem;
margin-left: 2px;
}
.window-app.sheet .window-content .sheet-body input[type="password"], .window-app.sheet .window-content .sheet-body input[type="date"], .window-app.sheet .window-content .sheet-body input[type="time"] {
color: @color-text;
background: @bg-sheet;
border: 1 none;
margin-bottom: 0.25rem;
margin-left: 2px;
}
.window-app.sheet .window-content .sheet-body select, .window-app.sheet .window-content .sheet-header select {
color: @color-text;
background: #fff;
border: 1 none;
margin-bottom: 0.25rem;
margin-left: 2px;
}
.fvtt-malefices .window-app .window-content, .fvtt-malefices .window-app.sheet .window-content .sheet-body{
font-size: 0.8rem;
/*background: url("../images/ui/pc_sheet_bg.webp") repeat left top;*/
background: @bg-sheet;
color: @color-text;
}
/* background: rgba(245,245,240,0.6) url("../images/ui/sheet_background.webp") left top;*/
section.sheet-body{padding: 0.25rem 0.5rem;}
.sheet header.sheet-header .profile-img {
object-fit: cover;
object-position: 50% 0;
margin: 0.5rem 0 0.5rem 0.5rem;
padding: 0;
}
nav.sheet-tabs {
font-size: @tab-font-size;
font-weight: bold;
height: @tab-height;
flex: 0 0 @tab-height;
margin: 0;
padding: 0 0 0 0.25rem;
text-align: center;
text-transform: uppercase;
line-height: 1.5rem;
border-top: 0 none;
border-bottom: 0 none;
background-color: @color-tab-bg;
color: @color-tab-text;
}
/* Dice tray specific overrides */
.dice-tray button svg * {
fill: #6d5923 !important;
}
.dice-tray input[type="text"] {
color: #6d5923 !important;
}
.dice-tray button {
color: #6d5923 !important;
}
nav.sheet-tabs .item {
position: relative;
padding: 0 0.25rem;
color: @color-tab-text;
margin-top: 4px;
margin-bottom: 4px;
}
nav.sheet-tabs .item:after {
content: "";
position: absolute;
top: 0;
right: 0;
height: 2rem;
width: 1px;
/*border-right: 1px dashed rgba(52, 52, 52, 0.25);*/
}
.sheet .tab[data-tab] {
padding: 0;
}
section.sheet-body:after {
content: "";
display: block;
clear: both;
}
.sheet header.sheet-header .flex-compteurs {text-align: right;}
.sheet header.sheet-header .resource-content {width: 2rem;}
.select-diff {
display: inline-block;
text-align: left;
width: 50px;
}
.window-app.sheet .window-content .tooltip:hover .tooltiptext {
top: 2rem;
left: 2rem;
margin: 0;
padding: 0.25rem;
}
.window-app.sheet .window-content .carac-value, .window-app.sheet .window-content .competence-xp {
margin: 0.05rem;
flex-basis: 3rem;
text-align: center;
}

12
less/malefices.less Normal file
View File

@@ -0,0 +1,12 @@
// Main LESS file for Maléfices system
// Importing base styles and component-specific styles
@import "variables";
@import "base";
@import "legacy-sheets";
@import "components";
@import "ui";
@import "dialogs";
@import "chat";
@import "item-sheet";
@import "actor-sheet";

180
less/ui.less Normal file
View File

@@ -0,0 +1,180 @@
/*************************************************************/
#pause
{
font-size: 2rem;
}
#pause > h3
{
color: #CCC
}
#pause > img {
content: url(../images/ui/logo_pause.webp);
height: 200px;
width: 200px;
top: -100px;
left: calc(50% - 132px);
}
#logo {
content : url(../images/ui/logo_pause.webp);
width: 100px;
height: 60px;
}
.dice-cell {
padding-left: 12px;
padding-right: 12px;
width: 60px;
text-align: center;
}
.dice-formula,
.dice-total {
height: 54px;
position:relative;
}
.status-small-label {
font-size: 0.65rem;
}
.no-grow {
flex-grow: 1;
max-width: 32px;
}
.status-col-name {
max-width: 72px;
}
.img-no-border {
max-width: 48px;
max-height: 48px;
border: 0px;
}
.items-title-bg {
margin-top: 6px;
color: @color-text;
}
.items-title-text {
margin-left: 4px;
}
.lock-icon {
width:16px;
height: 16px;
}
.item-sheet-img {
width: 64px;
height: auto;
border: 0;
}
.item-name-img {
flex-grow:1;
max-width: 2rem;
min-width: 2rem;
}
.item-name-label-header {
flex-grow:2;
max-width: 12rem;
min-width: 12rem;
}
.item-name-label-header-long {
flex-grow:2;
max-width: 14rem;
min-width: 14rem;
}
.item-name-label-header-long2 {
flex-grow:2;
max-width: 24rem;
min-width: 24rem;
}
.item-name-label {
flex-grow:2;
max-width: 10rem;
min-width: 10rem;
}
.item-name-label-long {
margin-top: 4px;
flex-grow:2;
max-width: 10rem;
min-width: 10rem;
}
.item-name-label-short {
flex-grow:1;
max-width: 4rem;
min-width: 4rem;
}
.item-name-label-medium {
margin-top: 4px;
flex-grow:2;
max-width: 6rem;
min-width: 6rem;
}
.item-name-label-long2 {
margin-top: 4px;
flex-grow:2;
max-width: 22rem;
min-width: 22rem;
}
.item-name-label-level2 {
flex-grow:2;
max-width: 9rem;
min-width: 9rem;
}
.item-field-label-short {
flex-grow:1;
max-width: 4rem;
min-width: 4rem;
}
.item-field-label-medium {
flex-grow:1;
max-width: 6rem;
min-width: 6rem;
}
.item-field-skill {
flex-grow:1;
max-width: 6.8rem;
min-width: 6.8rem;
}
.item-field-label-long {
flex-grow:1;
max-width: 10rem;
min-width: 10rem;
}
.item-control-end {
align-self: flex-end;
}
.alternate-list {
margin-top: 4px;
flex-wrap: nowrap;
}
.item-filler {
flex-grow: 6;
flex-shrink: 7;
}
.item-controls-fixed {
min-width:2rem;
max-width: 2rem;
}
.attribute-label {
font-weight: bold;
}
.flexrow-no-expand {
flex-grow: 0;
}
.item-input-small {
max-width: 16px;
max-height: 12px;
}
.flip-tarot {
transform: scaleY(-1);
}
.tarot-fixed-width {
width: 140px;
max-width: 140px;
}
.tarot-title {
text-align: center;
font-weight: bold;
}
.character-summary-rollable {
text-decoration: underline;
}

19
less/variables.less Normal file
View File

@@ -0,0 +1,19 @@
// ==================== Variables ====================
// Fonts
@font-rivanna: Rivanna, serif;
// Colors
@color-text: rgba(19, 18, 18, 0.95);
@color-accent: #ff6600;
@color-tab-bg: #252525;
@color-tab-text: beige;
@color-input-text: #494e6b;
@color-sidebar-bg: #f5f5f5;
// Backgrounds
@bg-sheet: url("../images/ui/background_01_clear.webp");
// Tabs
@tab-height: 3rem;
@tab-font-size: 1.2rem;

View File

@@ -0,0 +1,13 @@
/**
* Index des fiches AppV2 pour Maléfices
*/
// Actor sheets
export { default as MaleficesPersonnageSheet } from './malefices-personnage-sheet.mjs';
export { default as MaleficesNPCActorSheet } from './malefices-npc-actor-sheet.mjs';
// Item sheets
export { default as MaleficesArmeSheet } from './malefices-arme-sheet.mjs';
export { default as MaleficesEquipementSheet } from './malefices-equipement-sheet.mjs';
export { default as MaleficesArchetypeSheet } from './malefices-archetype-sheet.mjs';
export { default as MaleficesTarotSheet } from './malefices-tarot-sheet.mjs';
export { default as MaleficesSortilegeSheet } from './malefices-sortilege-sheet.mjs';
export { default as MaleficesElementbioSheet } from './malefices-elementbio-sheet.mjs';

View File

@@ -0,0 +1,132 @@
const { HandlebarsApplicationMixin } = foundry.applications.api
export default class MaleficesItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["fvtt-malefices", "item"],
position: {
width: 620,
height: 600,
},
form: {
submitOnChange: true,
},
window: {
resizable: true,
},
dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }],
actions: {
editImage: MaleficesItemSheet.#onEditImage,
postItem: MaleficesItemSheet.#onPostItem,
},
}
/** @type {object} */
tabGroups = { primary: "description" }
/** @override */
async _prepareContext() {
const context = {
fields: this.document.schema.fields,
systemFields: this.document.system.schema.fields,
item: this.document,
system: this.document.system,
source: this.document.toObject(),
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(
this.document.system.description ?? "", { async: true }
),
isEditable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
isGM: game.user.isGM,
config: game.system.malefices.config,
}
return context
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
this.#dragDrop.forEach((d) => d.bind(this.element))
// Manual tab navigation
const nav = this.element.querySelector('nav.tabs[data-group]')
if (nav) {
const group = nav.dataset.group
const activeTab = this.tabGroups[group] || "description"
nav.querySelectorAll('[data-tab]').forEach(link => {
const tab = link.dataset.tab
link.classList.toggle('active', tab === activeTab)
link.addEventListener('click', (event) => {
event.preventDefault()
this.tabGroups[group] = tab
this.render()
})
})
this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
content.classList.toggle('active', content.dataset.tab === activeTab)
})
}
}
// #region Drag-and-Drop
#createDragDropHandlers() {
return this.options.dragDrop.map((d) => {
d.permissions = {
dragstart: this._canDragStart.bind(this),
drop: this._canDragDrop.bind(this),
}
d.callbacks = {
dragstart: this._onDragStart.bind(this),
dragover: this._onDragOver.bind(this),
drop: this._onDrop.bind(this),
}
return new foundry.applications.ux.DragDrop.implementation(d)
})
}
_canDragStart(selector) { return this.isEditable }
_canDragDrop(selector) { return this.isEditable }
_onDragStart(event) {
const dragData = { type: "Item", uuid: this.document.uuid }
event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
}
_onDragOver(event) {}
async _onDrop(event) {}
// #endregion
// #region Actions
static async #onEditImage(event, target) {
const fp = new FilePicker({
type: "image",
current: this.document.img,
callback: (path) => { this.document.update({ img: path }) },
})
return fp.browse()
}
static async #onPostItem(event, target) {
let chatData = foundry.utils.duplicate(this.document)
if (this.document.actor) {
chatData.actor = { id: this.document.actor.id }
}
if (chatData.img?.includes("/blank.png")) {
chatData.img = null
}
chatData.jsondata = JSON.stringify({ compendium: "postedItem", payload: chatData })
const html = await foundry.applications.handlebars.renderTemplate(
'systems/fvtt-malefices/templates/post-item.hbs', chatData
)
ChatMessage.create({ user: game.user.id, content: html })
}
// #endregion
}

View File

@@ -0,0 +1,35 @@
import MaleficesItemSheet from "./base-item-sheet.mjs"
import { MaleficesUtility } from "../../malefices-utility.js"
export default class MaleficesArchetypeSheet extends MaleficesItemSheet {
static DEFAULT_OPTIONS = {
classes: ["archetype"],
position: { width: 620 },
window: { contentClasses: ["archetype-content"] },
}
static PARTS = {
main: { template: "systems/fvtt-malefices/templates/items/item-archetype-sheet.hbs" },
}
tabGroups = { primary: "details" }
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" },
}
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()
context.tarots = MaleficesUtility.getTarots()
return context
}
}

View File

@@ -0,0 +1,33 @@
import MaleficesItemSheet from "./base-item-sheet.mjs"
export default class MaleficesArmeSheet extends MaleficesItemSheet {
static DEFAULT_OPTIONS = {
classes: ["arme"],
position: { width: 640 },
window: { contentClasses: ["arme-content"] },
}
static PARTS = {
main: { template: "systems/fvtt-malefices/templates/items/item-arme-sheet.hbs" },
}
tabGroups = { primary: "details" }
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" },
}
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()
return context
}
}

View File

@@ -0,0 +1,225 @@
const { HandlebarsApplicationMixin } = foundry.applications.api
import { MaleficesUtility } from "../../malefices-utility.js"
export default class MaleficesActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
this._editScore = true
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["fvtt-malefices", "actor"],
position: {
width: 640,
height: 680,
},
form: {
submitOnChange: true,
},
window: {
resizable: true,
},
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
actions: {
editImage: MaleficesActorSheet.#onEditImage,
toggleSheet: MaleficesActorSheet.#onToggleSheet,
editItem: MaleficesActorSheet.#onEditItem,
deleteItem: MaleficesActorSheet.#onDeleteItem,
createItem: MaleficesActorSheet.#onCreateItem,
equipItem: MaleficesActorSheet.#onEquipItem,
modifyQuantity: MaleficesActorSheet.#onModifyQuantity,
modifyAmmo: MaleficesActorSheet.#onModifyAmmo,
rollAttribut: MaleficesActorSheet.#onRollAttribut,
rollArme: MaleficesActorSheet.#onRollArme,
editSubActor: MaleficesActorSheet.#onEditSubActor,
deleteSubActor: MaleficesActorSheet.#onDeleteSubActor,
},
}
/** @type {object} */
tabGroups = { primary: "main" }
/** @override */
async _prepareContext() {
const actor = this.document
return {
actor,
system: actor.system,
source: actor.toObject(),
fields: actor.schema.fields,
systemFields: actor.system.schema.fields,
isEditable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
isGM: game.user.isGM,
config: game.system.malefices.config,
editScore: this._editScore,
}
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
this.#dragDrop.forEach((d) => d.bind(this.element))
// Ignore Enter key in text inputs (not textarea)
this.element.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && e.target.tagName !== 'TEXTAREA') e.preventDefault()
})
// Manual tab navigation
const nav = this.element.querySelector('nav.tabs[data-group]')
if (nav) {
const group = nav.dataset.group
const activeTab = this.tabGroups[group] || "main"
nav.querySelectorAll('[data-tab]').forEach(link => {
link.classList.toggle('active', link.dataset.tab === activeTab)
link.addEventListener('click', (event) => {
event.preventDefault()
this.tabGroups[group] = link.dataset.tab
this.render()
})
})
this.element.querySelectorAll(`[data-group="${group}"][data-tab]`).forEach(content => {
content.classList.toggle('active', content.dataset.tab === activeTab)
})
}
// Handle .update-field change events (legacy support)
this.element.querySelectorAll('.update-field').forEach(el => {
el.addEventListener('change', (ev) => {
const fieldName = ev.currentTarget.dataset.fieldName
const value = Number(ev.currentTarget.value)
this.actor.update({ [fieldName]: value })
})
})
}
// #region Drag-and-Drop
#createDragDropHandlers() {
return this.options.dragDrop.map((d) => {
d.permissions = {
dragstart: this._canDragStart.bind(this),
drop: this._canDragDrop.bind(this),
}
d.callbacks = {
dragstart: this._onDragStart.bind(this),
dragover: this._onDragOver.bind(this),
drop: this._onDrop.bind(this),
}
return new foundry.applications.ux.DragDrop.implementation(d)
})
}
_canDragStart(selector) { return this.isEditable }
_canDragDrop(selector) { return this.isEditable }
_onDragStart(event) {
const li = event.currentTarget.closest('.item')
if (!li) return
const itemId = li.dataset.itemId
const item = this.actor.items.get(itemId)
if (item) {
event.dataTransfer.setData("text/plain", JSON.stringify({ type: "Item", uuid: item.uuid }))
}
}
_onDragOver(event) {}
async _onDrop(event) {
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
if (data?.type === "Actor") {
const actor = await fromUuid(data.uuid)
if (actor) this.actor.addSubActor(actor.id)
} else {
super._onDrop(event)
}
}
// #endregion
// #region Actions
static async #onEditImage(event, target) {
const fp = new FilePicker({
type: "image",
current: this.document.img,
callback: (path) => { this.document.update({ img: path }) },
})
return fp.browse()
}
static async #onToggleSheet(event, target) {
this._editScore = !this._editScore
this.render()
}
static async #onEditItem(event, target) {
const li = target.closest(".item")
const itemId = li?.dataset.itemId
if (!itemId) return
this.actor.items.get(itemId)?.sheet.render(true)
}
static async #onDeleteItem(event, target) {
const li = target.closest(".item")
MaleficesUtility.confirmDelete(this, li)
}
static async #onCreateItem(event, target) {
const dataType = target.dataset.type
this.actor.createEmbeddedDocuments('Item', [{ name: "NewItem", type: dataType }], { renderSheet: true })
}
static async #onEquipItem(event, target) {
const li = target.closest(".item")
const itemId = li?.dataset.itemId
if (!itemId) return
await this.actor.equipItem(itemId)
this.render()
}
static async #onModifyQuantity(event, target) {
const li = target.closest(".item")
const itemId = li?.dataset.itemId
if (!itemId) return
const delta = parseInt(target.dataset.delta) || 0
this.actor.incDecQuantity(itemId, delta)
}
static async #onModifyAmmo(event, target) {
const li = target.closest(".item")
const itemId = li?.dataset.itemId
if (!itemId) return
const delta = parseInt(target.dataset.delta) || 0
this.actor.incDecAmmo(itemId, delta)
}
static async #onRollAttribut(event, target) {
const attrKey = target.dataset.attrKey
this.actor.rollAttribut(attrKey)
}
static async #onRollArme(event, target) {
const armeId = target.dataset.armeId
this.actor.rollArme(armeId)
}
static async #onEditSubActor(event, target) {
const li = target.closest(".item")
const actorId = li?.dataset.actorId
if (!actorId) return
game.actors.get(actorId)?.sheet.render(true)
}
static async #onDeleteSubActor(event, target) {
const li = target.closest(".item")
const actorId = li?.dataset.actorId
if (!actorId) return
this.actor.delSubActor(actorId)
}
// #endregion
}

View File

@@ -0,0 +1,32 @@
import MaleficesItemSheet from "./base-item-sheet.mjs"
export default class MaleficesElementbioSheet extends MaleficesItemSheet {
static DEFAULT_OPTIONS = {
classes: ["elementbio"],
position: { width: 620 },
window: { contentClasses: ["elementbio-content"] },
}
static PARTS = {
main: { template: "systems/fvtt-malefices/templates/items/item-elementbio-sheet.hbs" },
}
tabGroups = { primary: "description" }
#getTabs() {
const tabs = {
description: { id: "description", group: "primary", label: "Description" },
}
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()
return context
}
}

View File

@@ -0,0 +1,32 @@
import MaleficesItemSheet from "./base-item-sheet.mjs"
export default class MaleficesEquipementSheet extends MaleficesItemSheet {
static DEFAULT_OPTIONS = {
classes: ["equipement"],
position: { width: 620 },
window: { contentClasses: ["equipement-content"] },
}
static PARTS = {
main: { template: "systems/fvtt-malefices/templates/items/item-equipement-sheet.hbs" },
}
tabGroups = { primary: "description" }
#getTabs() {
const tabs = {
description: { id: "description", group: "primary", label: "Description" },
}
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()
return context
}
}

View File

@@ -0,0 +1,27 @@
import MaleficesActorSheet from "./malefices-base-actor-sheet.mjs"
export default class MaleficesNPCActorSheet extends MaleficesActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["pnj"],
position: { width: 560, height: 460 },
}
/** @override */
static PARTS = {
main: { template: "systems/fvtt-malefices/templates/actors/npc-sheet.hbs" },
}
/** @override */
tabGroups = { primary: "main" }
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
this.document.system.description ?? "", { async: true }
)
return context
}
}

View File

@@ -0,0 +1,48 @@
import MaleficesActorSheet from "./malefices-base-actor-sheet.mjs"
export default class MaleficesPersonnageSheet extends MaleficesActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["personnage"],
position: { width: 640, height: 680 },
}
/** @override */
static PARTS = {
main: { template: "systems/fvtt-malefices/templates/actors/actor-sheet.hbs" },
}
/** @override */
tabGroups = { primary: "main" }
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
const actor = this.document
context.armes = foundry.utils.duplicate(actor.getArmes())
context.tarots = foundry.utils.duplicate(actor.getTarots())
context.tarotsCache = foundry.utils.duplicate(actor.getHiddenTarots())
context.archetype = foundry.utils.duplicate(actor.getArchetype())
context.equipements = foundry.utils.duplicate(actor.getEquipements())
context.elementsbio = actor.getElementsBio()
context.sorts = actor.getSorts()
context.phyMalus = actor.getPhysiqueMalus()
context.subActors = foundry.utils.duplicate(actor.getSubActors())
// Expose nested biodata schema fields for {{formInput}} helper
context.biodataFields = actor.system.schema.fields.biodata.fields
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
actor.system.biodata?.description ?? "", { async: true }
)
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
actor.system.biodata?.notes ?? "", { async: true }
)
context.enrichedEquipementlibre = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
actor.system.equipementlibre ?? "", { async: true }
)
return context
}
}

View File

@@ -0,0 +1,33 @@
import MaleficesItemSheet from "./base-item-sheet.mjs"
export default class MaleficesSortilegeSheet extends MaleficesItemSheet {
static DEFAULT_OPTIONS = {
classes: ["sortilege"],
position: { width: 620 },
window: { contentClasses: ["sortilege-content"] },
}
static PARTS = {
main: { template: "systems/fvtt-malefices/templates/items/item-sortilege-sheet.hbs" },
}
tabGroups = { primary: "details" }
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" },
}
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()
return context
}
}

View File

@@ -0,0 +1,33 @@
import MaleficesItemSheet from "./base-item-sheet.mjs"
export default class MaleficesTarotSheet extends MaleficesItemSheet {
static DEFAULT_OPTIONS = {
classes: ["tarot"],
position: { width: 660, height: 640 },
window: { contentClasses: ["tarot-content"] },
}
static PARTS = {
main: { template: "systems/fvtt-malefices/templates/items/item-tarot-sheet.hbs" },
}
tabGroups = { primary: "details" }
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" },
}
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()
return context
}
}

View File

@@ -6,12 +6,12 @@
import { MaleficesUtility } from "./malefices-utility.js"; import { MaleficesUtility } from "./malefices-utility.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class MaleficesActorSheet extends ActorSheet { export class MaleficesActorSheet extends foundry.appv1.sheets.ActorSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
return mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["fvtt-malefices", "sheet", "actor", "malefices-actor-sheet"], classes: ["fvtt-malefices", "sheet", "actor", "malefices-actor-sheet"],
template: "systems/fvtt-malefices/templates/actors/actor-sheet.hbs", template: "systems/fvtt-malefices/templates/actors/actor-sheet.hbs",
width: 640, width: 640,
@@ -33,20 +33,20 @@ export class MaleficesActorSheet extends ActorSheet {
name: this.actor.name, name: this.actor.name,
editable: this.isEditable, editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked", cssClass: this.isEditable ? "editable" : "locked",
system: duplicate(this.object.system), system: foundry.utils.duplicate(this.object.system),
limited: this.object.limited, limited: this.object.limited,
armes: duplicate(this.actor.getArmes()), armes: foundry.utils.duplicate(this.actor.getArmes()),
tarots: duplicate(this.actor.getTarots()), tarots: foundry.utils.duplicate(this.actor.getTarots()),
tarotsCache: duplicate(this.actor.getHiddenTarots()), tarotsCache: foundry.utils.duplicate(this.actor.getHiddenTarots()),
archetype: duplicate(this.actor.getArchetype()), archetype: foundry.utils.duplicate(this.actor.getArchetype()),
equipements: duplicate(this.actor.getEquipements()), equipements: foundry.utils.duplicate(this.actor.getEquipements()),
subActors: duplicate(this.actor.getSubActors()), subActors: foundry.utils.duplicate(this.actor.getSubActors()),
phyMalus: this.actor.getPhysiqueMalus(), phyMalus: this.actor.getPhysiqueMalus(),
elementsbio: this.actor.getElementsBio(), elementsbio: this.actor.getElementsBio(),
sorts: this.actor.getSorts(), sorts: this.actor.getSorts(),
description: await TextEditor.enrichHTML(this.object.system.biodata.description, { async: true }), description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.description, { async: true }),
notes: await TextEditor.enrichHTML(this.object.system.biodata.notes, { async: true }), notes: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.notes, { async: true }),
equipementlibre: await TextEditor.enrichHTML(this.object.system.equipementlibre, { async: true }), equipementlibre: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.equipementlibre, { async: true }),
options: this.options, options: this.options,
owner: this.document.isOwner, owner: this.document.isOwner,
editScore: this.options.editScore, editScore: this.options.editScore,
@@ -66,10 +66,10 @@ export class MaleficesActorSheet extends ActorSheet {
// Everything below here is only needed if the sheet is editable // Everything below here is only needed if the sheet is editable
if (!this.options.editable) return; if (!this.options.editable) return;
html.bind("keydown", function(e) { // Ignore Enter in actores sheet html.bind("keydown", function(e) { // Ignore Enter in actores sheet
if (e.keyCode === 13) return false; if (e.keyCode === 13) return false;
}); });
// Update Inventory Item // Update Inventory Item
html.find('.item-edit').click(ev => { html.find('.item-edit').click(ev => {
@@ -87,14 +87,14 @@ export class MaleficesActorSheet extends ActorSheet {
let dataType = $(ev.currentTarget).data("type") let dataType = $(ev.currentTarget).data("type")
this.actor.createEmbeddedDocuments('Item', [{ name: "NewItem", type: dataType }], { renderSheet: true }) this.actor.createEmbeddedDocuments('Item', [{ name: "NewItem", type: dataType }], { renderSheet: true })
}) })
html.find('.subactor-edit').click(ev => { html.find('.subactor-edit').click(ev => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
let actorId = li.data("actor-id"); let actorId = li.data("actor-id");
let actor = game.actors.get( actorId ); let actor = game.actors.get( actorId );
actor.sheet.render(true); actor.sheet.render(true);
}); });
html.find('.subactor-delete').click(ev => { html.find('.subactor-delete').click(ev => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
let actorId = li.data("actor-id"); let actorId = li.data("actor-id");
@@ -117,38 +117,38 @@ export class MaleficesActorSheet extends ActorSheet {
const li = $(event.currentTarget).parents(".item") const li = $(event.currentTarget).parents(".item")
this.actor.incDecAmmo( li.data("item-id"), +1 ) this.actor.incDecAmmo( li.data("item-id"), +1 )
} ); } );
html.find('.roll-attribut').click((event) => { html.find('.roll-attribut').click((event) => {
let attrKey = $(event.currentTarget).data("attr-key") let attrKey = $(event.currentTarget).data("attr-key")
this.actor.rollAttribut(attrKey) this.actor.rollAttribut(attrKey)
}); });
html.find('.roll-arme').click((event) => { html.find('.roll-arme').click((event) => {
const armeId = $(event.currentTarget).data("arme-id") const armeId = $(event.currentTarget).data("arme-id")
this.actor.rollArme(armeId) this.actor.rollArme(armeId)
}); });
html.find('.lock-unlock-sheet').click((event) => { html.find('.lock-unlock-sheet').click((event) => {
this.options.editScore = !this.options.editScore; this.options.editScore = !this.options.editScore;
this.render(true); this.render(true);
}); });
html.find('.item-link a').click((event) => { html.find('.item-link a').click((event) => {
const itemId = $(event.currentTarget).data("item-id"); const itemId = $(event.currentTarget).data("item-id");
const item = this.actor.getOwnedItem(itemId); const item = this.actor.getOwnedItem(itemId);
item.sheet.render(true); item.sheet.render(true);
}); });
html.find('.item-equip').click(ev => { html.find('.item-equip').click(ev => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
this.actor.equipItem( li.data("item-id") ); this.actor.equipItem( li.data("item-id") );
this.render(true); this.render(true);
}); });
html.find('.update-field').change(ev => { html.find('.update-field').change(ev => {
const fieldName = $(ev.currentTarget).data("field-name"); const fieldName = $(ev.currentTarget).data("field-name");
let value = Number(ev.currentTarget.value); let value = Number(ev.currentTarget.value);
this.actor.update( { [`${fieldName}`]: value } ); this.actor.update( { [`${fieldName}`]: value } );
}); });
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
/** @override */ /** @override */
setPosition(options = {}) { setPosition(options = {}) {

View File

@@ -29,7 +29,7 @@ export class MaleficesActor extends Actor {
if (data instanceof Array) { if (data instanceof Array) {
return super.create(data, options); return super.create(data, options);
} }
// If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic // If the created actor has items (only applicable to foundry.utils.duplicated actors) bypass the new actor creation logic
if (data.items) { if (data.items) {
let actor = super.create(data, options); let actor = super.create(data, options);
return actor; return actor;
@@ -92,7 +92,7 @@ export class MaleficesActor extends Actor {
return comp; return comp;
} }
getArchetype() { getArchetype() {
let comp = duplicate(this.items.find(item => item.type == 'archetype') || {name: "Pas d'archetype"}) let comp = foundry.utils.duplicate(this.items.find(item => item.type == 'archetype') || {name: "Pas d'archetype"})
if (comp && comp.system) { if (comp && comp.system) {
comp.tarot = MaleficesUtility.getTarot(comp.system.lametutelaire) comp.tarot = MaleficesUtility.getTarot(comp.system.lametutelaire)
} }
@@ -101,25 +101,25 @@ export class MaleficesActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getElementsBio() { getElementsBio() {
let comp = duplicate(this.items.filter(item => item.type == 'elementbio') || []) let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'elementbio') || [])
MaleficesUtility.sortArrayObjectsByName(comp) MaleficesUtility.sortArrayObjectsByName(comp)
return comp; return comp;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getTarots() { getTarots() {
let comp = duplicate(this.items.filter(item => item.type == 'tarot' && !item.system.isgm) || []) let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'tarot' && !item.system.isgm) || [])
MaleficesUtility.sortArrayObjectsByName(comp) MaleficesUtility.sortArrayObjectsByName(comp)
return comp; return comp;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getHiddenTarots() { getHiddenTarots() {
let comp = duplicate(this.items.filter(item => item.type == 'tarot' && item.system.isgm) || []) let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'tarot' && item.system.isgm) || [])
MaleficesUtility.sortArrayObjectsByName(comp) MaleficesUtility.sortArrayObjectsByName(comp)
return comp; return comp;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getArmes() { getArmes() {
let comp = duplicate(this.items.filter(item => item.type == 'arme') || []) let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'arme') || [])
MaleficesUtility.sortArrayObjectsByName(comp) MaleficesUtility.sortArrayObjectsByName(comp)
return comp; return comp;
} }
@@ -127,7 +127,7 @@ export class MaleficesActor extends Actor {
getItemById(id) { getItemById(id) {
let item = this.items.find(item => item.id == id); let item = this.items.find(item => item.id == id);
if (item) { if (item) {
item = duplicate(item) item = foundry.utils.duplicate(item)
} }
return item; return item;
} }
@@ -173,7 +173,7 @@ export class MaleficesActor extends Actor {
/* ------------------------------------------- */ /* ------------------------------------------- */
async buildContainerTree() { async buildContainerTree() {
let equipments = duplicate(this.items.filter(item => item.type == "equipment") || []) let equipments = foundry.utils.duplicate(this.items.filter(item => item.type == "equipment") || [])
for (let equip1 of equipments) { for (let equip1 of equipments) {
if (equip1.system.iscontainer) { if (equip1.system.iscontainer) {
equip1.system.contents = [] equip1.system.contents = []
@@ -238,13 +238,13 @@ export class MaleficesActor extends Actor {
getSubActors() { getSubActors() {
let subActors = []; let subActors = [];
for (let id of this.system.subactors) { for (let id of this.system.subactors) {
subActors.push(duplicate(game.actors.get(id))) subActors.push(foundry.utils.duplicate(game.actors.get(id)))
} }
return subActors; return subActors;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async addSubActor(subActorId) { async addSubActor(subActorId) {
let subActors = duplicate(this.system.subactors); let subActors = foundry.utils.duplicate(this.system.subactors);
subActors.push(subActorId); subActors.push(subActorId);
await this.update({ 'system.subactors': subActors }); await this.update({ 'system.subactors': subActors });
} }
@@ -291,7 +291,7 @@ export class MaleficesActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
incDecAttr(attrKey, value) { incDecAttr(attrKey, value) {
let attr = duplicate(this.system.attributs[attrKey]) let attr = foundry.utils.duplicate(this.system.attributs[attrKey])
attr.value += value attr.value += value
this.update( { [`system.attributs.${attrKey}`]: attr}) this.update( { [`system.attributs.${attrKey}`]: attr})
} }
@@ -341,6 +341,7 @@ export class MaleficesActor extends Actor {
rollData.isReroll = false rollData.isReroll = false
rollData.confrontationDegre = 0 rollData.confrontationDegre = 0
rollData.confrontationModif = 0 rollData.confrontationModif = 0
rollData.config = game.system.malefices.config
console.log("ROLLDATA", rollData) console.log("ROLLDATA", rollData)
@@ -358,7 +359,7 @@ export class MaleficesActor extends Actor {
rollAttribut(attrKey) { rollAttribut(attrKey) {
let attr = this.system.attributs[attrKey] let attr = this.system.attributs[attrKey]
let rollData = this.getCommonRollData() let rollData = this.getCommonRollData()
rollData.attr = duplicate(attr) rollData.attr = foundry.utils.duplicate(attr)
rollData.mode = "attribut" rollData.mode = "attribut"
rollData.title = attr.label rollData.title = attr.label
rollData.img = this.getAtttributImage(attrKey) rollData.img = this.getAtttributImage(attrKey)
@@ -369,12 +370,12 @@ export class MaleficesActor extends Actor {
rollArme(weaponId) { rollArme(weaponId) {
let arme = this.items.get(weaponId) let arme = this.items.get(weaponId)
if (arme) { if (arme) {
arme = duplicate(arme) arme = foundry.utils.duplicate(arme)
let rollData = this.getCommonRollData() let rollData = this.getCommonRollData()
if (arme.system.armetype == "mainsnues" || arme.system.armetype == "epee") { if (arme.system.armetype == "mainsnues" || arme.system.armetype == "epee") {
rollData.attr = { label: "(Physique+Habilité)/2", value: Math.floor( (this.getPhysiqueMalus()+this.system.attributs.physique.value+this.system.attributs.habilite.value) / 2) } rollData.attr = { label: "(Physique+Habilité)/2", value: Math.floor( (this.getPhysiqueMalus()+this.system.attributs.physique.value+this.system.attributs.habilite.value) / 2) }
} else { } else {
rollData.attr = duplicate(this.system.attributs.habilite) rollData.attr = foundry.utils.duplicate(this.system.attributs.habilite)
} }
rollData.mode = "arme" rollData.mode = "arme"
rollData.arme = arme rollData.arme = arme
@@ -388,8 +389,7 @@ export class MaleficesActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async startRoll(rollData) { async startRoll(rollData) {
let rollDialog = await MaleficesRollDialog.create(this, rollData) await MaleficesRollDialog.create(this, rollData)
rollDialog.render(true)
} }
} }

View File

@@ -80,7 +80,7 @@ export class MaleficesCommands {
console.log("===> Processing command") console.log("===> Processing command")
let command = commandsTable[name]; let command = commandsTable[name];
path = path + name + " "; path = path + name + " ";
if (command && command.subTable) { if (command?.subTable) {
if (params[0]) { if (params[0]) {
return this._processCommand(command.subTable, params[0], params.slice(1), content, msg, path) return this._processCommand(command.subTable, params[0], params.slice(1), content, msg, path)
} }
@@ -89,7 +89,7 @@ export class MaleficesCommands {
return true; return true;
} }
} }
if (command && command.func) { if (command?.func) {
const result = command.func(content, msg, params); const result = command.func(content, msg, params);
if (result == false) { if (result == false) {
CrucibleCommands._chatAnswer(msg, command.descr); CrucibleCommands._chatAnswer(msg, command.descr);
@@ -115,7 +115,7 @@ export class MaleficesCommands {
maxPlayerCard: 4, maxPlayerCard: 4,
maxSecretCard: 1, maxSecretCard: 1,
cards: [], cards: [],
players: duplicate(game.users), players: foundry.utils.duplicate(game.users),
secretCards: [], secretCards: [],
deck: MaleficesUtility.getTarots() deck: MaleficesUtility.getTarots()
} }
@@ -125,7 +125,6 @@ export class MaleficesCommands {
tirageData.secretCards.push({ name: "???", img: "systems/fvtt-malefices/images/tarots/background.webp" }) tirageData.secretCards.push({ name: "???", img: "systems/fvtt-malefices/images/tarots/background.webp" })
let tirageDialog = await MaleficesTirageTarotDialog.create(this, tirageData) let tirageDialog = await MaleficesTirageTarotDialog.create(this, tirageData)
tirageDialog.render(true)
} }
} }
/* --------------------------------------------- */ /* --------------------------------------------- */
@@ -140,7 +139,7 @@ export class MaleficesCommands {
selectedCard.system.isgm = false selectedCard.system.isgm = false
selectedCard.value = (selectedCard.system.ispositif)? selectedCard.system.numericvalueup : selectedCard.system.numericvaluedown selectedCard.value = (selectedCard.system.ispositif)? selectedCard.system.numericvalueup : selectedCard.system.numericvaluedown
MaleficesUtility.createChatMessage(game.user.name, "", { MaleficesUtility.createChatMessage(game.user.name, "", {
content: await renderTemplate(`systems/fvtt-malefices/templates/chat/display-tarot-card.hbs`, selectedCard) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-malefices/templates/chat/display-tarot-card.hbs`, selectedCard)
}) })
} }

View File

@@ -27,4 +27,41 @@ export const MALEFICES_CONFIG = {
"epee": "Epée, sabre, javelot, etc", "epee": "Epée, sabre, javelot, etc",
"mainsnues": "Mains Nues" "mainsnues": "Mains Nues"
}, },
confrontationDegreOptions :{
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5"
},
confrontationModifOptions: {
"-1": "-1",
"0": "0",
"1": "+1"
},
bonusMalusPersoOptions: [
{value: "-3", label: "-3"},
{value: "-2", label: "-2"},
{value: "-1", label: "-1"},
{value: "0", label: "0"},
{value: "+1", label: "+1"},
{value: "+2", label: "+2"},
{value: "+3", label: "+3"}
],
bonusMalusDefOptions: [
{value: "-6", label: "-6 (réussite critique)"},
{value: "-3", label: "-3 (réussite)"},
{value: "0", label: "0 (echec ou pas d'esquive)"},
{value: "+3", label: "+3 (echec critique)"}
],
bonusMalusPorteeOptions: [
{value: "1", label: "+1 (Portée courte)"},
{value: "0", label: "0 (Portée moyenne)"},
{value: "-1", label: "-1 (Portée longue)"}
]
} }

View File

@@ -4,12 +4,12 @@ import { MaleficesUtility } from "./malefices-utility.js";
* Extend the basic ItemSheet with some very simple modifications * Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet} * @extends {ItemSheet}
*/ */
export class MaleficesItemSheet extends ItemSheet { export class MaleficesItemSheet extends foundry.appv1.sheets.ItemSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
return mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["fvtt-malefices", "sheet", "item"], classes: ["fvtt-malefices", "sheet", "item"],
template: "systems/fvtt-malefices/templates/item-sheet.hbs", template: "systems/fvtt-malefices/templates/item-sheet.hbs",
dragDrop: [{ dragSelector: null, dropSelector: null }], dragDrop: [{ dragSelector: null, dropSelector: null }],
@@ -43,20 +43,20 @@ export class MaleficesItemSheet extends ItemSheet {
name: this.object.name, name: this.object.name,
editable: this.isEditable, editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked", cssClass: this.isEditable ? "editable" : "locked",
system: duplicate(this.object.system), system: foundry.utils.duplicate(this.object.system),
config: duplicate(game.system.malefices.config), config: foundry.utils.duplicate(game.system.malefices.config),
limited: this.object.limited, limited: this.object.limited,
options: this.options, options: this.options,
owner: this.document.isOwner, owner: this.document.isOwner,
description: await TextEditor.enrichHTML(this.object.system.description, { async: true }), description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.description, { async: true }),
notes: await TextEditor.enrichHTML(this.object.system.notes, { async: true }), notes: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.notes, { async: true }),
isGM: game.user.isGM isGM: game.user.isGM
} }
if ( this.object.type == "archetype") { if ( this.object.type == "archetype") {
formData.tarots = MaleficesUtility.getTarots() formData.tarots = MaleficesUtility.getTarots()
} }
this.options.editable = !(this.object.origin == "embeddedItem"); this.options.editable = !(this.object.origin == "embeddedItem");
console.log("ITEM DATA", formData, this); console.log("ITEM DATA", formData, this);
return formData; return formData;
@@ -76,24 +76,18 @@ export class MaleficesItemSheet extends ItemSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
postItem() { postItem() {
let chatData = duplicate(this.item) let chatData = foundry.utils.duplicate(this.item)
if (this.actor) { if (this.actor) {
chatData.actor = { id: this.actor.id }; chatData.actor = { id: this.actor.id };
} }
// Don't post any image for the item (which would leave a large gap) if the default image is used if (chatData.img?.includes("/blank.png")) {
if (chatData.img.includes("/blank.png")) {
chatData.img = null; chatData.img = null;
} }
// JSON object for easy creation chatData.config = game.system.malefices.config
chatData.jsondata = JSON.stringify( chatData.jsondata = JSON.stringify({ compendium: "postedItem", payload: chatData })
{
compendium: "postedItem",
payload: chatData,
});
renderTemplate('systems/Malefices/templates/post-item.html', chatData).then(html => { foundry.applications.handlebars.renderTemplate('systems/fvtt-malefices/templates/post-item.hbs', chatData).then(html => {
let chatOptions = MaleficesUtility.chatDataSetup(html); ChatMessage.create(MaleficesUtility.chatDataSetup(html))
ChatMessage.create(chatOptions)
}); });
} }
@@ -102,7 +96,7 @@ export class MaleficesItemSheet extends ItemSheet {
let levelIndex = Number($(ev.currentTarget).parents(".item").data("level-index")) let levelIndex = Number($(ev.currentTarget).parents(".item").data("level-index"))
let choiceIndex = Number($(ev.currentTarget).parents(".item").data("choice-index")) let choiceIndex = Number($(ev.currentTarget).parents(".item").data("choice-index"))
let featureId = $(ev.currentTarget).parents(".item").data("feature-id") let featureId = $(ev.currentTarget).parents(".item").data("feature-id")
let itemData = this.object.system.levels[levelIndex].choices[choiceIndex].features[featureId] let itemData = this.object.system.levels[levelIndex].choices[choiceIndex].features[featureId]
if (itemData.name != 'None') { if (itemData.name != 'None') {

View File

@@ -1,7 +1,7 @@
import { MaleficesUtility } from "./malefices-utility.js"; import { MaleficesUtility } from "./malefices-utility.js";
export const defaultItemImg = { export const defaultItemImg = {
arme: "systems/fvtt-malefices/images/icons/arme.webp", arme: "systems/fvtt-malefices/images/icons/epee.webp",
equipement: "systems/fvtt-malefices/images/icons/equipement.webp", equipement: "systems/fvtt-malefices/images/icons/equipement.webp",
elementbio: "systems/fvtt-malefices/images/icons/wisdom.webp", elementbio: "systems/fvtt-malefices/images/icons/wisdom.webp",
archetype: "systems/fvtt-malefices/images/icons/archetype.webp", archetype: "systems/fvtt-malefices/images/icons/archetype.webp",

View File

@@ -9,15 +9,19 @@
/* -------------------------------------------- */ /* -------------------------------------------- */
// Import Modules // Import Modules
import { MaleficesActor } from "./malefices-actor.js"; import { MaleficesActor } from "./malefices-actor.js";
import { MaleficesItemSheet } from "./malefices-item-sheet.js";
import { MaleficesActorSheet } from "./malefices-actor-sheet.js";
import { MaleficesNPCSheet } from "./malefices-npc-sheet.js";
import { MaleficesUtility } from "./malefices-utility.js"; import { MaleficesUtility } from "./malefices-utility.js";
import { MaleficesCombat } from "./malefices-combat.js"; import { MaleficesCombat } from "./malefices-combat.js";
import { MaleficesItem } from "./malefices-item.js"; import { MaleficesItem } from "./malefices-item.js";
import { MaleficesHotbar } from "./malefices-hotbar.js" import { MaleficesHotbar } from "./malefices-hotbar.js"
import { MaleficesCharacterSummary } from "./malefices-summary-app.js" import { MaleficesCharacterSummary } from "./malefices-summary-app.js"
import { MALEFICES_CONFIG } from "./malefices-config.js" import { MALEFICES_CONFIG } from "./malefices-config.js"
import { ClassCounter} from "https://www.uberwald.me/fvtt_appcount/count-class-ready.js"
// Import DataModels
import * as models from "./models/index.mjs"
// Import AppV2 Sheets
import * as sheets from "./applications/sheets/_module.mjs"
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Foundry VTT Initialization */ /* Foundry VTT Initialization */
@@ -38,7 +42,7 @@ Hooks.once("init", async function () {
MaleficesUtility.preloadHandlebarsTemplates(); MaleficesUtility.preloadHandlebarsTemplates();
/* -------------------------------------------- */ /* -------------------------------------------- */
// Set an initiative formula for the system // Set an initiative formula for the system
CONFIG.Combat.initiative = { CONFIG.Combat.initiative = {
formula: "1d6", formula: "1d6",
decimals: 1 decimals: 1
@@ -53,58 +57,51 @@ Hooks.once("init", async function () {
// Define custom Entity classes // Define custom Entity classes
CONFIG.Combat.documentClass = MaleficesCombat CONFIG.Combat.documentClass = MaleficesCombat
CONFIG.Actor.documentClass = MaleficesActor CONFIG.Actor.documentClass = MaleficesActor
CONFIG.Actor.dataModels = {
personnage: models.PersonnageDataModel,
pnj: models.PnjDataModel
}
CONFIG.Item.documentClass = MaleficesItem CONFIG.Item.documentClass = MaleficesItem
CONFIG.Item.dataModels = {
arme: models.ArmeDataModel,
equipement: models.EquipementDataModel,
archetype: models.ArchetypeDataModel,
tarot: models.TarotDataModel,
sortilege: models.SortilegeDataModel,
elementbio: models.ElementbioDataModel
}
/* -------------------------------------------- */ /* -------------------------------------------- */
// Register sheet application classes // Register AppV2 Actor Sheets
Actors.unregisterSheet("core", ActorSheet); foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
Actors.registerSheet("fvtt-malefices", MaleficesActorSheet, { types: ["personnage"], makeDefault: true }); foundry.documents.collections.Actors.registerSheet("fvtt-malefices", sheets.MaleficesPersonnageSheet, { types: ["personnage"], makeDefault: true });
Actors.registerSheet("fvtt-malefices", MaleficesNPCSheet, { types: ["pnj"], makeDefault: false }); foundry.documents.collections.Actors.registerSheet("fvtt-malefices", sheets.MaleficesNPCActorSheet, { types: ["pnj"], makeDefault: true });
Items.unregisterSheet("core", ItemSheet); // Register AppV2 Item Sheets
Items.registerSheet("fvtt-malefices", MaleficesItemSheet, { makeDefault: true }); foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
foundry.documents.collections.Items.registerSheet("fvtt-malefices", sheets.MaleficesArmeSheet, { types: ["arme"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-malefices", sheets.MaleficesEquipementSheet, { types: ["equipement"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-malefices", sheets.MaleficesArchetypeSheet, { types: ["archetype"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-malefices", sheets.MaleficesTarotSheet, { types: ["tarot"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-malefices", sheets.MaleficesSortilegeSheet, { types: ["sortilege"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-malefices", sheets.MaleficesElementbioSheet, { types: ["elementbio"], makeDefault: true });
MaleficesUtility.init() MaleficesUtility.init()
}); });
/* -------------------------------------------- */ /* -------------------------------------------- */
function welcomeMessage() { async function welcomeMessage() {
if (game.user.isGM) { if (game.user.isGM) {
const content = await foundry.applications.handlebars.renderTemplate(
'systems/fvtt-malefices/templates/chat/welcome-message.hbs', {}
)
ChatMessage.create({ ChatMessage.create({
user: game.user.id, user: game.user.id,
whisper: [game.user.id], whisper: [game.user.id],
content: `<div id="welcome-message-malefices"><span class="rdd-roll-part"> content
<strong>Bienvenu dans Malefices, le JDR qui sent le souffre !</strong>
<p>Le Livre de Base de Maléfices v4 est nécessaire pour jouer : https://arkhane-asylum.fr/en/malefices/</p>
<p>Maléfices et un jeu de rôle publié par Arkhane Asylum Publishing, tout les droits leur appartiennent.</p>
<p>Système développé par LeRatierBretonnien avec l'aide de la Dame du Lac et Malik, support sur le <a href="https://discord.gg/pPSDNJk">Discord FR de Foundry</a>.</p>
<p>Commandes : /tirage pour le tirage des tarots, /carte pour tirer une simple carte et /resume pour le résumé des PJs (MJ seulement)` });
}
}
/* -------------------------------------------- */
// Register world usage statistics
function registerUsageCount(registerKey) {
if (game.user.isGM) {
game.settings.register(registerKey, "world-key", {
name: "Unique world key",
scope: "world",
config: false,
default: "",
type: String
}); });
let worldKey = game.settings.get(registerKey, "world-key")
if (worldKey == undefined || worldKey == "") {
worldKey = randomID(32)
game.settings.set(registerKey, "world-key", worldKey)
}
// Simple API counter
let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.version}"`
//$.ajaxSetup({
//headers: { 'Access-Control-Allow-Origin': '*' }
//})
$.ajax(regURL)
} }
} }
@@ -122,7 +119,7 @@ Hooks.once("ready", function () {
}); });
} }
registerUsageCount('fvtt-malefices') ClassCounter.registerUsageCount();
welcomeMessage(); welcomeMessage();
MaleficesUtility.ready() MaleficesUtility.ready()
MaleficesCharacterSummary.ready() MaleficesCharacterSummary.ready()
@@ -143,4 +140,3 @@ Hooks.on("chatMessage", (html, content, msg) => {
} }
return true; return true;
}); });

View File

@@ -6,12 +6,12 @@
import { MaleficesUtility } from "./malefices-utility.js"; import { MaleficesUtility } from "./malefices-utility.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class MaleficesNPCSheet extends ActorSheet { export class MaleficesNPCSheet extends foundry.appv1.sheets.ActorSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
return mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["Malefices", "sheet", "actor"], classes: ["Malefices", "sheet", "actor"],
template: "systems/fvtt-malefices/templates/npc-sheet.html", template: "systems/fvtt-malefices/templates/npc-sheet.html",
width: 640, width: 640,
@@ -25,7 +25,7 @@ export class MaleficesNPCSheet extends ActorSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
async getData() { async getData() {
const objectData = this.object.system const objectData = this.object.system
let actorData = duplicate(objectData) let actorData = foundry.utils.duplicate(objectData)
let formData = { let formData = {
title: this.title, title: this.title,
@@ -38,16 +38,16 @@ export class MaleficesNPCSheet extends ActorSheet {
data: actorData, data: actorData,
limited: this.object.limited, limited: this.object.limited,
skills: this.actor.getSkills( ), skills: this.actor.getSkills( ),
weapons: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getWeapons()) ), weapons: this.actor.checkAndPrepareEquipments( foundry.utils.duplicate(this.actor.getWeapons()) ),
armors: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getArmors())), armors: this.actor.checkAndPrepareEquipments( foundry.utils.duplicate(this.actor.getArmors())),
shields: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getShields())), shields: this.actor.checkAndPrepareEquipments( foundry.utils.duplicate(this.actor.getShields())),
spells: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getLore())), spells: this.actor.checkAndPrepareEquipments( foundry.utils.duplicate(this.actor.getLore())),
equipments: this.actor.checkAndPrepareEquipments(duplicate(this.actor.getEquipmentsOnly()) ), equipments: this.actor.checkAndPrepareEquipments(foundry.utils.duplicate(this.actor.getEquipmentsOnly()) ),
equippedWeapons: this.actor.checkAndPrepareEquipments(duplicate(this.actor.getEquippedWeapons()) ), equippedWeapons: this.actor.checkAndPrepareEquipments(foundry.utils.duplicate(this.actor.getEquippedWeapons()) ),
equippedArmor: this.actor.getEquippedArmor(), equippedArmor: this.actor.getEquippedArmor(),
equippedShield: this.actor.getEquippedShield(), equippedShield: this.actor.getEquippedShield(),
subActors: duplicate(this.actor.getSubActors()), subActors: foundry.utils.duplicate(this.actor.getSubActors()),
moneys: duplicate(this.actor.getMoneys()), moneys: foundry.utils.duplicate(this.actor.getMoneys()),
encCapacity: this.actor.getEncumbranceCapacity(), encCapacity: this.actor.getEncumbranceCapacity(),
saveRolls: this.actor.getSaveRoll(), saveRolls: this.actor.getSaveRoll(),
conditions: this.actor.getConditions(), conditions: this.actor.getConditions(),
@@ -71,10 +71,10 @@ export class MaleficesNPCSheet extends ActorSheet {
// Everything below here is only needed if the sheet is editable // Everything below here is only needed if the sheet is editable
if (!this.options.editable) return; if (!this.options.editable) return;
html.bind("keydown", function(e) { // Ignore Enter in actores sheet html.bind("keydown", function(e) { // Ignore Enter in actores sheet
if (e.keyCode === 13) return false; if (e.keyCode === 13) return false;
}); });
// Update Inventory Item // Update Inventory Item
html.find('.item-edit').click(ev => { html.find('.item-edit').click(ev => {
@@ -92,17 +92,17 @@ export class MaleficesNPCSheet extends ActorSheet {
let dataType = $(ev.currentTarget).data("type") let dataType = $(ev.currentTarget).data("type")
this.actor.createEmbeddedDocuments('Item', [{ name: "NewItem", type: dataType }], { renderSheet: true }) this.actor.createEmbeddedDocuments('Item', [{ name: "NewItem", type: dataType }], { renderSheet: true })
}) })
html.find('.equip-activate').click(ev => { html.find('.equip-activate').click(ev => {
const li = $(ev.currentTarget).parents(".item") const li = $(ev.currentTarget).parents(".item")
let itemId = li.data("item-id") let itemId = li.data("item-id")
this.actor.equipActivate( itemId) this.actor.equipActivate( itemId)
}); });
html.find('.equip-deactivate').click(ev => { html.find('.equip-deactivate').click(ev => {
const li = $(ev.currentTarget).parents(".item") const li = $(ev.currentTarget).parents(".item")
let itemId = li.data("item-id") let itemId = li.data("item-id")
this.actor.equipDeactivate( itemId) this.actor.equipDeactivate( itemId)
}); });
html.find('.subactor-edit').click(ev => { html.find('.subactor-edit').click(ev => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
@@ -110,7 +110,7 @@ export class MaleficesNPCSheet extends ActorSheet {
let actor = game.actors.get( actorId ); let actor = game.actors.get( actorId );
actor.sheet.render(true); actor.sheet.render(true);
}); });
html.find('.subactor-delete').click(ev => { html.find('.subactor-delete').click(ev => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
let actorId = li.data("actor-id"); let actorId = li.data("actor-id");
@@ -133,7 +133,7 @@ export class MaleficesNPCSheet extends ActorSheet {
const li = $(event.currentTarget).parents(".item") const li = $(event.currentTarget).parents(".item")
this.actor.incDecAmmo( li.data("item-id"), +1 ) this.actor.incDecAmmo( li.data("item-id"), +1 )
} ); } );
html.find('.roll-ability').click((event) => { html.find('.roll-ability').click((event) => {
const abilityKey = $(event.currentTarget).data("ability-key"); const abilityKey = $(event.currentTarget).data("ability-key");
this.actor.rollAbility(abilityKey); this.actor.rollAbility(abilityKey);
@@ -142,7 +142,7 @@ export class MaleficesNPCSheet extends ActorSheet {
const li = $(event.currentTarget).parents(".item") const li = $(event.currentTarget).parents(".item")
const skillId = li.data("item-id") const skillId = li.data("item-id")
this.actor.rollSkill(skillId) this.actor.rollSkill(skillId)
}); });
html.find('.roll-weapon').click((event) => { html.find('.roll-weapon').click((event) => {
const li = $(event.currentTarget).parents(".item"); const li = $(event.currentTarget).parents(".item");
@@ -163,28 +163,28 @@ export class MaleficesNPCSheet extends ActorSheet {
const saveKey = $(event.currentTarget).data("save-key") const saveKey = $(event.currentTarget).data("save-key")
this.actor.rollSave(saveKey) this.actor.rollSave(saveKey)
}); });
html.find('.lock-unlock-sheet').click((event) => { html.find('.lock-unlock-sheet').click((event) => {
this.options.editScore = !this.options.editScore; this.options.editScore = !this.options.editScore;
this.render(true); this.render(true);
}); });
html.find('.item-link a').click((event) => { html.find('.item-link a').click((event) => {
const itemId = $(event.currentTarget).data("item-id"); const itemId = $(event.currentTarget).data("item-id");
const item = this.actor.getOwnedItem(itemId); const item = this.actor.getOwnedItem(itemId);
item.sheet.render(true); item.sheet.render(true);
}); });
html.find('.item-equip').click(ev => { html.find('.item-equip').click(ev => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
this.actor.equipItem( li.data("item-id") ); this.actor.equipItem( li.data("item-id") );
this.render(true); this.render(true);
}); });
html.find('.update-field').change(ev => { html.find('.update-field').change(ev => {
const fieldName = $(ev.currentTarget).data("field-name"); const fieldName = $(ev.currentTarget).data("field-name");
let value = Number(ev.currentTarget.value); let value = Number(ev.currentTarget.value);
this.actor.update( { [`${fieldName}`]: value } ); this.actor.update( { [`${fieldName}`]: value } );
}); });
} }

View File

@@ -1,92 +1,57 @@
import { MaleficesUtility } from "./malefices-utility.js"; import { MaleficesUtility } from "./malefices-utility.js";
export class MaleficesRollDialog extends Dialog { export class MaleficesRollDialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async create(actor, rollData) { static async create(actor, rollData) {
const isCard = rollData?.attr?.iscard
const template = isCard
? 'systems/fvtt-malefices/templates/dialogs/confrontation-dialog.hbs'
: 'systems/fvtt-malefices/templates/dialogs/roll-dialog-generic.hbs'
let options = { classes: ["MaleficesDialog"], width: 540, height: 'fit-content', 'z-index': 99999 } const content = await foundry.applications.handlebars.renderTemplate(template, rollData)
let html
if (rollData.attr && rollData.attr.iscard) {
html = await renderTemplate('systems/fvtt-malefices/templates/dialogs/confrontation-dialog.hbs', rollData);
} else {
html = await renderTemplate('systems/fvtt-malefices/templates/dialogs/roll-dialog-generic.hbs', rollData);
}
return new MaleficesRollDialog(actor, rollData, html, options); return foundry.applications.api.DialogV2.wait({
} window: {
title: isCard ? "Tirage" : "Jet de dé",
/* -------------------------------------------- */ icon: isCard ? "fa-solid fa-layer-group" : "fa-solid fa-dice-d20",
constructor(actor, rollData, html, options, close = undefined) {
let isCard = rollData.attr && rollData.attr.iscard
let conf = {
title: (isCard) ? "Jet" : "Tirage",
content: html,
buttons: {
roll: {
icon: '<i class="fas fa-check"></i>',
label: (isCard) ? "Tirer une carte" : "Lancer le dé",
callback: () => { this.roll() }
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Annuler",
callback: () => { this.close() }
}
}, },
close: close classes: ["malefices-roll-dialog"],
} position: { width: 540 },
modal: false,
super(conf, options); rejectClose: false,
content,
this.actor = actor; buttons: [
this.rollData = rollData; {
action: "roll",
label: isCard ? "Tirer une carte" : "Lancer le dé",
icon: isCard ? "fa-solid fa-layer-group" : "fa-solid fa-check",
default: true,
callback: (event, button, dialog) => {
MaleficesRollDialog._updateRollDataFromForm(rollData, button.form.elements)
if (isCard) {
MaleficesUtility.tirageConfrontationMalefices(rollData)
} else {
MaleficesUtility.rollMalefices(rollData)
}
}
},
{
action: "cancel",
label: "Annuler",
icon: "fa-solid fa-times",
}
],
})
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
roll() { static _updateRollDataFromForm(rollData, elements) {
let isCard = this.rollData.attr && this.rollData.attr.iscard if (elements.bonusMalusPerso) rollData.bonusMalusPerso = Number(elements.bonusMalusPerso.value)
if (isCard) { if (elements.bonusMalusSituation) rollData.bonusMalusSituation = Number(elements.bonusMalusSituation.value)
MaleficesUtility.tirageConfrontationMalefices(this.rollData) if (elements.bonusMalusDef) rollData.bonusMalusDef = Number(elements.bonusMalusDef.value)
} else { if (elements.bonusMalusPortee) rollData.bonusMalusPortee = Number(elements.bonusMalusPortee.value)
MaleficesUtility.rollMalefices(this.rollData) if (elements.confrontationDegre) rollData.confrontationDegre = Number(elements.confrontationDegre.value)
} if (elements.confrontationModif) rollData.confrontationModif = Number(elements.confrontationModif.value)
}
/* -------------------------------------------- */
async refreshDialog() {
const content = await renderTemplate("systems/fvtt-malefices/templates/dialogs/roll-dialog-generic.hbs", this.rollData)
this.data.content = content
this.render(true)
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
var dialog = this;
function onLoad() {
}
$(function () { onLoad(); });
html.find('#bonusMalusSituation').change((event) => {
this.rollData.bonusMalusSituation = Number(event.currentTarget.value)
})
html.find('#bonusMalusPerso').change((event) => {
this.rollData.bonusMalusPerso = Number(event.currentTarget.value)
})
html.find('#bonusMalusDef').change((event) => {
this.rollData.bonusMalusDef = Number(event.currentTarget.value)
})
html.find('#bonusMalusPortee').change((event) => {
this.rollData.bonusMalusPortee = Number(event.currentTarget.value)
})
html.find('#confrontationDegre').change((event) => {
this.rollData.confrontationDegre = Number(event.currentTarget.value)
})
html.find('#confrontationModif').change((event) => {
this.rollData.confrontationModif = Number(event.currentTarget.value)
})
} }
} }

View File

@@ -1,134 +1,133 @@
/* -------------------------------------------- */ /* -------------------------------------------- */
import { MaleficesUtility } from "./malefices-utility.js"; import { MaleficesUtility } from "./malefices-utility.js";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
/* -------------------------------------------- */ /* -------------------------------------------- */
export class MaleficesCharacterSummary extends Application { export class MaleficesCharacterSummary extends HandlebarsApplicationMixin(ApplicationV2) {
/* -------------------------------------------- */
static DEFAULT_OPTIONS = {
id: "malefices-character-summary",
classes: ["MaleficesDialog"],
window: { title: "Résumé des Personnages", resizable: true },
position: { width: 960, height: "auto" },
dragDrop: [{ dragSelector: null, dropSelector: ".character-summary-container" }],
}
static PARTS = {
form: { template: "systems/fvtt-malefices/templates/dialogs/character-summary.hbs" }
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static displayPCSummary() { static displayPCSummary() {
if (game.user.isGM) { if (game.user.isGM) {
game.system.malefices.charSummary.render(true) game.system.malefices.charSummary.render({ force: true })
} else { } else {
ui.notifications.info("Commande /tirage réservée au MJ !") ui.notifications.info("Commande /resume réservée au MJ !")
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
updatePCSummary() { updatePCSummary() {
if (this.rendered) { if (this.element?.isConnected) {
this.render(true) this.render({ force: true })
} }
} }
/* -------------------------------------------- */
static createSummaryPos() {
return { top: 200, left: 200 };
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static ready() { static ready() {
if (!game.user.isGM) { // Uniquement si GM if (!game.user.isGM) {
return return
} }
let charSummary = new MaleficesCharacterSummary() game.system.malefices.charSummary = new MaleficesCharacterSummary()
game.system.malefices.charSummary = charSummary
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
constructor() { constructor(options = {}) {
super(); super(options)
//game.settings.set("world", "character-summary-data", {npcList: [], x:0, y:0}) const saved = game.settings.get("world", "character-summary-data")
this.settings = game.settings.get("world", "character-summary-data") this.extraList = saved.extraList ?? saved.npcList ?? []
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static get defaultOptions() { async _prepareContext(_options) {
return mergeObject(super.defaultOptions, { const pcs = game.actors.filter(ac => ac.type === "personnage" && ac.hasPlayerOwner)
template: "systems/fvtt-malefices/templates/dialogs/character-summary.hbs", const extras = []
popOut: true, const validList = []
resizable: true, for (const actorId of this.extraList) {
dragDrop: [{ dragSelector: ".items-list .item", dropSelector: null }], const actor = game.actors.get(actorId)
classes: ["bol", "dialog"], width: 920, height: 'fit-content' if (actor) { extras.push(actor); validList.push(actorId) }
}
if (validList.length !== this.extraList.length) {
this.extraList = validList
this._persist()
}
return { pcs, extras, config: game.system.malefices.config }
}
/* -------------------------------------------- */
_onRender(_context, _options) {
const el = this.element
el.querySelectorAll('.actor-open').forEach(a => {
a.addEventListener('click', event => {
const li = event.currentTarget.closest('.item')
const actor = game.actors.get(li.dataset.actorId)
actor?.sheet.render(true)
})
}) })
el.querySelectorAll('.summary-roll').forEach(a => {
a.addEventListener('click', event => {
const li = event.currentTarget.closest('.item')
const actor = game.actors.get(li.dataset.actorId)
const key = event.currentTarget.dataset.key
actor?.rollAttribut(key)
})
})
el.querySelectorAll('.actor-delete').forEach(a => {
a.addEventListener('click', event => {
const li = event.currentTarget.closest('.item')
this.extraList = this.extraList.filter(id => id !== li.dataset.actorId)
this._persist()
this.render({ force: true })
})
})
const dropZone = el.querySelector('.character-summary-container')
if (dropZone) {
dropZone.addEventListener('dragover', ev => ev.preventDefault())
dropZone.addEventListener('drop', ev => { ev.stopPropagation(); this._onDrop(ev) })
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getData() { _canDragDrop(_selector) { return true }
let formData = super.getData();
formData.pcs = game.actors.filter(ac => ac.type == "personnage" && ac.hasPlayerOwner) /* -------------------------------------------- */
formData.npcs = [] _persist() {
let newList = [] const saved = game.settings.get("world", "character-summary-data")
let toUpdate = false game.settings.set("world", "character-summary-data", { ...saved, extraList: this.extraList })
for (let actorId of this.settings.npcList) {
let actor = game.actors.get(actorId)
if (actor) {
formData.npcs.push(actor)
newList.push(actorId)
} else {
toUpdate = true
}
}
formData.config = game.system.malefices.config
if (toUpdate) {
this.settings.npcList = newList
//console.log("Going to update ...", this.settings)
game.settings.set("world", "character-summary-data", this.settings)
}
return formData
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
updateNPC() { _saveAndRefresh() {
game.settings.set("world", "character-summary-data", game.system.malefices.charSummary.settings) this.render({ force: true })
game.system.malefices.charSummary.close()
setTimeout(function () { game.system.malefices.charSummary.render(true) }, 500)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async _onDrop(event) { async _onDrop(event) {
//console.log("Dragged data are : ", dragData) try {
let data = event.dataTransfer.getData('text/plain') const dataItem = JSON.parse(event.dataTransfer.getData('text/plain'))
let dataItem = JSON.parse(data) const actor = fromUuidSync(dataItem.uuid)
let actor = fromUuidSync(dataItem.uuid) if (actor && !this.extraList.includes(actor.id)) {
if (actor) { this.extraList.push(actor.id)
game.system.malefices.charSummary.settings.npcList.push(actor.id) this._persist()
game.system.malefices.charSummary.updateNPC() this.render({ force: true })
}
} else { } catch(e) { /* not a valid drag payload */ }
ui.notifications.warn("Pas d'acteur trouvé")
}
}
/* -------------------------------------------- */
/** @override */
async activateListeners(html) {
super.activateListeners(html);
html.find('.actor-open').click((event) => {
const li = $(event.currentTarget).parents(".item")
const actor = game.actors.get(li.data("actor-id"))
actor.sheet.render(true)
})
html.find('.summary-roll').click((event) => {
const li = $(event.currentTarget).parents(".item")
const actor = game.actors.get(li.data("actor-id"))
let type = $(event.currentTarget).data("type")
let key = $(event.currentTarget).data("key")
actor.rollAttribut(key)
})
html.find('.actor-delete').click(event => {
const li = $(event.currentTarget).parents(".item");
let actorId = li.data("actor-id")
let newList = game.system.malefices.charSummary.settings.npcList.filter(id => id != actorId)
game.system.malefices.charSummary.settings.npcList = newList
game.system.malefices.charSummary.updateNPC()
})
} }
} }

View File

@@ -1,155 +1,132 @@
import { MaleficesUtility } from "./malefices-utility.js"; import { MaleficesUtility } from "./malefices-utility.js";
export class MaleficesTirageTarotDialog extends Dialog { const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
export class MaleficesTirageTarotDialog extends HandlebarsApplicationMixin(ApplicationV2) {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async create(actor, tirageData) { static DEFAULT_OPTIONS = {
id: "malefices-tirage-tarot",
classes: ["MaleficesDialog"],
window: { title: "Tirage des Tarots", resizable: true },
position: { width: 720, height: 740 },
}
let options = { classes: ["MaleficesDialog"], width: 720, height: 740, 'z-index': 99999 }; static PARTS = {
let html = await renderTemplate('systems/fvtt-malefices/templates/dialogs/tirage-tarot-dialog.hbs', tirageData); form: { template: 'systems/fvtt-malefices/templates/dialogs/tirage-tarot-dialog.hbs' }
return new MaleficesTirageTarotDialog(actor, tirageData, html, options);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
constructor(actor, tirageData, html, options, close = undefined) { constructor(actor, tirageData, options = {}) {
let conf = { super(options)
title: "Tirage des tarots", this.actor = actor
content: html, this.tirageData = tirageData
buttons: { }
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Fermer/Annuler",
callback: () => { this.close() }
}
},
close: close
}
super(conf, options); /* -------------------------------------------- */
static async create(actor, tirageData) {
const app = new MaleficesTirageTarotDialog(actor, tirageData)
app.render({ force: true })
return app
}
this.actor = actor; /* -------------------------------------------- */
this.tirageData = tirageData; async _prepareContext(_options) {
return { ...this.tirageData }
}
/* -------------------------------------------- */
_onRender(_context, _options) {
const el = this.element
el.querySelector('#playerId')?.addEventListener('change', (event) => {
if (event.currentTarget.value !== "none") {
this.tirageData.playerId = event.currentTarget.value
this.processSelectedPlayer()
}
})
el.querySelector('#actorId')?.addEventListener('change', (event) => {
if (event.currentTarget.value !== "none") {
this.attributeToActor(event.currentTarget.value)
}
})
el.querySelector('.tirage-close-btn')?.addEventListener('click', () => this.close())
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async sendCardRequest() { async sendCardRequest() {
this.tirageData.state = 'waiting-user-card' this.tirageData.state = 'waiting-user-card'
let msg = await MaleficesUtility.createChatMessage(this.tirageData.user.name, "useronly", { await MaleficesUtility.createChatMessage(this.tirageData.user.name, "useronly", {
content: await renderTemplate(`systems/fvtt-malefices/templates/chat/request-tarot-card.hbs`, this.tirageData) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-malefices/templates/chat/request-tarot-card.hbs`, this.tirageData)
}) })
//msg.setFlag("world", "tirage-data", this.tirageData)
console.log("MSG IS", msg)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
drawCard() { drawCard() {
let index = Math.round(Math.random() * (this.tirageData.deck.length-1)) let index = Math.round(Math.random() * (this.tirageData.deck.length - 1))
let selectedCard = this.tirageData.deck[index] let selectedCard = this.tirageData.deck[index]
selectedCard.system.ispositif = true selectedCard.system.ispositif = true
if ( selectedCard.system.isdualside) { // Cas des cartes pouvant avoir 2 sens if (selectedCard.system.isdualside) {
selectedCard.system.ispositif = (Math.random() > 0.5) selectedCard.system.ispositif = (Math.random() > 0.5)
} }
console.log("CARD SELECTED:", selectedCard)
// Cas spécial de la Roue de la Fortune // Cas spécial de la Roue de la Fortune
if ( selectedCard.name.toLowerCase().includes("fortune")) { if (selectedCard.name.toLowerCase().includes("fortune")) {
this.tirageData.maxPlayerCard += 1 this.tirageData.maxPlayerCard += 1
this.tirageData.maxSecretCard += 1 this.tirageData.maxSecretCard += 1
} }
let newList = [] this.tirageData.deck = this.tirageData.deck.filter(c => c.name !== selectedCard.name)
for(let card of this.tirageData.deck) {
if (card.name != selectedCard.name) {
newList.push(card)
}
}
this.tirageData.deck = newList
return selectedCard return selectedCard
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async addCard( msgId ) { async addCard(msgId) {
MaleficesUtility.removeChatMessageId(msgId) MaleficesUtility.removeChatMessageId(msgId)
let selectedCard = this.drawCard() let selectedCard = this.drawCard()
selectedCard.system.isgm = false selectedCard.system.isgm = false
await MaleficesUtility.createChatMessage(this.tirageData.user.name, "gmroll", { await MaleficesUtility.createChatMessage(this.tirageData.user.name, "gmroll", {
content: await renderTemplate(`systems/fvtt-malefices/templates/chat/display-tarot-card.hbs`, selectedCard) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-malefices/templates/chat/display-tarot-card.hbs`, selectedCard)
}) })
if (this.tirageData.cards[0].name == "???") { if (this.tirageData.cards[0].name == "???") this.tirageData.cards.shift()
this.tirageData.cards.shift()
}
this.tirageData.cards.push(selectedCard) this.tirageData.cards.push(selectedCard)
this.tirageData.nbCard++ this.tirageData.nbCard++
if (this.tirageData.nbCard == this.tirageData.maxPlayerCard) { if (this.tirageData.nbCard == this.tirageData.maxPlayerCard) {
for (let i=0; i<this.tirageData.maxSecretCard; i++) { for (let i = 0; i < this.tirageData.maxSecretCard; i++) {
let selectedCard = this.drawCard() let secretCard = this.drawCard()
selectedCard.system.isgm = true secretCard.system.isgm = true
await MaleficesUtility.createChatMessage(this.tirageData.user.name, "blindroll", { await MaleficesUtility.createChatMessage(this.tirageData.user.name, "blindroll", {
content: await renderTemplate(`systems/fvtt-malefices/templates/chat/display-tarot-card.hbs`, selectedCard) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-malefices/templates/chat/display-tarot-card.hbs`, secretCard)
}) })
if (this.tirageData.secretCards[0].name == "???") { if (this.tirageData.secretCards[0].name == "???") this.tirageData.secretCards.shift()
this.tirageData.secretCards.shift() this.tirageData.secretCards.push(secretCard)
}
this.tirageData.secretCards.push(selectedCard)
} }
this.tirageData.actors = duplicate(game.actors) this.tirageData.actors = foundry.utils.duplicate(game.actors)
this.tirageData.state = 'attribute-to-actor' this.tirageData.state = 'attribute-to-actor'
}else { } else {
this.sendCardRequest() this.sendCardRequest()
} }
this.refreshDialog() this.render({ force: true })
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async processSelectedPlayer() { async processSelectedPlayer() {
let user = game.users.get(this.tirageData.playerId) this.tirageData.user = game.users.get(this.tirageData.playerId)
this.tirageData.user = user
this.tirageData.players = null this.tirageData.players = null
console.log("Going to work with ", user.name)
game.system.malefices.currentTirage = this game.system.malefices.currentTirage = this
this.refreshDialog() this.render({ force: true })
this.sendCardRequest() this.sendCardRequest()
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
attributeToActor(actorId) { attributeToActor(actorId) {
let actor = game.actors.get(actorId) const actor = game.actors.get(actorId)
if (actor) { if (actor) {
actor.createEmbeddedDocuments('Item', this.tirageData.cards) actor.createEmbeddedDocuments('Item', this.tirageData.cards)
actor.createEmbeddedDocuments('Item', this.tirageData.secretCards) actor.createEmbeddedDocuments('Item', this.tirageData.secretCards)
ui.notifications.info("Les cartes ont été attribuées à " + actor.name) ui.notifications.info("Les cartes ont été attribuées à " + actor.name)
} }
} }
/* -------------------------------------------- */
async refreshDialog() {
const content = await renderTemplate("systems/fvtt-malefices/templates/dialogs/tirage-tarot-dialog.hbs", this.tirageData)
this.data.content = content
this.render(true)
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
var dialog = this;
function onLoad() {
}
$(function () { onLoad(); });
html.find('#playerId').change((event) => {
if ( event.currentTarget.value != "none") {
dialog.tirageData.playerId = event.currentTarget.value
dialog.processSelectedPlayer()
}
})
html.find('#actorId').change((event) => {
if ( event.currentTarget.value != "none") {
let actorId = event.currentTarget.value
dialog.attributeToActor(actorId)
}
})
}
} }

View File

@@ -43,12 +43,20 @@ export class MaleficesUtility {
Handlebars.registerHelper('add', function (a, b) { Handlebars.registerHelper('add', function (a, b) {
return parseInt(a) + parseInt(b); return parseInt(a) + parseInt(b);
}) })
// Handle v12 removal of this helper
Handlebars.registerHelper('select', function (selected, options) {
const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected));
const rgx = new RegExp(' value=[\"\']' + escapedValue + '[\"\']');
const html = options.fn(this);
return html.replace(rgx, "$& selected");
});
game.settings.register("world", "character-summary-data", { game.settings.register("world", "character-summary-data", {
name: "character-summary-data", name: "character-summary-data",
scope: "world", scope: "world",
config: false, config: false,
default: { npcList: [], x: 200, y: 200 }, default: { extraList: [], x: 200, y: 200 },
type: Object type: Object
}) })
@@ -65,10 +73,10 @@ export class MaleficesUtility {
/*-------------------------------------------- */ /*-------------------------------------------- */
static getTarots() { static getTarots() {
return duplicate(this.tarots) return foundry.utils.duplicate(this.tarots)
} }
static getTarot(tId) { static getTarot(tId) {
return this.tarots.find(t => t._id == tId) return this.tarots.find(t => t._id == tId)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -99,14 +107,14 @@ export class MaleficesUtility {
if (game.user.isGM) { if (game.user.isGM) {
game.system.malefices.currentTirage.addCard(msgId) game.system.malefices.currentTirage.addCard(msgId)
} else { } else {
game.socket.emit( "system.fvtt-malefices", {name: "msg-draw-card", data: {msgId: msgId}}) game.socket.emit("system.fvtt-malefices", { name: "msg-draw-card", data: { msgId: msgId } })
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async chatListeners(html) { static async chatListeners(html) {
html.on("click", '.roll-destin', event => { $(html).on("click", '.roll-destin', event => {
let messageId = MaleficesUtility.findChatMessageId(event.currentTarget) let messageId = MaleficesUtility.findChatMessageId(event.currentTarget)
let message = game.messages.get(messageId) let message = game.messages.get(messageId)
let rollData = message.getFlag("world", "rolldata") let rollData = message.getFlag("world", "rolldata")
@@ -115,11 +123,11 @@ export class MaleficesUtility {
rollData.isReroll = true rollData.isReroll = true
this.rollMalefices(rollData) this.rollMalefices(rollData)
}) })
html.on("click", '.draw-tarot-card', event => { $(html).on("click", '.draw-tarot-card', event => {
let messageId = MaleficesUtility.findChatMessageId(event.currentTarget) let messageId = MaleficesUtility.findChatMessageId(event.currentTarget)
this.drawDeckCard(messageId) this.drawDeckCard(messageId)
}) })
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -127,10 +135,15 @@ export class MaleficesUtility {
const templatePaths = [ const templatePaths = [
'systems/fvtt-malefices/templates/actors/editor-notes-gm.hbs', 'systems/fvtt-malefices/templates/actors/editor-notes-gm.hbs',
'systems/fvtt-malefices/templates/items/partial-item-header.hbs',
'systems/fvtt-malefices/templates/items/partial-item-nav.hbs', 'systems/fvtt-malefices/templates/items/partial-item-nav.hbs',
'systems/fvtt-malefices/templates/items/partial-item-description.hbs' 'systems/fvtt-malefices/templates/items/partial-item-description.hbs',
'systems/fvtt-malefices/templates/post-item.hbs',
'systems/fvtt-malefices/templates/actors/npc-sheet.hbs',
'systems/fvtt-malefices/templates/chat/welcome-message.hbs',
'systems/fvtt-malefices/templates/dialogs/character-summary.hbs'
] ]
return loadTemplates(templatePaths); return foundry.applications.handlebars.loadTemplates(templatePaths);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -196,7 +209,7 @@ export class MaleficesUtility {
let id = rollData.rollId let id = rollData.rollId
let oldRollData = this.rollDataStore[id] || {} let oldRollData = this.rollDataStore[id] || {}
let newRollData = mergeObject(oldRollData, rollData) let newRollData = foundry.utils.mergeObject(oldRollData, rollData)
this.rollDataStore[id] = newRollData this.rollDataStore[id] = newRollData
} }
@@ -204,7 +217,7 @@ export class MaleficesUtility {
static async onSocketMesssage(msg) { static async onSocketMesssage(msg) {
console.log("SOCKET MESSAGE", msg.name) console.log("SOCKET MESSAGE", msg.name)
if (msg.name == "msg-draw-card") { if (msg.name == "msg-draw-card") {
if ( game.user.isGM && game.system.malefices.currentTirage) { if (game.user.isGM && game.system.malefices.currentTirage) {
game.system.malefices.currentTirage.addCard(msg.data.msgId) game.system.malefices.currentTirage.addCard(msg.data.msgId)
} }
} }
@@ -271,71 +284,79 @@ export class MaleficesUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static processSpecialCard(actor, rollData) { static processSpecialCard(actor, rollData) {
if (rollData.selectedCard.name.toLowerCase().includes("archange")) { if (rollData.selectedCard.name.toLowerCase().includes("archange")) {
let actorCard = actor.items.find( c => c.type =="tarot" && c.name.toLowerCase().includes("archange")) let actorCard = actor.items.find(c => c.type == "tarot" && c.name.toLowerCase().includes("archange"))
if (actorCard) { if (actorCard) {
MaleficesUtility.createChatMessage(actor.name, "gmroll", { MaleficesUtility.createChatMessage(actor.name, "gmroll", {
content: `Conséquence supplémentaire ! <br>L'Archange : ${actor.name} gagne 1 point de Spiritualité.` }) content: `Conséquence supplémentaire ! <br>L'Archange : ${actor.name} gagne 1 point de Spiritualité.`
})
actor.incDecAttr("spiritualite", 1) actor.incDecAttr("spiritualite", 1)
} }
} }
if (rollData.selectedCard.name.toLowerCase().includes("vicaire")) { if (rollData.selectedCard.name.toLowerCase().includes("vicaire")) {
let actorCard = actor.items.find( c => c.type =="tarot" && c.name.toLowerCase().includes("vicaire")) let actorCard = actor.items.find(c => c.type == "tarot" && c.name.toLowerCase().includes("vicaire"))
if (actorCard) { if (actorCard) {
MaleficesUtility.createChatMessage(actor.name, "blindroll", { MaleficesUtility.createChatMessage(actor.name, "blindroll", {
content: `Conséquence supplémentaire ! <br>Le Vicaire : ${actor.name} vient de gagner 1 point en Pratique de la Magie Blanche (MPMB, secret).` }) content: `Conséquence supplémentaire ! <br>Le Vicaire : ${actor.name} vient de gagner 1 point en Pratique de la Magie Blanche (MPMB, secret).`
})
actor.incDecMPMB(1) actor.incDecMPMB(1)
} }
} }
if (rollData.selectedCard.name.toLowerCase().includes("chance")) { if (rollData.selectedCard.name.toLowerCase().includes("chance")) {
let actorCard = actor.items.find( c => c.type =="tarot" && c.name.toLowerCase().includes("chance")) let actorCard = actor.items.find(c => c.type == "tarot" && c.name.toLowerCase().includes("chance"))
if (actorCard) { if (actorCard) {
MaleficesUtility.createChatMessage(actor.name, "gmroll", { MaleficesUtility.createChatMessage(actor.name, "gmroll", {
content: `Conséquence supplémentaire ! <br>La Chance : ${actor.name} a gagné 1 point de Destin.` }) content: `Conséquence supplémentaire ! <br>La Chance : ${actor.name} a gagné 1 point de Destin.`
})
actor.incDecDestin(1) actor.incDecDestin(1)
} }
} }
if (rollData.selectedCard.name.toLowerCase().includes("mort")) { if (rollData.selectedCard.name.toLowerCase().includes("mort")) {
let actorCard = actor.items.find( c => c.type =="tarot" && c.name.toLowerCase().includes("mort")) let actorCard = actor.items.find(c => c.type == "tarot" && c.name.toLowerCase().includes("mort"))
if (actorCard) { if (actorCard) {
MaleficesUtility.createChatMessage(actor.name, "gmroll", { MaleficesUtility.createChatMessage(actor.name, "gmroll", {
content: `Conséquence supplémentaire ! <br>La Mort : ${actor.name} est pétrifié par la peur.` }) content: `Conséquence supplémentaire ! <br>La Mort : ${actor.name} est pétrifié par la peur.`
})
actor.incDecDestin(1) actor.incDecDestin(1)
} }
} }
if (rollData.selectedCard.name.toLowerCase().includes("diable")) { if (rollData.selectedCard.name.toLowerCase().includes("diable")) {
let actorCard = actor.items.find( c => c.type =="tarot" && c.name.toLowerCase().includes("diable")) let actorCard = actor.items.find(c => c.type == "tarot" && c.name.toLowerCase().includes("diable"))
if (actorCard) { if (actorCard) {
MaleficesUtility.createChatMessage(actor.name, "gmroll", { MaleficesUtility.createChatMessage(actor.name, "gmroll", {
content: `Conséquence supplémentaire ! <br>Le Diable : ${actor.name} gagne 1 point de Rationnalité.` }) content: `Conséquence supplémentaire ! <br>Le Diable : ${actor.name} gagne 1 point de Rationnalité.`
})
actor.incDecAttr("rationnalite", 1) actor.incDecAttr("rationnalite", 1)
} }
} }
if (rollData.selectedCard.name.toLowerCase().includes("lune noire")) { if (rollData.selectedCard.name.toLowerCase().includes("lune noire")) {
let actorCard = actor.items.find( c => c.type =="tarot" && c.name.toLowerCase().includes("lune noire")) let actorCard = actor.items.find(c => c.type == "tarot" && c.name.toLowerCase().includes("lune noire"))
if (actorCard) { if (actorCard) {
MaleficesUtility.createChatMessage(actor.name, "blindroll", { MaleficesUtility.createChatMessage(actor.name, "blindroll", {
content: `Conséquence supplémentaire ! <br>La Lune Noire : ${actor.name} vient de gagner 1 point de Fluide (secret).` }) content: `Conséquence supplémentaire ! <br>La Lune Noire : ${actor.name} vient de gagner 1 point de Fluide (secret).`
})
actor.incDecFluide(1) actor.incDecFluide(1)
} }
} }
if (rollData.selectedCard.name.toLowerCase().includes("grand livre")) { if (rollData.selectedCard.name.toLowerCase().includes("grand livre")) {
let actorCard = actor.items.find( c => c.type =="tarot" && c.name.toLowerCase().includes("grand livre")) let actorCard = actor.items.find(c => c.type == "tarot" && c.name.toLowerCase().includes("grand livre"))
if (actorCard) { if (actorCard) {
MaleficesUtility.createChatMessage(actor.name, "blindroll", { MaleficesUtility.createChatMessage(actor.name, "blindroll", {
content: `Conséquence supplémentaire ! <br>La Lune Noire : ${actor.name} vient de gagner 1 point de Fluide (secret).` }) content: `Conséquence supplémentaire ! <br>La Lune Noire : ${actor.name} vient de gagner 1 point de Fluide (secret).`
})
actor.incDecFluide(1) actor.incDecFluide(1)
} }
} }
if (rollData.selectedCard.name.toLowerCase().includes("sorcier")) { if (rollData.selectedCard.name.toLowerCase().includes("sorcier")) {
let actorCard = actor.items.find( c => c.type =="tarot" && c.name.toLowerCase().includes("sorcier")) let actorCard = actor.items.find(c => c.type == "tarot" && c.name.toLowerCase().includes("sorcier"))
if (actorCard) { if (actorCard) {
MaleficesUtility.createChatMessage(actor.name, "blindroll", { MaleficesUtility.createChatMessage(actor.name, "blindroll", {
content: `Conséquence supplémentaire ! <br>Le Vicaire : ${actor.name} vient de gagner 1 point en Pratique de la Magie Noire (MPMN, secret).` }) content: `Conséquence supplémentaire ! <br>Le Vicaire : ${actor.name} vient de gagner 1 point en Pratique de la Magie Noire (MPMN, secret).`
})
actor.incDecMPMN(1) actor.incDecMPMN(1)
} }
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static computeResults(rollData) { static computeResults(rollData) {
@@ -362,30 +383,30 @@ export class MaleficesUtility {
rollData.target = rollData.attr.value - rollData.confrontationDegre + rollData.confrontationModif rollData.target = rollData.attr.value - rollData.confrontationDegre + rollData.confrontationModif
let deck = this.getTarots() let deck = this.getTarots()
let index = Math.round(Math.random() * (deck.length-1)) let index = Math.round(Math.random() * (deck.length - 1))
let selectedCard = deck[index] let selectedCard = deck[index]
selectedCard.system.ispositif = (Math.random() > 0.5) selectedCard.system.ispositif = (Math.random() > 0.5)
selectedCard.value = (selectedCard.system.ispositif)? selectedCard.system.numericvalueup : selectedCard.system.numericvaluedown selectedCard.value = (selectedCard.system.ispositif) ? selectedCard.system.numericvalueup : selectedCard.system.numericvaluedown
rollData.total = selectedCard.value rollData.total = selectedCard.value
rollData.selectedCard = selectedCard rollData.selectedCard = selectedCard
await MaleficesUtility.createChatMessage(actor.name, "gmroll", { await MaleficesUtility.createChatMessage(actor.name, "gmroll", {
content: await renderTemplate(`systems/fvtt-malefices/templates/chat/display-tarot-card.hbs`, selectedCard) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-malefices/templates/chat/display-tarot-card.hbs`, selectedCard)
}) })
this.computeResults(rollData) this.computeResults(rollData)
if (rollData.isSuccess) { if (rollData.isSuccess) {
rollData.gainAttr = Math.ceil(rollData.confrontationDegre/2) + ((rollData.isCritical ) ? 1 : 0) rollData.gainAttr = Math.ceil(rollData.confrontationDegre / 2) + ((rollData.isCritical) ? 1 : 0)
actor.incDecAttr(rollData.attr.abbrev, rollData.gainAttr ) actor.incDecAttr(rollData.attr.abbrev, rollData.gainAttr)
} else { } else {
rollData.gainAttr = rollData.confrontationDegre rollData.gainAttr = rollData.confrontationDegre
actor.incDecAttr(rollData.attr.abbrev, -rollData.gainAttr ) actor.incDecAttr(rollData.attr.abbrev, -rollData.gainAttr)
} }
await MaleficesUtility.createChatMessage(actor.name, "gmroll", { await MaleficesUtility.createChatMessage(actor.name, "gmroll", {
content: await renderTemplate(`systems/fvtt-malefices/templates/chat/chat-confrontation-result.hbs`, rollData) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-malefices/templates/chat/chat-confrontation-result.hbs`, rollData)
}) })
this.processSpecialCard(actor, rollData) this.processSpecialCard(actor, rollData)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async rollMalefices(rollData) { static async rollMalefices(rollData) {
@@ -402,15 +423,15 @@ export class MaleficesUtility {
// Performs roll // Performs roll
console.log("Roll formula", diceFormula) console.log("Roll formula", diceFormula)
let myRoll = new Roll(diceFormula).roll({ async: false }) let myRoll = await new Roll(diceFormula).roll()
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode")) await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
rollData.roll = duplicate(myRoll) rollData.roll = foundry.utils.duplicate(myRoll)
rollData.total = myRoll.total rollData.total = myRoll.total
this.computeResults(rollData) this.computeResults(rollData)
let msg = await this.createChatWithRollMode(rollData.alias, { let msg = await this.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-malefices/templates/chat/chat-generic-result.hbs`, rollData) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-malefices/templates/chat/chat-generic-result.hbs`, rollData)
}) })
msg.setFlag("world", "rolldata", rollData) msg.setFlag("world", "rolldata", rollData)
if (rollData.mode == "initiative") { if (rollData.mode == "initiative") {
@@ -462,7 +483,7 @@ export class MaleficesUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static blindMessageToGM(chatOptions) { static blindMessageToGM(chatOptions) {
let chatGM = duplicate(chatOptions); let chatGM = foundry.utils.duplicate(chatOptions);
chatGM.whisper = this.getUsers(user => user.isGM); chatGM.whisper = this.getUsers(user => user.isGM);
chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content; chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content;
console.log("blindMessageToGM", chatGM); console.log("blindMessageToGM", chatGM);
@@ -512,7 +533,7 @@ export class MaleficesUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static getBasicRollData() { static getBasicRollData() {
let rollData = { let rollData = {
rollId: randomID(16), rollId: foundry.utils.randomID(16),
bonusMalusPerso: 0, bonusMalusPerso: 0,
bonusMalusSituation: 0, bonusMalusSituation: 0,
bonusMalusDef: 0, bonusMalusDef: 0,
@@ -538,30 +559,16 @@ export class MaleficesUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async confirmDelete(actorSheet, li) { static async confirmDelete(actorSheet, li) {
let itemId = li.data("item-id"); const itemId = li.dataset.itemId;
let msgTxt = "<p>Are you sure to remove this Item ?"; const confirmed = await foundry.applications.api.DialogV2.confirm({
let buttons = { window: { title: "Confirmer la suppression" },
delete: { content: "<p>Supprimer cet objet ?</p>",
icon: '<i class="fas fa-check"></i>', yes: { label: "Supprimer", icon: "fas fa-trash" },
label: "Yes, remove it", no: { label: "Annuler", icon: "fas fa-times" },
callback: () => {
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
li.slideUp(200, () => actorSheet.render(false));
}
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Cancel"
}
}
msgTxt += "</p>";
let d = new Dialog({
title: "Confirm removal",
content: msgTxt,
buttons: buttons,
default: "cancel"
}); });
d.render(true); if (confirmed) {
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
}
} }
} }

View File

@@ -0,0 +1,12 @@
/**
* Data model pour les archétypes
*/
export default class ArchetypeDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
lametutelaire: new fields.StringField({ initial: "" }),
description: new fields.HTMLField({ initial: "" })
};
}
}

19
modules/models/arme.mjs Normal file
View File

@@ -0,0 +1,19 @@
/**
* Data model pour les armes
*/
export default class ArmeDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
armetype: new fields.StringField({ initial: "" }),
porteecourte: new fields.StringField({ initial: "" }),
porteemoyenne: new fields.StringField({ initial: "" }),
dommagenormale: new fields.NumberField({ initial: 0, integer: true }),
dommagepart: new fields.NumberField({ initial: 0, integer: true }),
dommagecritique: new fields.NumberField({ initial: 0, integer: true }),
dommagecritiqueKO: new fields.BooleanField({ initial: false }),
dommagecritiquemort: new fields.BooleanField({ initial: false }),
description: new fields.HTMLField({ initial: "" })
};
}
}

View File

@@ -0,0 +1,11 @@
/**
* Data model pour les éléments biographiques
*/
export default class ElementbioDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" })
};
}
}

View File

@@ -0,0 +1,11 @@
/**
* Data model pour les équipements
*/
export default class EquipementDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" })
};
}
}

15
modules/models/index.mjs Normal file
View File

@@ -0,0 +1,15 @@
/**
* Index des DataModels pour Maléfices
*/
// Modèles d'acteurs
export { default as PersonnageDataModel } from './personnage.mjs';
export { default as PnjDataModel } from './pnj.mjs';
// Modèles d'items
export { default as ArmeDataModel } from './arme.mjs';
export { default as EquipementDataModel } from './equipement.mjs';
export { default as ArchetypeDataModel } from './archetype.mjs';
export { default as TarotDataModel } from './tarot.mjs';
export { default as SortilegeDataModel } from './sortilege.mjs';
export { default as ElementbioDataModel } from './elementbio.mjs';

View File

@@ -0,0 +1,95 @@
/**
* Data model pour les personnages joueurs (type "personnage")
*/
export default class PersonnageDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
// Template biodata
biodata: new fields.SchemaField({
age: new fields.NumberField({ initial: 0, integer: true }),
size: new fields.StringField({ initial: "" }),
lieunaissance: new fields.StringField({ initial: "" }),
nationalite: new fields.StringField({ initial: "" }),
profession: new fields.StringField({ initial: "" }),
residence: new fields.StringField({ initial: "" }),
milieusocial: new fields.StringField({ initial: "" }),
poids: new fields.StringField({ initial: "" }),
cheveux: new fields.StringField({ initial: "" }),
sexe: new fields.StringField({ initial: "" }),
yeux: new fields.StringField({ initial: "" }),
enfance: new fields.StringField({ initial: "" }),
adulte: new fields.StringField({ initial: "" }),
loisirs: new fields.StringField({ initial: "" }),
singularite: new fields.StringField({ initial: "" }),
politique: new fields.StringField({ initial: "" }),
religion: new fields.StringField({ initial: "" }),
fantastique: new fields.StringField({ initial: "" }),
description: new fields.HTMLField({ initial: "" }),
notes: new fields.HTMLField({ initial: "" }),
gmnotes: new fields.HTMLField({ initial: "" })
}),
// Template core
subactors: new fields.ArrayField(new fields.StringField(), { initial: [] }),
lamesdestin: new fields.ArrayField(new fields.StringField(), { initial: [] }),
pointdestin: new fields.NumberField({ initial: 1, integer: true }),
fluide: new fields.NumberField({ initial: 5, integer: true }),
mpmb: new fields.NumberField({ initial: 0, integer: true }),
mpmn: new fields.NumberField({ initial: 0, integer: true }),
equipementlibre: new fields.HTMLField({ initial: "" }),
attributs: new fields.SchemaField({
constitution: new fields.SchemaField({
label: new fields.StringField({ initial: "Constitution" }),
abbrev: new fields.StringField({ initial: "constitution" }),
value: new fields.NumberField({ initial: 0, integer: true }),
hasmax: new fields.BooleanField({ initial: true }),
max: new fields.NumberField({ initial: 0, integer: true })
}),
physique: new fields.SchemaField({
label: new fields.StringField({ initial: "Aptitudes Physiques" }),
abbrev: new fields.StringField({ initial: "physique" }),
value: new fields.NumberField({ initial: 0, integer: true }),
hasmax: new fields.BooleanField({ initial: false }),
max: new fields.NumberField({ initial: 0, integer: true })
}),
culturegenerale: new fields.SchemaField({
label: new fields.StringField({ initial: "Culture Générale" }),
abbrev: new fields.StringField({ initial: "culturegenerale" }),
value: new fields.NumberField({ initial: 0, integer: true }),
hasmax: new fields.BooleanField({ initial: false }),
max: new fields.NumberField({ initial: 0, integer: true })
}),
habilite: new fields.SchemaField({
label: new fields.StringField({ initial: "Habilité" }),
abbrev: new fields.StringField({ initial: "habilite" }),
value: new fields.NumberField({ initial: 0, integer: true }),
hasmax: new fields.BooleanField({ initial: false }),
max: new fields.NumberField({ initial: 0, integer: true })
}),
perception: new fields.SchemaField({
label: new fields.StringField({ initial: "Perception" }),
abbrev: new fields.StringField({ initial: "perception" }),
value: new fields.NumberField({ initial: 0, integer: true }),
hasmax: new fields.BooleanField({ initial: false }),
max: new fields.NumberField({ initial: 0, integer: true })
}),
spiritualite: new fields.SchemaField({
label: new fields.StringField({ initial: "Spiritualite" }),
abbrev: new fields.StringField({ initial: "spiritualite" }),
value: new fields.NumberField({ initial: 0, integer: true }),
hasmax: new fields.BooleanField({ initial: false }),
iscard: new fields.BooleanField({ initial: true }),
max: new fields.NumberField({ initial: 0, integer: true })
}),
rationnalite: new fields.SchemaField({
label: new fields.StringField({ initial: "Rationnalite" }),
abbrev: new fields.StringField({ initial: "rationnalite" }),
value: new fields.NumberField({ initial: 0, integer: true }),
hasmax: new fields.BooleanField({ initial: false }),
iscard: new fields.BooleanField({ initial: true }),
max: new fields.NumberField({ initial: 0, integer: true })
})
})
};
}
}

12
modules/models/pnj.mjs Normal file
View File

@@ -0,0 +1,12 @@
/**
* Data model pour les PNJ (type "pnj")
*/
export default class PnjDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
npctype: new fields.StringField({ initial: "" }),
description: new fields.HTMLField({ initial: "" })
};
}
}

View File

@@ -0,0 +1,12 @@
/**
* Data model pour les sortilèges
*/
export default class SortilegeDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
seuil: new fields.NumberField({ initial: 0, integer: true }),
description: new fields.HTMLField({ initial: "" })
};
}
}

17
modules/models/tarot.mjs Normal file
View File

@@ -0,0 +1,17 @@
/**
* Data model pour les lames de tarot
*/
export default class TarotDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
tarottype: new fields.StringField({ initial: "" }),
numericvalueup: new fields.NumberField({ initial: 0, integer: true }),
numericvaluedown: new fields.NumberField({ initial: 0, integer: true }),
isdualside: new fields.BooleanField({ initial: false }),
ispositif: new fields.BooleanField({ initial: true }),
isgm: new fields.BooleanField({ initial: false }),
description: new fields.HTMLField({ initial: "" })
};
}
}

4982
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

17
package.json Normal file
View File

@@ -0,0 +1,17 @@
{
"name": "fvtt-malefices",
"version": "13.0.0",
"description": "Maléfices RPG for FoundryVTT - French",
"scripts": {
"build": "gulp build",
"watch": "gulp watch"
},
"author": "Uberwald/LeRatierBretonnien",
"license": "SEE LICENSE IN LICENCE.txt",
"devDependencies": {
"gulp": "^4.0.2",
"gulp-less": "^5.0.0",
"gulp-rename": "^2.0.0",
"gulp-sourcemaps": "^3.0.0"
}
}

View File

@@ -1 +1 @@
MANIFEST-000014 MANIFEST-000064

View File

@@ -1,8 +1,7 @@
2023/10/26-09:14:30.238994 7f5603fff6c0 Recovering log #12 2026/02/27-10:28:01.892467 7f56f8bfd6c0 Recovering log #63
2023/10/26-09:14:30.250498 7f5603fff6c0 Delete type=3 #10 2026/02/27-10:28:01.948160 7f56f8bfd6c0 Delete type=0 #63
2023/10/26-09:14:30.250614 7f5603fff6c0 Delete type=0 #12 2026/02/27-10:28:01.948272 7f56f8bfd6c0 Delete type=3 #62
2023/10/26-09:23:17.065670 7f56037fe6c0 Level-0 table #17: started 2026/02/27-14:29:37.861283 7f54e37ef6c0 Level-0 table #67: started
2023/10/26-09:23:17.065742 7f56037fe6c0 Level-0 table #17: 0 bytes OK 2026/02/27-14:29:37.861314 7f54e37ef6c0 Level-0 table #67: 0 bytes OK
2023/10/26-09:23:17.072462 7f56037fe6c0 Delete type=0 #15 2026/02/27-14:29:37.867488 7f54e37ef6c0 Delete type=0 #65
2023/10/26-09:23:17.079476 7f56037fe6c0 Manual compaction at level-0 from '!items!2HWSdXDSFei9KC6y' @ 72057594037927935 : 1 .. '!items!xtYE2kVIfNtrXSoU' @ 0 : 0; will stop at (end) 2026/02/27-14:29:37.880370 7f54e37ef6c0 Manual compaction at level-0 from '!items!2HWSdXDSFei9KC6y' @ 72057594037927935 : 1 .. '!items!xtYE2kVIfNtrXSoU' @ 0 : 0; will stop at (end)
2023/10/26-09:23:17.079561 7f56037fe6c0 Manual compaction at level-1 from '!items!2HWSdXDSFei9KC6y' @ 72057594037927935 : 1 .. '!items!xtYE2kVIfNtrXSoU' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,3 @@
2023/10/26-09:13:41.637133 7f5603fff6c0 Recovering log #8 2026/02/27-00:10:01.008967 7fbc5bfff6c0 Recovering log #60
2023/10/26-09:13:41.648494 7f5603fff6c0 Delete type=3 #6 2026/02/27-00:10:01.061613 7fbc5bfff6c0 Delete type=3 #58
2023/10/26-09:13:41.648626 7f5603fff6c0 Delete type=0 #8 2026/02/27-00:10:01.061695 7fbc5bfff6c0 Delete type=0 #60
2023/10/26-09:13:55.694567 7f56037fe6c0 Level-0 table #13: started
2023/10/26-09:13:55.694619 7f56037fe6c0 Level-0 table #13: 0 bytes OK
2023/10/26-09:13:55.701338 7f56037fe6c0 Delete type=0 #11
2023/10/26-09:13:55.715996 7f56037fe6c0 Manual compaction at level-0 from '!items!2HWSdXDSFei9KC6y' @ 72057594037927935 : 1 .. '!items!xtYE2kVIfNtrXSoU' @ 0 : 0; will stop at (end)
2023/10/26-09:13:55.716063 7f56037fe6c0 Manual compaction at level-1 from '!items!2HWSdXDSFei9KC6y' @ 72057594037927935 : 1 .. '!items!xtYE2kVIfNtrXSoU' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000014 MANIFEST-000064

View File

@@ -1,8 +1,7 @@
2023/10/26-09:14:30.225660 7f56117fa6c0 Recovering log #12 2026/02/27-10:28:01.832073 7f56f93fe6c0 Recovering log #63
2023/10/26-09:14:30.236009 7f56117fa6c0 Delete type=3 #10 2026/02/27-10:28:01.886020 7f56f93fe6c0 Delete type=0 #63
2023/10/26-09:14:30.236095 7f56117fa6c0 Delete type=0 #12 2026/02/27-10:28:01.886089 7f56f93fe6c0 Delete type=3 #62
2023/10/26-09:23:17.051895 7f56037fe6c0 Level-0 table #17: started 2026/02/27-14:29:37.854040 7f54e37ef6c0 Level-0 table #67: started
2023/10/26-09:23:17.051934 7f56037fe6c0 Level-0 table #17: 0 bytes OK 2026/02/27-14:29:37.854089 7f54e37ef6c0 Level-0 table #67: 0 bytes OK
2023/10/26-09:23:17.058810 7f56037fe6c0 Delete type=0 #15 2026/02/27-14:29:37.861166 7f54e37ef6c0 Delete type=0 #65
2023/10/26-09:23:17.072664 7f56037fe6c0 Manual compaction at level-0 from '!items!5J6qIaWdnhEGMAXJ' @ 72057594037927935 : 1 .. '!items!nkRQU81L1gWOfaeo' @ 0 : 0; will stop at (end) 2026/02/27-14:29:37.880355 7f54e37ef6c0 Manual compaction at level-0 from '!items!5J6qIaWdnhEGMAXJ' @ 72057594037927935 : 1 .. '!items!nkRQU81L1gWOfaeo' @ 0 : 0; will stop at (end)
2023/10/26-09:23:17.079499 7f56037fe6c0 Manual compaction at level-1 from '!items!5J6qIaWdnhEGMAXJ' @ 72057594037927935 : 1 .. '!items!nkRQU81L1gWOfaeo' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,3 @@
2023/10/26-09:13:41.623945 7f5610ff96c0 Recovering log #8 2026/02/27-00:10:00.951941 7fbc5b7fe6c0 Recovering log #60
2023/10/26-09:13:41.634738 7f5610ff96c0 Delete type=3 #6 2026/02/27-00:10:01.006960 7fbc5b7fe6c0 Delete type=3 #58
2023/10/26-09:13:41.634829 7f5610ff96c0 Delete type=0 #8 2026/02/27-00:10:01.007022 7fbc5b7fe6c0 Delete type=0 #60
2023/10/26-09:13:55.687144 7f56037fe6c0 Level-0 table #13: started
2023/10/26-09:13:55.687180 7f56037fe6c0 Level-0 table #13: 0 bytes OK
2023/10/26-09:13:55.694295 7f56037fe6c0 Delete type=0 #11
2023/10/26-09:13:55.701584 7f56037fe6c0 Manual compaction at level-0 from '!items!5J6qIaWdnhEGMAXJ' @ 72057594037927935 : 1 .. '!items!nkRQU81L1gWOfaeo' @ 0 : 0; will stop at (end)
2023/10/26-09:13:55.716019 7f56037fe6c0 Manual compaction at level-1 from '!items!5J6qIaWdnhEGMAXJ' @ 72057594037927935 : 1 .. '!items!nkRQU81L1gWOfaeo' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

View File

@@ -1 +1 @@
MANIFEST-000014 MANIFEST-000064

View File

@@ -1,8 +1,7 @@
2023/10/26-09:14:30.253493 7f5610ff96c0 Recovering log #12 2026/02/27-10:28:01.956384 7f56e3fff6c0 Recovering log #63
2023/10/26-09:14:30.264581 7f5610ff96c0 Delete type=3 #10 2026/02/27-10:28:02.013265 7f56e3fff6c0 Delete type=0 #63
2023/10/26-09:14:30.264682 7f5610ff96c0 Delete type=0 #12 2026/02/27-10:28:02.013323 7f56e3fff6c0 Delete type=3 #62
2023/10/26-09:23:17.058988 7f56037fe6c0 Level-0 table #17: started 2026/02/27-14:29:37.873932 7f54e37ef6c0 Level-0 table #67: started
2023/10/26-09:23:17.059024 7f56037fe6c0 Level-0 table #17: 0 bytes OK 2026/02/27-14:29:37.873976 7f54e37ef6c0 Level-0 table #67: 0 bytes OK
2023/10/26-09:23:17.065403 7f56037fe6c0 Delete type=0 #15 2026/02/27-14:29:37.880214 7f54e37ef6c0 Delete type=0 #65
2023/10/26-09:23:17.079441 7f56037fe6c0 Manual compaction at level-0 from '!macros!ESV4er8Hy6liMOC3' @ 72057594037927935 : 1 .. '!macros!zDPgmHiwNxBWhoYz' @ 0 : 0; will stop at (end) 2026/02/27-14:29:37.880389 7f54e37ef6c0 Manual compaction at level-0 from '!macros!ESV4er8Hy6liMOC3' @ 72057594037927935 : 1 .. '!macros!zDPgmHiwNxBWhoYz' @ 0 : 0; will stop at (end)
2023/10/26-09:23:17.079539 7f56037fe6c0 Manual compaction at level-1 from '!macros!ESV4er8Hy6liMOC3' @ 72057594037927935 : 1 .. '!macros!zDPgmHiwNxBWhoYz' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,3 @@
2023/10/26-09:13:41.652680 7f5611ffb6c0 Recovering log #8 2026/02/27-00:10:01.064062 7fbc5affd6c0 Recovering log #60
2023/10/26-09:13:41.662766 7f5611ffb6c0 Delete type=3 #6 2026/02/27-00:10:01.123843 7fbc5affd6c0 Delete type=3 #58
2023/10/26-09:13:41.662869 7f5611ffb6c0 Delete type=0 #8 2026/02/27-00:10:01.123915 7fbc5affd6c0 Delete type=0 #60
2023/10/26-09:13:55.701609 7f56037fe6c0 Level-0 table #13: started
2023/10/26-09:13:55.701657 7f56037fe6c0 Level-0 table #13: 0 bytes OK
2023/10/26-09:13:55.708471 7f56037fe6c0 Delete type=0 #11
2023/10/26-09:13:55.716035 7f56037fe6c0 Manual compaction at level-0 from '!macros!ESV4er8Hy6liMOC3' @ 72057594037927935 : 1 .. '!macros!zDPgmHiwNxBWhoYz' @ 0 : 0; will stop at (end)
2023/10/26-09:13:55.716078 7f56037fe6c0 Manual compaction at level-1 from '!macros!ESV4er8Hy6liMOC3' @ 72057594037927935 : 1 .. '!macros!zDPgmHiwNxBWhoYz' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

View File

@@ -1 +1 @@
MANIFEST-000014 MANIFEST-000064

View File

@@ -1,8 +1,7 @@
2023/10/26-09:14:30.211232 7f5611ffb6c0 Recovering log #12 2026/02/27-10:28:01.770730 7f56f9bff6c0 Recovering log #63
2023/10/26-09:14:30.222971 7f5611ffb6c0 Delete type=3 #10 2026/02/27-10:28:01.825327 7f56f9bff6c0 Delete type=0 #63
2023/10/26-09:14:30.223076 7f5611ffb6c0 Delete type=0 #12 2026/02/27-10:28:01.825401 7f56f9bff6c0 Delete type=3 #62
2023/10/26-09:23:17.024333 7f56037fe6c0 Level-0 table #17: started 2026/02/27-14:29:37.867627 7f54e37ef6c0 Level-0 table #67: started
2023/10/26-09:23:17.024403 7f56037fe6c0 Level-0 table #17: 0 bytes OK 2026/02/27-14:29:37.867663 7f54e37ef6c0 Level-0 table #67: 0 bytes OK
2023/10/26-09:23:17.030633 7f56037fe6c0 Delete type=0 #15 2026/02/27-14:29:37.873757 7f54e37ef6c0 Delete type=0 #65
2023/10/26-09:23:17.041759 7f56037fe6c0 Manual compaction at level-0 from '!items!1DRKmbzGzbCRCswc' @ 72057594037927935 : 1 .. '!items!zbGGMEQFdwVdlKAf' @ 0 : 0; will stop at (end) 2026/02/27-14:29:37.880380 7f54e37ef6c0 Manual compaction at level-0 from '!items!1DRKmbzGzbCRCswc' @ 72057594037927935 : 1 .. '!items!zbGGMEQFdwVdlKAf' @ 0 : 0; will stop at (end)
2023/10/26-09:23:17.051874 7f56037fe6c0 Manual compaction at level-1 from '!items!1DRKmbzGzbCRCswc' @ 72057594037927935 : 1 .. '!items!zbGGMEQFdwVdlKAf' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,3 @@
2023/10/26-09:13:41.610590 7f56117fa6c0 Recovering log #8 2026/02/27-00:10:00.890338 7fbca89ff6c0 Recovering log #60
2023/10/26-09:13:41.620893 7f56117fa6c0 Delete type=3 #6 2026/02/27-00:10:00.949636 7fbca89ff6c0 Delete type=3 #58
2023/10/26-09:13:41.620978 7f56117fa6c0 Delete type=0 #8 2026/02/27-00:10:00.949712 7fbca89ff6c0 Delete type=0 #60
2023/10/26-09:13:55.669182 7f56037fe6c0 Level-0 table #13: started
2023/10/26-09:13:55.669226 7f56037fe6c0 Level-0 table #13: 0 bytes OK
2023/10/26-09:13:55.676595 7f56037fe6c0 Delete type=0 #11
2023/10/26-09:13:55.687107 7f56037fe6c0 Manual compaction at level-0 from '!items!1DRKmbzGzbCRCswc' @ 72057594037927935 : 1 .. '!items!zbGGMEQFdwVdlKAf' @ 0 : 0; will stop at (end)
2023/10/26-09:13:55.694543 7f56037fe6c0 Manual compaction at level-1 from '!items!1DRKmbzGzbCRCswc' @ 72057594037927935 : 1 .. '!items!zbGGMEQFdwVdlKAf' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

View File

Binary file not shown.

File diff suppressed because it is too large Load Diff

1
styles/simple.css.map Normal file

File diff suppressed because one or more lines are too long

View File

@@ -3,8 +3,6 @@
"esmodules": [ "esmodules": [
"modules/malefices-main.js" "modules/malefices-main.js"
], ],
"gridDistance": 1,
"gridUnits": "u",
"languages": [ "languages": [
{ {
"lang": "fr", "lang": "fr",
@@ -24,45 +22,56 @@
"type": "Item", "type": "Item",
"label": "Tarots", "label": "Tarots",
"name": "malefices-tarots", "name": "malefices-tarots",
"path": "packs/malefices-tarots.db", "path": "packs/malefices-tarots",
"system": "fvtt-malefices", "system": "fvtt-malefices",
"private": false, "flags": {},
"flags": {} "ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
}
}, },
{ {
"type": "Item", "type": "Item",
"label": "Armes", "label": "Armes",
"name": "malefices-armes", "name": "malefices-armes",
"path": "packs/malefices-armes.db", "path": "packs/malefices-armes",
"system": "fvtt-malefices", "system": "fvtt-malefices",
"private": false, "flags": {},
"flags": {} "ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
}
}, },
{ {
"type": "Item", "type": "Item",
"label": "Archetypes", "label": "Archetypes",
"name": "malefices-archetypes", "name": "malefices-archetypes",
"path": "packs/malefices-archetypes.db", "path": "packs/malefices-archetypes",
"system": "fvtt-malefices", "system": "fvtt-malefices",
"private": false, "flags": {},
"flags": {} "ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
}
}, },
{ {
"type": "Macro", "type": "Macro",
"label": "Macros", "label": "Macros",
"name": "malefices-macros", "name": "malefices-macros",
"path": "packs/malefices-macros.db", "path": "packs/malefices-macros",
"system": "fvtt-malefices", "system": "fvtt-malefices",
"private": false, "flags": {},
"flags": {} "ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
}
} }
], ],
"license": "LICENSE.txt", "license": "LICENSE.txt",
"manifest": "https://www.uberwald.me/gitea/public/fvtt-malefices/raw/branch/master/system.json", "manifest": "https://www.uberwald.me/gitea/public/fvtt-malefices/raw/branch/master/system.json",
"compatibility": { "compatibility": {
"minimum": "10", "minimum": "11",
"verified": "11", "verified": "13"
"maximum": "11"
}, },
"id": "fvtt-malefices", "id": "fvtt-malefices",
"primaryTokenAttribute": "secondary.health", "primaryTokenAttribute": "secondary.health",
@@ -73,7 +82,56 @@
], ],
"title": "Maléfices, le Jeu de Rôle", "title": "Maléfices, le Jeu de Rôle",
"url": "https://www.uberwald.me/gitea/public/fvtt-malefices", "url": "https://www.uberwald.me/gitea/public/fvtt-malefices",
"version": "11.0.2", "version": "13.0.1",
"download": "https://www.uberwald.me/gitea/public/fvtt-malefices/archive/fvtt-malefices-v11.0.2.zip", "download": "https://www.uberwald.me/gitea/public/fvtt-malefices/archive/fvtt-malefices-v13.0.1.zip",
"background": "systems/fvtt-malefices/images/ui/malefice_welcome_page.webp" "background": "systems/fvtt-malefices/images/ui/malefices-background-01.jpg",
} "documentTypes": {
"Actor": {
"personnage": {
"htmlFields": [
"biodata.description",
"biodata.notes",
"biodata.gmnotes",
"equipementlibre"
]
},
"pnj": {
"htmlFields": [
"description"
]
}
},
"Item": {
"arme": {
"htmlFields": [
"description"
]
},
"equipement": {
"htmlFields": [
"description"
]
},
"archetype": {
"htmlFields": [
"description"
]
},
"tarot": {
"htmlFields": [
"description"
]
},
"sortilege": {
"htmlFields": [
"description"
]
},
"elementbio": {
"htmlFields": [
"description"
]
}
}
}
}

View File

@@ -1,147 +0,0 @@
{
"Actor": {
"types": [
"personnage"
],
"templates": {
"biodata": {
"biodata": {
"age": 0,
"size": "",
"lieunaissance": "",
"nationalite": "",
"profession": "",
"residence": "",
"milieusocial": "",
"poids": "",
"cheveux": "",
"sexe": "",
"yeux": "",
"enfance": "",
"adulte": "",
"loisirs": "",
"singularite": "",
"politique": "",
"religion": "",
"fantastique": "",
"description": "",
"notes": "",
"gmnotes": ""
}
},
"core": {
"subactors": [],
"lamesdestin": [],
"pointdestin": 1,
"fluide": 5,
"mpmb": 0,
"mpmn": 0,
"attributs": {
"constitution": {
"label": "Constitution",
"abbrev": "constitution",
"value": 0,
"hasmax": true,
"max": 0
},
"physique": {
"label": "Aptitudes Physiques",
"abbrev": "physique",
"value": 0,
"max": 0
},
"culturegenerale": {
"label": "Culture Générale",
"abbrev": "culturegenerale",
"value": 0,
"max": 0
},
"habilite": {
"label": "Habilité",
"abbrev": "habilite",
"value": 0,
"max": 0
},
"perception": {
"label": "Perception",
"abbrev": "perception",
"value": 0,
"max": 0
},
"spiritualite": {
"label": "Spiritualite",
"abbrev": "spiritualite",
"hasmax": false,
"iscard": true,
"value": 0,
"max": 0
},
"rationnalite": {
"label": "Rationnalite",
"abbrev": "rationnalite",
"hasmax": false,
"iscard": true,
"value": 0,
"max": 0
}
},
"equipementlibre": ""
},
"npccore": {
"npctype": "",
"description": ""
}
},
"personnage": {
"templates": [
"biodata",
"core"
]
}
},
"Item": {
"types": [
"arme",
"equipement",
"archetype",
"tarot",
"sortilege",
"elementbio"
],
"templates": {},
"elementbio": {
"description": ""
},
"equipement": {
"description": ""
},
"tarot": {
"tarottype": "",
"numericvalueup": 0,
"numericvaluedown": 0,
"isdualside": false,
"ispositif": true,
"isgm": false,
"description": ""
},
"archetype": {
"lametutelaire": "",
"description": ""
},
"sortilege": {
"seuil": 0,
"description": ""
},
"arme": {
"armetype": "",
"porteecourte": "",
"porteemoyenne": "",
"dommagenormale": 0,
"dommagepart": 0,
"dommagecritique": 0,
"dommagecritiqueKO": false,
"dommagecritiquemort": false,
"description": ""
}
}
}

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}}" autocomplete="off"> <section class="{{cssClass}}" autocomplete="off">
{{!-- Sheet Header --}} {{!-- Sheet Header --}}
<header class="sheet-header"> <header class="sheet-header">
@@ -6,11 +6,11 @@
<div class="flexrow"> <div class="flexrow">
<div class="profile-img-container"> <div class="profile-img-container">
<img class="profile-img" src="{{img}}" data-edit="img" title="{{name}}" /> <img class="profile-img" src="{{actor.img}}" data-action="editImage" title="{{actor.name}}" />
</div> </div>
<div class="flexcol"> <div class="flexcol">
<h1 class="charname margin-right"><input name="name" type="text" value="{{name}}" placeholder="Name" /></h1> <h1 class="charname margin-right"><input name="name" type="text" value="{{actor.name}}" placeholder="Name" /></h1>
<div class="flexrow"> <div class="flexrow">
<ul> <ul>
@@ -33,7 +33,7 @@
</header> </header>
{{!-- Sheet Tab Navigation --}} {{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary"> <nav class="tabs" data-group="primary">
<a class="item" data-tab="main">Principal</a> <a class="item" data-tab="main">Principal</a>
<a class="item" data-tab="equipements">Equipement</a> <a class="item" data-tab="equipements">Equipement</a>
<a class="item" data-tab="biodata">Biographie</a> <a class="item" data-tab="biodata">Biographie</a>
@@ -45,7 +45,7 @@
{{!-- Skills Tab --}} {{!-- Skills Tab --}}
<div class="tab main" data-group="primary" data-tab="main"> <div class="tab main" data-group="primary" data-tab="main">
<div class="grid grid-2col"> <div class="grid grid-2col">
<div> <div>
@@ -58,7 +58,7 @@
{{#each system.attributs as |attr key|}} {{#each system.attributs as |attr key|}}
<li class="item flexrow list-item"> <li class="item flexrow list-item">
<span class="item-name-label-long"> <span class="item-name-label-long">
<a class="roll-attribut" data-attr-key="{{key}}">{{attr.label}} <a class="roll-attribut" data-action="rollAttribut" data-attr-key="{{key}}">{{attr.label}}
{{#if attr.iscard}} {{#if attr.iscard}}
<i class="fa-solid fa-cards-blank"></i> <i class="fa-solid fa-cards-blank"></i>
{{else}} {{else}}
@@ -68,12 +68,12 @@
<input type="text" class="item-field-label-short" name="system.attributs.{{key}}.value" value="{{attr.value}}" data-dtype="Number"/> <input type="text" class="item-field-label-short" name="system.attributs.{{key}}.value" value="{{attr.value}}" data-dtype="Number"/>
{{#if attr.hasmax}} {{#if attr.hasmax}}
<input type="text" class="item-field-label-short" name="system.attributs.{{key}}.max" value="{{attr.max}}" data-dtype="Number"/> <input type="text" class="item-field-label-short" name="system.attributs.{{key}}.max" value="{{attr.max}}" data-dtype="Number"/>
{{/if}} {{/if}}
{{#if (eq key "physique")}} {{#if (eq key "physique")}}
{{#if @root.phyMalus}} {{#if @root.phyMalus}}
({{@root.phyMalus}}) ({{@root.phyMalus}})
{{/if}} {{/if}}
{{/if}} {{/if}}
</li> </li>
{{/each}} {{/each}}
@@ -81,7 +81,7 @@
<span class="item-field-label-long">Points de Destin</span> <span class="item-field-label-long">Points de Destin</span>
<input type="text" class="item-field-label-short" name="system.pointdestin" value="{{system.pointdestin}}" data-dtype="Number"/> <input type="text" class="item-field-label-short" name="system.pointdestin" value="{{system.pointdestin}}" data-dtype="Number"/>
</li> </li>
{{#if isGM}} {{#if isGM}}
<li class="item flexrow list-item"> <li class="item flexrow list-item">
<span class="item-field-label-long">Fluide (MJ)</span> <span class="item-field-label-long">Fluide (MJ)</span>
@@ -110,7 +110,7 @@
{{#each armes as |arme key|}} {{#each armes as |arme key|}}
<li class="item flexrow list-item"> <li class="item flexrow list-item">
<span class="item-field-label-long"><a class="roll-arme" data-arme-id="{{arme._id}}">{{arme.name}}<i class="fa-solid fa-dice-d20"></i></a></span> <span class="item-field-label-long"><a class="roll-arme" data-action="rollArme" data-arme-id="{{arme._id}}">{{arme.name}}<i class="fa-solid fa-dice-d20"></i></a></span>
</li> </li>
{{/each}} {{/each}}
</ul> </ul>
@@ -122,19 +122,19 @@
</span> </span>
<div class="item-filler">&nbsp;</div> <div class="item-filler">&nbsp;</div>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-add" data-type="elementbio" title="Create Item"><i class="fas fa-plus"></i></a> <a class="item-control item-add" data-action="createItem" data-type="elementbio" title="Create Item"><i class="fas fa-plus"></i></a>
</div> </div>
</li> </li>
{{#each elementsbio as |elem key|}} {{#each elementsbio as |elem key|}}
<li class="item list-item flexrow list-item-shadow" data-item-id="{{elem._id}}"> <li class="item list-item flexrow list-item-shadow" data-item-id="{{elem._id}}">
<a class="item-edit item-name-img" title="Edit Item"><img class="sheet-competence-img" <a class="item-name-img" data-action="editItem" title="Edit Item"><img class="sheet-competence-img"
src="{{elem.img}}" /></a> src="{{elem.img}}" /></a>
<span class="item-name-label">{{elem.name}}</span> <span class="item-name-label">{{elem.name}}</span>
<div class="item-filler">&nbsp;</div> <div class="item-filler">&nbsp;</div>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a> <a class="item-control" data-action="deleteItem" title="Delete Item"><i class="fas fa-trash"></i></a>
</div> </div>
</li> </li>
{{/each}} {{/each}}
@@ -153,7 +153,7 @@
<h3><label class="items-title-text">Equipements (saisie libre)</label></h3> <h3><label class="items-title-text">Equipements (saisie libre)</label></h3>
</span> </span>
<div class="form-group small-editor"> <div class="form-group small-editor">
{{editor equipementlibre target="system.equipementlibre" button=true owner=owner editable=editable}} {{formInput systemFields.equipementlibre enriched=enrichedEquipementlibre value=system.equipementlibre name="system.equipementlibre" toggled=true}}
</div> </div>
<ul class="item-list alternate-list"> <ul class="item-list alternate-list">
@@ -171,12 +171,12 @@
<label class="item-field-label-medium">Critique</label> <label class="item-field-label-medium">Critique</label>
</span> </span>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-add" data-type="weapon" title="Create Item"><i class="fas fa-plus"></i></a> <a class="item-control" data-action="createItem" data-type="arme" title="Créer une arme"><i class="fas fa-plus"></i></a>
</div> </div>
</li> </li>
{{#each armes as |arme key|}} {{#each armes as |arme key|}}
<li class="item flexrow list-item list-item-shadow" data-item-id="{{arme._id}}"> <li class="item flexrow list-item list-item-shadow" data-item-id="{{arme._id}}">
<a class="item-edit item-name-img" title="Edit Item"><img class="sheet-competence-img" <a class="item-name-img" data-action="editItem" title="Edit Item"><img class="sheet-competence-img"
src="{{arme.img}}" /></a> src="{{arme.img}}" /></a>
<span class="item-name-label">{{arme.name}}</span> <span class="item-name-label">{{arme.name}}</span>
<span class="item-field-label-medium"><label>{{arme.system.dommagenormale}}</label></span> <span class="item-field-label-medium"><label>{{arme.system.dommagenormale}}</label></span>
@@ -185,7 +185,7 @@
<div class="item-filler">&nbsp;</div> <div class="item-filler">&nbsp;</div>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a> <a class="item-control" data-action="deleteItem" title="Delete Item"><i class="fas fa-trash"></i></a>
</div> </div>
</li> </li>
{{/each}} {{/each}}
@@ -201,19 +201,19 @@
</span> </span>
<div class="item-filler">&nbsp;</div> <div class="item-filler">&nbsp;</div>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-add" data-type="equipment" title="Create Item"><i class="fas fa-plus"></i></a> <a class="item-control" data-action="createItem" data-type="equipement" title="Créer un équipement"><i class="fas fa-plus"></i></a>
</div> </div>
</li> </li>
{{#each equipements as |equip key|}} {{#each equipements as |equip key|}}
<li class="item list-item flexrow list-item-shadow" data-item-id="{{equip._id}}"> <li class="item list-item flexrow list-item-shadow" data-item-id="{{equip._id}}">
<a class="item-edit item-name-img" title="Edit Item"><img class="sheet-competence-img" <a class="item-name-img" data-action="editItem" title="Edit Item"><img class="sheet-competence-img"
src="{{equip.img}}" /></a> src="{{equip.img}}" /></a>
<span class="item-name-label">{{equip.name}}</span> <span class="item-name-label">{{equip.name}}</span>
<div class="item-filler">&nbsp;</div> <div class="item-filler">&nbsp;</div>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a> <a class="item-control" data-action="deleteItem" title="Delete Item"><i class="fas fa-trash"></i></a>
</div> </div>
</li> </li>
{{/each}} {{/each}}
@@ -229,13 +229,13 @@
</span> </span>
<div class="item-filler">&nbsp;</div> <div class="item-filler">&nbsp;</div>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-add" data-type="equipment" title="Create Item"><i class="fas fa-plus"></i></a> <a class="item-control" data-action="createItem" data-type="sortilege" title="Créer un Sortilège"><i class="fas fa-plus"></i></a>
</div> </div>
</li> </li>
{{#each sorts as |sort key|}} {{#each sorts as |sort key|}}
<li class="item list-item flexrow list-item-shadow" data-item-id="{{sort._id}}"> <li class="item list-item flexrow list-item-shadow" data-item-id="{{sort._id}}">
<a class="item-edit item-name-img" title="Edit Item"><img class="sheet-competence-img" <a class="item-name-img" data-action="editItem" title="Edit Item"><img class="sheet-competence-img"
src="{{sort.img}}" /></a> src="{{sort.img}}" /></a>
<span class="item-name-label">{{sort.name}}</span> <span class="item-name-label">{{sort.name}}</span>
<span class="item-field-label-medium"> <span class="item-field-label-medium">
@@ -243,7 +243,7 @@
</span> </span>
<div class="item-filler">&nbsp;</div> <div class="item-filler">&nbsp;</div>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a> <a class="item-control" data-action="deleteItem" title="Delete Item"><i class="fas fa-trash"></i></a>
</div> </div>
</li> </li>
{{/each}} {{/each}}
@@ -261,11 +261,11 @@
<ul class="item-list alternate-list"> <ul class="item-list alternate-list">
<li class="item flexrow" data-item-id="{{archetype._id}}"> <li class="item flexrow" data-item-id="{{archetype._id}}">
<label class="item-name-label-medium">Archetype</label> <label class="item-name-label-medium">Archetype</label>
<a class="item-edit item-name-img" title="Edit Item"><img class="sheet-competence-img" <a class="item-name-img" data-action="editItem" title="Edit Item"><img class="sheet-competence-img"
src="{{archetype.img}}" /></a> src="{{archetype.img}}" /></a>
<span class="item-name-label-medium">{{archetype.name}}</span> <span class="item-name-label-medium">{{archetype.name}}</span>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a> <a class="item-control" data-action="deleteItem" title="Delete Item"><i class="fas fa-trash"></i></a>
</div> </div>
</li> </li>
<li class="item flexrow"> <li class="item flexrow">
@@ -320,7 +320,7 @@
</ul> </ul>
</div> </div>
</div> </div>
<ul> <ul>
<li class="item flexrow"> <li class="item flexrow">
<label class="generic-label">Position vis-à-vis du fantastique</label> <label class="generic-label">Position vis-à-vis du fantastique</label>
@@ -339,19 +339,19 @@
<label class="item-field-label-medium">Sens</label> <label class="item-field-label-medium">Sens</label>
</span> </span>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-add" data-type="tarot" title="Create Item"><i class="fas fa-plus"></i></a> <a class="item-control" data-action="createItem" data-type="tarot" title="Créer une lame de tarot"><i class="fas fa-plus"></i></a>
</div> </div>
</li> </li>
{{#each tarots as |tarot key|}} {{#each tarots as |tarot key|}}
<li class="item flexrow list-item list-item-shadow" data-item-id="{{tarot._id}}"> <li class="item flexrow list-item list-item-shadow" data-item-id="{{tarot._id}}">
<a class="item-edit item-name-img" title="Edit Item"><img class="sheet-competence-img" <a class="item-name-img" data-action="editItem" title="Edit Item"><img class="sheet-competence-img"
src="{{tarot.img}}" /></a> src="{{tarot.img}}" /></a>
<span class="item-name-label">{{tarot.name}}</span> <span class="item-name-label">{{tarot.name}}</span>
<span class="item-field-label-medium"><label>{{#if tarot.system.ispositif}}Positif{{else}}Négatif{{/if}}</label></span> <span class="item-field-label-medium"><label>{{#if tarot.system.ispositif}}Positif{{else}}Négatif{{/if}}</label></span>
<div class="item-filler">&nbsp;</div> <div class="item-filler">&nbsp;</div>
{{#if @root.isGM}} {{#if @root.isGM}}
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a> <a class="item-control" data-action="deleteItem" title="Delete Item"><i class="fas fa-trash"></i></a>
</div> </div>
{{/if}} {{/if}}
</li> </li>
@@ -368,19 +368,19 @@
<label class="item-field-label-medium">Sens</label> <label class="item-field-label-medium">Sens</label>
</span> </span>
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-add" data-type="tarot" title="Create Item"><i class="fas fa-plus"></i></a> <a class="item-control" data-action="createItem" data-type="tarot" title="Créer une lame de Tarot (secret)"><i class="fas fa-plus"></i></a>
</div> </div>
</li> </li>
{{#each tarotsCache as |tarot key|}} {{#each tarotsCache as |tarot key|}}
<li class="item flexrow list-item list-item-shadow" data-item-id="{{tarot._id}}"> <li class="item flexrow list-item list-item-shadow" data-item-id="{{tarot._id}}">
<a class="item-edit item-name-img" title="Edit Item"><img class="sheet-competence-img" <a class="item-name-img" data-action="editItem" title="Edit Item"><img class="sheet-competence-img"
src="{{tarot.img}}" /></a> src="{{tarot.img}}" /></a>
<span class="item-name-label">{{tarot.name}}</span> <span class="item-name-label">{{tarot.name}}</span>
<span class="item-field-label-medium"><label>{{#if tarot.system.ispositif}}Positif{{else}}Négatif{{/if}}</label></span> <span class="item-field-label-medium"><label>{{#if tarot.system.ispositif}}Positif{{else}}Négatif{{/if}}</label></span>
<div class="item-filler">&nbsp;</div> <div class="item-filler">&nbsp;</div>
{{#if @root.isGM}} {{#if @root.isGM}}
<div class="item-controls item-controls-fixed"> <div class="item-controls item-controls-fixed">
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a> <a class="item-control" data-action="deleteItem" title="Delete Item"><i class="fas fa-trash"></i></a>
</div> </div>
{{/if}} {{/if}}
</li> </li>
@@ -396,19 +396,19 @@
<h3><label class="items-title-text">Background</label></h3> <h3><label class="items-title-text">Background</label></h3>
</span> </span>
<div class="form-group editor"> <div class="form-group editor">
{{editor description target="system.biodata.description" button=true owner=owner editable=editable}} {{formInput biodataFields.description enriched=enrichedDescription value=system.biodata.description name="system.biodata.description" toggled=true}}
</div> </div>
<hr> <hr>
<span class="item-name-label-header items-title-bg"> <span class="item-name-label-header items-title-bg">
<h3><label class="items-title-text">Notes</label></h3> <h3><label class="items-title-text">Notes</label></h3>
</span> </span>
<div class="form-group editor"> <div class="form-group editor">
{{editor notes target="system.biodata.notes" button=true owner=owner editable=editable}} {{formInput biodataFields.notes enriched=enrichedNotes value=system.biodata.notes name="system.biodata.notes" toggled=true}}
</div> </div>
<hr> <hr>
</article> </div>
</div>
</div> </div>
</section> </section>
</form>
</section>

View File

@@ -0,0 +1,22 @@
<section class="{{cssClass}}" autocomplete="off">
<header class="sheet-header">
<div class="profile-img-container">
<img class="profile-img" src="{{actor.img}}" data-action="editImage" title="{{actor.name}}" />
</div>
<div class="header-fields flexcol">
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Nom" /></h1>
<div class="flexrow">
<label class="item-name-label-medium">Type</label>
<input type="text" name="system.npctype" value="{{system.npctype}}" />
</div>
</div>
</header>
<section class="sheet-body">
<div class="form-group editor" style="margin-top:0.5rem;">
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
</div>
</section>
</section>

View File

@@ -1,37 +1,55 @@
<div class="chat-message-header"> <div class="malefices-chat-card">
{{#if actorImg}}
<img class="actor-icon" src="{{actorImg}}" alt="{{alias}}" /> <header class="chat-card-header">
{{/if}} {{#if actorImg}}
<h4 class="chat-actor-name">{{alias}}</h4> <img class="chat-actor-img" src="{{actorImg}}" alt="{{alias}}" />
{{/if}}
<div class="chat-actor-name">{{alias}}</div>
{{#if img}}
<div class="chat-header-roll">
<img class="chat-roll-icon" src="{{img}}" alt="Confrontation" />
<span class="chat-roll-name">Confrontation</span>
</div>
{{/if}}
</header>
<div class="chat-card-separator"></div>
<dl class="chat-roll-details">
<div class="chat-detail-row">
<dt>{{attr.label}}</dt>
<dd>{{attr.value}}</dd>
</div>
{{#if confrontationDegre}}
<div class="chat-detail-row">
<dt>Degré de confrontation</dt>
<dd>{{confrontationDegre}}</dd>
</div>
{{/if}}
{{#if confrontationModif}}
<div class="chat-detail-row">
<dt>Ajustement</dt>
<dd>{{confrontationModif}}</dd>
</div>
{{/if}}
<div class="chat-detail-row chat-detail-target">
<dt>Seuil final</dt>
<dd>{{target}}</dd>
</div>
<div class="chat-detail-row chat-detail-roll">
<dt>Valeur de la carte</dt>
<dd>{{total}}</dd>
</div>
</dl>
<div class="chat-card-result {{#if isSuccess}}result-success{{else}}result-failure{{/if}}">
{{#if isSuccess}}
<i class="fa-solid fa-check"></i> Réussite !
<div class="chat-result-damage">{{attr.label}} augmente de {{gainAttr}} points</div>
{{else}}
<i class="fa-solid fa-xmark"></i> Échec !
<div class="chat-result-damage">{{attr.label}} diminue de {{gainAttr}} points</div>
{{/if}}
</div>
</div> </div>
<hr>
{{#if img}}
<div >
<img class="chat-icon" src="{{img}}" alt="{{name}}" />
</div>
{{/if}}
<div class="flexcol">
</div>
<div>
<ul>
<li>Confrontation de {{attr.label}} : {{attr.value}}
</li>
<li>Degré de la confrontation: {{confrontationDegre}} </li>
<li>Ajustement spécial : {{confrontationModif}} </li>
<li>Seuil final : {{target}} </li>
<li>Valeur de la carte : {{total}} </li>
{{#if isSuccess}}
<li><label class="chat-result-text chat-result-success ">Réussite !</label> Votre {{attr.label}} augmente de {{gainAttr}} points.</li>
{{else}}
<li><label class="chat-result-text chat-result-failure ">Echec !</label> Votre {{attr.label}} diminue de {{gainAttr}} points.</li>
{{/if}}
</ul>
</div>
</div>

View File

@@ -1,76 +1,91 @@
<div class="chat-message-header"> <div class="malefices-chat-card">
{{#if actorImg}}
<img class="actor-icon" src="{{actorImg}}" alt="{{alias}}" />
{{/if}}
<h4 class="chat-actor-name">{{alias}}</h4>
</div>
<hr> <header class="chat-card-header">
{{#if actorImg}}
{{#if img}} <img class="chat-actor-img" src="{{actorImg}}" alt="{{alias}}" />
<div > {{/if}}
<img class="chat-icon" src="{{img}}" alt="{{name}}" /> <div class="chat-actor-name">{{alias}}</div>
</div> {{#if img}}
{{/if}} <div class="chat-header-roll">
<img class="chat-roll-icon" src="{{img}}" alt="{{name}}" />
<span class="chat-roll-name">{{name}}</span>
</div>
{{/if}}
</header>
<div class="flexcol"> <div class="chat-card-separator"></div>
<dl class="chat-roll-details">
{{#if attr}}
<div class="chat-detail-row">
<dt>{{attr.label}}</dt>
<dd>{{attr.value}}</dd>
</div>
{{/if}}
{{#if bonusMalusPerso}}
<div class="chat-detail-row">
<dt>Bonus/Malus biographique</dt>
<dd>{{bonusMalusPerso}}</dd>
</div>
{{/if}}
{{#if bonusMalusSituation}}
<div class="chat-detail-row">
<dt>Bonus/Malus de situation</dt>
<dd>{{bonusMalusSituation}}</dd>
</div>
{{/if}}
<div class="chat-detail-row chat-detail-target">
<dt>Seuil final</dt>
<dd>{{target}}</dd>
</div> </div>
<div class="chat-detail-row chat-detail-roll">
<dt>Résultat du dé</dt>
<dd>{{roll.total}}</dd>
</div>
</dl>
<div> <div class="chat-card-result {{#if isSuccess}}result-success{{else}}result-failure{{/if}}">
<ul> {{#if isSuccess}}
{{#if attr}} {{#if isCritical}}
<li>{{attr.label}} : {{attr.value}} <i class="fa-solid fa-star"></i> Réussite Critique !
</li> {{#if arme}}
{{/if}} {{#if arme.system.dommagecritiquemort}}
<div class="chat-result-damage">La victime est morte !</div>
<li>Bonus/Malus perso: {{bonusMalusPerso}} </li>
<li>Bonus/Malus situation: {{bonusMalusSituation}} </li>
<li>Seuil final: {{target}} </li>
<li>Resultat {{roll.total}} </li>
{{#if isSuccess}}
{{#if isCritical}}
<li><label class="chat-result-text chat-result-success ">Réussite Critique !</label></li>
{{#if arme}}
{{#if arme.system.dommagecritiquemort}}
<li><label class="chat-result-success">La victime est morte !</label></li>
{{else}}
{{#if arme.system.dommagecritiqueko}}
<li><label class="chat-result-text chat-result-success ">La victime est KO !</label></li>
{{/if}}
<li><label class="chat-result-success ">La victime subit {{arme.system.dommagecritique}} dommages</label></li>
{{/if}}
{{/if}}
{{else}}
{{#if isPart}}
<li><label class="chat-result-text chat-result-success ">Réussite Particulière !</label></li>
{{#if arme}}
<li><label class="chat-result-success ">La victime subit {{arme.system.dommagepart}} dommages</label></li>
{{/if}}
{{else}}
<li><label class="chat-result-text chat-result-success ">Succés !</label></li>
{{#if arme}}
<li><label class="chat-result-success ">La victime subit {{arme.system.dommagenormale}} dommages</label></li>
{{/if}}
{{/if}}
{{/if}}
{{else}}
{{#if isFumble}}
<li><label class="chat-result-text chat-result-failure ">Echec Critique !</label></li>
{{else}} {{else}}
<li><label class="chat-result-text chat-result-failure">Echec !</label></li> {{#if arme.system.dommagecritiqueko}}
<div class="chat-result-damage">La victime est KO !</div>
{{/if}}
<div class="chat-result-damage">{{arme.system.dommagecritique}} dommages</div>
{{/if}} {{/if}}
{{/if}} {{/if}}
{{else if isPart}}
{{#if isReroll}} <i class="fa-solid fa-circle-check"></i> Réussite Particulière !
{{else}} {{#if arme}}
{{#if (gt destin 0)}} <div class="chat-result-damage">{{arme.system.dommagepart}} dommages</div>
<button class="chat-card-button roll-destin">Relancer (1 Destin)</button>
{{/if}}
{{/if}} {{/if}}
{{else}}
</ul> <i class="fa-solid fa-check"></i> Succès !
</div> {{#if arme}}
<div class="chat-result-damage">{{arme.system.dommagenormale}} dommages</div>
{{/if}}
{{/if}}
{{else}}
{{#if isFumble}}
<i class="fa-solid fa-skull"></i> Échec Critique !
{{else}}
<i class="fa-solid fa-xmark"></i> Échec !
{{/if}}
{{/if}}
</div> </div>
{{#unless isReroll}}
{{#if (gt destin 0)}}
<div class="chat-card-actions">
<button class="chat-card-button roll-destin">
<i class="fa-solid fa-rotate-right"></i> Relancer (1 Destin)
</button>
</div>
{{/if}}
{{/unless}}
</div>

Some files were not shown because too many files have changed in this diff Show More