33 Commits

Author SHA1 Message Date
dbd8352d42 Correction compendiums armes
All checks were successful
Release Creation / build (release) Successful in 1m6s
2026-03-05 21:16:06 +01:00
9aaf5dbe93 Ajout compendiums ? 2026-03-05 20:49:54 +01:00
b407f6e8c0 Migration complétée vers appv2
All checks were successful
Release Creation / build (release) Successful in 1m14s
2026-02-28 12:14:21 +01:00
aecc15d8b9 Migration complétée vers appv2 2026-02-28 12:14:01 +01:00
a2b712b78d Diverses corrections + ajouts armes en compendiums
All checks were successful
Release Creation / build (release) Successful in 1m53s
2026-02-26 13:30:53 +01:00
fc6bb7a4b1 CSS rework et autres améliorations
All checks were successful
Release Creation / build (release) Successful in 46s
2026-02-12 23:08:51 +01:00
f26130d208 CSS rework et autres améliorations 2026-02-12 23:05:22 +01:00
39da08d4cb CSS rework et autres améliorations 2026-02-12 23:04:37 +01:00
e639b6ae3e Fix rollData = 1
All checks were successful
Release Creation / build (release) Successful in 3m36s
2025-12-01 17:57:19 +01:00
8c247a8981 Echec automatique sur 1
All checks were successful
Release Creation / build (release) Successful in 49s
2025-11-21 21:36:27 +01:00
a09e1a1d95 Les compétences de base ne peuvent être supprimées
All checks were successful
Release Creation / build (release) Successful in 47s
2025-11-02 18:30:20 +01:00
787f88873a Les compétences de base ne peuvent être supprimées 2025-11-02 18:29:46 +01:00
ac481e0dd9 Various minot fixes
All checks were successful
Release Creation / build (release) Successful in 1m18s
2025-10-30 20:24:24 +00:00
375622d900 Add welcome message 2025-10-17 15:31:32 +02:00
3bc055cc1f Add welcome message 2025-10-17 15:28:15 +02:00
c97b7a4889 Correction sur blessures et actions restantes
All checks were successful
Release Creation / build (release) Successful in 53s
2025-10-16 22:59:32 +02:00
5d13500838 Fix sur combat + mains gauche
All checks were successful
Release Creation / build (release) Successful in 43s
2025-09-24 16:43:52 +02:00
d21515e1e3 Fix sur combat + mains gauche 2025-09-24 16:43:40 +02:00
78ef009465 Fix sur combat + mains gauche 2025-09-24 16:42:08 +02:00
e794611bf3 Gestion assistée pour les actions
All checks were successful
Release Creation / build (release) Successful in 46s
2025-09-24 15:11:58 +02:00
529a62045e Gestion assistée pour les actions 2025-09-24 15:09:00 +02:00
d462d22a0a Manye enhancements for combat
All checks were successful
Release Creation / build (release) Successful in 42s
2025-09-18 17:26:02 +02:00
710ee54531 Manye enhancements for combat 2025-09-18 17:24:42 +02:00
7994aa7db4 Fix savoir lors de la creation de perso
All checks were successful
Release Creation / build (release) Successful in 1m30s
2025-09-08 23:47:19 +02:00
5176b4ce87 Amelioration histoire creation de perso, CSS bouton et genre de la providence
All checks were successful
Release Creation / build (release) Successful in 56s
2025-07-20 11:21:08 +02:00
3d6f195fc2 Educcation fix + CSS v13
All checks were successful
Release Creation / build (release) Successful in 43s
2025-07-02 23:08:34 +02:00
3693d68c24 Correction sur les compétences de base
All checks were successful
Release Creation / build (release) Successful in 1m0s
2025-06-04 15:29:32 +02:00
16ccd2f3e1 Foundry v13 migration 2025-05-09 10:28:22 +02:00
631eb280ca Foundry v13 migration
All checks were successful
Release Creation / build (release) Successful in 55s
2025-05-09 10:26:29 +02:00
88ca98945f Fix #3 2025-04-20 09:25:48 +02:00
edfb2105d3 Various enhancements + fixes
All checks were successful
Release Creation / build (release) Successful in 56s
2025-04-12 00:18:45 +02:00
84cc59c57d Fix and enhancements
All checks were successful
Release Creation / build (release) Successful in 2m23s
2025-04-05 23:15:46 +02:00
e9c0fbd818 Fix URL scheme
All checks were successful
Release Creation / build (release) Successful in 1m30s
2025-03-19 13:55:30 +01:00
138 changed files with 7425 additions and 3267 deletions

View File

@@ -11,42 +11,53 @@ jobs:
- run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner."
#- uses: actions/checkout@v3
- uses: RouxAntoine/checkout@v3.5.4
- uses: https://github.com/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
uses: https://github.com/battila7/get-version-action@v2
# Substitute the Manifest and Download URLs in the module.json
- name: Substitute Manifest and Download Links For Versioned Ones
id: sub_manifest_link_version
uses: microsoft/variable-substitution@v1
uses: https://github.com/microsoft/variable-substitution@v1
with:
files: 'system.json'
files: "system.json"
env:
version: ${{steps.get_version.outputs.version-without-v}}
url: https://www.uberwald.me/gitea/${{gitea.repository}}
manifest: https://www.uberwald.me/gitea/public/${{gitea.repository}}/releases/download/latest/system.json
download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-te-deum-${{github.event.release.tag_name}}.zip
manifest: https://www.uberwald.me/gitea/public/fvtt-te-deum/releases/download/latest/system.json
download: https://www.uberwald.me/gitea/public/fvtt-te-deum/releases/download/${{github.event.release.tag_name}}/fvtt-te-deum.zip
# Create a zip file with all files required by the module to add to the release
- run: |
apt update -y
apt install -y zip
- run: zip -r ./fvtt-te-deum-${{github.event.release.tag_name}}.zip system.json template.json README.md LICENSE.txt assets/ fonts/ images/ lang/ modules/ styles/ packs/ templates/ te-deum.mjs
- run: zip -r ./fvtt-te-deum.zip system.json README.md LICENSE.txt assets/ fonts/ images/ lang/ modules/ styles/ packs/ templates/
- name: setup go
uses: https://github.com/actions/setup-go@v4
with:
go-version: '>=1.20.1'
go-version: ">=1.20.1"
- name: Use Go Action
id: use-go-action
uses: https://gitea.com/actions/release-action@main
with:
files: |-
./fvtt-te-deum-${{github.event.release.tag_name}}.zip
./fvtt-te-deum.zip
system.json
api_key: '${{secrets.ALLOW_PUSH_RELEASE}}'
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-te-deum'
version: ${{github.event.release.tag_name}}
manifest: 'https://www.uberwald.me/gitea/public/fvtt-te-deum/releases/download/latest/system.json'
notes: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-te-deum.zip'
compatibility-minimum: '13'
compatibility-verified: '13'

54
.gitignore vendored Normal file
View File

@@ -0,0 +1,54 @@
# Dependencies
node_modules/
package-lock.json
# Build outputs
styles/tedeum.css
styles/*.css.map
# IDE & Editor files
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# System files
Thumbs.db
desktop.ini
# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.log
# Temporary files
*.tmp
*.temp
.cache/
# Environment variables
.env
.env.local
# Optional: Uncomment if you want to ignore pack database files
# These are usually committed for systems, but can be regenerated
# packs/**/*.ldb
# packs/**/LOG
# packs/**/LOG.old
# packs/**/MANIFEST-*
# packs/**/CURRENT
# Compiled source
dist/
build/
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db

View File

@@ -1,3 +1,7 @@
# 13.0.0
- Support de Foundry v13
# 12.0.23
- Correction sur les jets réussie en tir

View File

@@ -1,25 +1,17 @@
var gulp = require('gulp');
var less = require('gulp-less');
var postcss = require('gulp-postcss');
var autoprefixer = require('autoprefixer');
var cssnext = require('postcss-preset-env');
var precss = require('precss');
gulp.task('css', function () {
var processors = [
autoprefixer,
cssnext,
precss
];
return gulp.src('./postcss/*.css')
.pipe(postcss(processors))
return gulp.src('./less/*.less')
.pipe(less())
.pipe(postcss([autoprefixer]))
.pipe(gulp.dest('./styles'));
});
gulp.task('watch', function () {
gulp.watch('./less/*.less', gulp.series('css'));
});
function watchUpdates() {
gulp.watch('./postcss/*.css', css);
}
gulp.task('default', gulp.series('css'));

BIN
images/icons/xpplus1.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

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

@@ -0,0 +1,469 @@
.editor {
border: 2;
height: 100%;
padding: 0 3px;
}
.medium-editor {
border: 2;
height: 240px;
max-height: 240px;
overflow-y: auto;
overflow-x: hidden;
padding: 0 3px;
prose-mirror, .editor, .editor-content, .ProseMirror {
overflow: hidden;
height: auto;
}
}
.small-editor {
border: 2;
height: 120px;
max-height: 120px;
overflow-y: auto;
overflow-x: hidden;
padding: 0 3px;
prose-mirror, .editor, .editor-content, .ProseMirror {
overflow: hidden;
height: auto;
}
}
.questionnaire-reponse {
max-width: 42rem;
margin-left: 1rem;
}
.questionnaire-element {
margin-top: 0.5rem;
}
// Style unifié pour tous les inputs et selects (fiches acteur, item, roll dialogs)
.fvtt-te-deum {
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="submit"]):not([type="image"]):not([type="file"]),
select {
background: rgba(248, 245, 238, 0.95);
color: rgba(19, 18, 18, 0.95);
border: 1px solid rgba(139, 115, 85, 0.35);
border-radius: 3px;
padding: 0.15rem 0.3rem;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
&:hover {
border-color: rgba(139, 115, 85, 0.65);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}
&:focus {
outline: none;
border-color: rgba(139, 115, 85, 0.85);
box-shadow: 0 0 0 2px rgba(139, 115, 85, 0.2);
}
&:disabled {
color: rgba(19, 18, 18, 0.4);
background: rgba(220, 216, 205, 0.6);
border-color: rgba(139, 115, 85, 0.15);
}
}
textarea {
background: rgba(248, 245, 238, 0.95);
color: rgba(19, 18, 18, 0.95);
border: 1px solid rgba(139, 115, 85, 0.35);
border-radius: 3px;
padding: 0.2rem 0.4rem;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
&:hover {
border-color: rgba(139, 115, 85, 0.65);
}
&:focus {
outline: none;
border-color: rgba(139, 115, 85, 0.85);
box-shadow: 0 0 0 2px rgba(139, 115, 85, 0.2);
}
}
}
.fvtt-te-deum.window-app .window-content,
.fvtt-te-deum.application .window-content,
.fvtt-te-deum.window-app.sheet .window-content .sheet-body,
.fvtt-te-deum.application.sheet .window-content .sheet-body {
font-size: 0.8rem;
background: rgba(226, 226, 222, 0.95);
color: rgba(19, 18, 18, 0.95);
}
// Améliorations pour les fiches d'items
.item-form {
.sheet-header {
background: linear-gradient(
135deg,
rgba(226, 226, 222, 0.95) 0%,
rgba(240, 235, 225, 0.9) 100%
);
padding: 0.8rem;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
border: 1px solid rgba(139, 115, 85, 0.3);
margin-bottom: 0.5rem;
h1.charname input {
// Voir règles haute-spécificité charname ci-dessous
}
}
.sheet-body {
ul {
list-style: none;
padding: 0;
margin: 0;
li.flexrow {
background: rgba(255, 255, 255, 0.3);
padding: 0.4rem 0.6rem;
margin: 0.3rem 0;
border-radius: 4px;
border-left: 3px solid rgba(139, 115, 85, 0.3);
transition: all 0.2s ease;
&:hover {
background: rgba(255, 255, 255, 0.5);
border-left-color: rgba(139, 115, 85, 0.6);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
}
}
h3 {
background: linear-gradient(
135deg,
rgba(196, 186, 166, 0.6) 0%,
rgba(226, 226, 222, 0.5) 100%
);
padding: 0.4rem 0.6rem;
margin: 0.8rem 0 0.4rem 0;
border-radius: 4px;
border-left: 4px solid rgba(139, 115, 85, 0.6);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
font-family: MailartRubberstamp;
font-size: 1.1rem;
color: #3d3a2e;
}
input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
}
}
// Champ "name" — règles haute-spécificité (spec 0,6,2 / 0,7,2) pour surcharger les règles génériques d'input
// Fiche acteur : font-size 3rem
.fvtt-te-deum.application .window-content .sheet-header h1.charname input[name="name"],
.fvtt-te-deum.window-app .window-content .sheet-header h1.charname input[name="name"] {
font-family: "GreatPrimer";
font-size: 3rem;
background: transparent;
border: none;
border-bottom: 2px solid rgba(139, 115, 85, 0.4);
color: rgba(50, 35, 15, 0.95);
width: 100%;
height: 100%;
margin: 0;
letter-spacing: 0.05em;
transition: border-color 0.2s ease;
&:hover {
border-width: 2px;
border-color: rgba(139, 115, 85, 0.7);
}
&:focus {
border-bottom-color: rgba(139, 115, 85, 0.9);
outline: none;
}
}
// Fiche item : même style mais font-size plus petit
.fvtt-te-deum.application .window-content .item-form .sheet-header h1.charname input[name="name"],
.fvtt-te-deum.window-app .window-content .item-form .sheet-header h1.charname input[name="name"] {
font-size: 1.6rem;
height: auto;
}
.fvtt-te-deum .sheet-body {
padding: 0.25rem 0.5rem;
&:after {
content: "";
display: block;
clear: both;
}
}
.fvtt-te-deum nav {
&.tabs {
.item {
z-index: 2;
position: relative;
opacity: 1;
color: rgba(29, 28, 31);
padding: 0 0.25rem;
&:after {
content: "";
position: absolute;
top: 0;
right: 0;
height: 2rem;
width: 1px;
}
}
}
}
.select-diff {
display: inline-block;
text-align: left;
width: 50px;
}
.fvtt-te-deum.window-app.sheet .window-content .carac-value,
.fvtt-te-deum.application.sheet .window-content .carac-value,
.fvtt-te-deum.window-app.sheet .window-content .competence-xp,
.fvtt-te-deum.application.sheet .window-content .competence-xp {
margin: 0.05rem;
flex-basis: 3rem;
text-align: center;
}
.fvtt-te-deum h1,
.fvtt-te-deum h2,
.fvtt-te-deum h3,
.fvtt-te-deum h4 {
font-weight: bold;
color: rgba(19, 18, 18, 0.95);
}
.fvtt-te-deum .malus-sante {
font-size: 0.88rem;
font-weight: normal;
}
.fvtt-te-deum .malus-sante-active {
color: rgba(200, 80, 10, 0.95);
font-weight: bold;
}
.fvtt-te-deum ul,
.fvtt-te-deum ol {
margin: 0;
padding: 0;
}
.fvtt-te-deum ul,
.fvtt-te-deum li {
list-style-type: none;
}
.header-fields {
li {
margin: 0;
padding: 0;
}
}
.alterne-list {
& > .list-item {
&:hover {
background: rgba(226, 226, 222, 0.4);
transform: translateX(2px);
}
&:nth-child(even) {
background: rgba(240, 235, 225, 0.3);
}
&:nth-child(odd) {
background: rgba(250, 245, 235, 0.2);
}
}
}
.specialisation-label {
font-size: 0.8rem;
}
.carac-label,
.attr-label {
font-weight: bold;
}
.list-item {
margin: 0.125rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border-radius: 0.3rem;
padding: 0.3rem 0.4rem;
flex: 1 1 5rem;
border: 1px solid rgba(139, 115, 85, 0.15);
transition: all 0.2s ease;
&:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
border-color: rgba(139, 115, 85, 0.3);
background: rgba(255, 255, 255, 0.3);
}
}
.list-item-shadow {
background: linear-gradient(
135deg,
rgba(170, 168, 167, 0.25) 0%,
rgba(200, 195, 185, 0.2) 100%
);
flex-grow: 0;
flex-wrap: nowrap;
justify-content: flex-start;
border-left: 3px solid rgba(139, 115, 85, 0.3);
}
.list-item-shadow2 {
background: linear-gradient(
135deg,
rgba(87, 60, 32, 0.2) 0%,
rgba(120, 90, 60, 0.15) 100%
);
flex-grow: 0;
flex-wrap: nowrap;
justify-content: flex-start;
border-left: 3px solid rgba(87, 60, 32, 0.4);
}
.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;
border-radius: 3px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
border: 1px solid rgba(139, 115, 85, 0.3);
}
.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;
}
.comp-li {
max-width: 8rem;
width: 8rem;
}
.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;
}

235
less/base.less Normal file
View File

@@ -0,0 +1,235 @@
.fvtt-te-deum.window-app,
.fvtt-te-deum.application {
text-align: justify;
font-size: 16px;
letter-spacing: 1px;
&.sheet {
.window-content {
margin: 0;
padding: 0;
font-family: "GreatPrimer";
.sheet-header {
color: rgba(19, 18, 18, 0.95);
background: rgba(226, 226, 222, 0.95);
}
.tooltip {
&:hover {
.tooltiptext {
top: 2rem;
left: 2rem;
margin: 0;
padding: 0.25rem;
}
}
}
}
}
}
.fvtt-te-deum.sheet header.sheet-header h1 input,
.window-app .window-header,
.application .window-header,
#actors .directory-list,
#navigation #scene-list .scene.nav-item {
font-size: 1rem;
}
.fvtt-te-deum.sheet {
nav {
&.sheet-tabs,
&.tabs {
font-size: 0.8rem;
font-size: 1.2rem;
font-weight: bold;
height: 3rem;
flex: 0 0 3rem;
margin: 0;
padding: 0 0 0 0.25rem;
text-align: center;
line-height: 1.5rem;
border-top: 0 none;
border-bottom: 0 none;
background:
linear-gradient(rgba(226, 226, 222, 0.5), rgba(226, 226, 222, 0.5)),
url("../images/ui/frise_bottom_01.webp");
background-repeat: no-repeat;
background-size: 100% 100%;
z-index: 1;
}
}
header {
&.sheet-header {
.profile-img {
-o-object-fit: cover;
object-fit: cover;
-o-object-position: 50% 0;
object-position: 50% 0;
margin: 0.5rem 0 0.5rem 0.5rem;
padding: 0;
}
.flex-compteurs {
text-align: right;
}
.resource-content {
width: 2rem;
}
}
}
.tab[data-tab] {
padding: 0;
}
li {
margin: 0.2rem;
padding: 0.15rem;
}
}
.fvtt-te-deum.window-app input,
.fvtt-te-deum.application input,
.fvtt-te-deum .item-form,
.fvtt-te-deum.sheet header.sheet-header .flex-group-center.flex-compteurs,
.fvtt-te-deum.sheet header.sheet-header .flex-group-center.flex-fatigue,
.fvtt-te-deum select,
.fvtt-te-deum button,
.item-checkbox,
#sidebar,
#players,
#navigation #nav-toggle {
font-size: 0.8rem;
}
.window-header {
background: rgba(0, 0, 0, 0.75);
}
.strong-text {
font-weight: bold;
}
.fvtt-te-deum .tabs .item.active,
.fvtt-te-deum .blessures-list li ul li:first-child:hover,
.fvtt-te-deum a:hover {
text-shadow: 1px 0px 0px #ff6600;
}
.rollable:hover,
.rollable:focus {
color: #000;
text-shadow: 0 0 10px red;
cursor: pointer;
}
li.folder > .folder-header h3 {
color: #aaa;
}
.fvtt-te-deum table {
border: 1px solid #7a7971;
}
.grid,
.grid-2col {
display: grid;
grid-column: span 2 / span 2;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-gap: 10px;
grid-gap: 10px;
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 {
justify-content: center;
align-items: center;
text-align: center;
padding: 5px;
}
.flex-group-left {
justify-content: flex-start;
text-align: left;
}
.flex-group-right {
justify-content: flex-end;
text-align: right;
}
.flex-center {
align-items: center;
justify-content: center;
text-align: center;
}
.table-create-actor {
font-size: 0.8rem;
}
.flex-between {
justify-content: space-between;
}
.flex-shrink {
flex: "flex-shrink";
}

581
less/chat.less Normal file
View File

@@ -0,0 +1,581 @@
.chat-message-header {
background: linear-gradient(
135deg,
rgba(226, 226, 222, 0.95) 0%,
rgba(196, 186, 166, 0.9) 100%
);
font-size: 0.9rem;
min-height: 26px;
text-align: center;
vertical-align: middle;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 2px solid rgba(139, 115, 85, 0.6);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
padding: 0.1rem 0.3rem;
border-radius: 6px 6px 0 0;
}
.chat-message .message-header .flavor-text,
.chat-message .message-header .whisper-to {
font-size: 0.9rem;
}
.chat-result-text {
font-weight: bold;
font-family: GreatPrimer;
font-size: 1.1rem;
color: rgba(80, 50, 15, 0.95);
padding: 0 0 0.1rem 0;
line-height: 1.3;
border-bottom: 1px solid rgba(139, 115, 85, 0.4);
display: block;
margin-bottom: 0.2rem;
letter-spacing: 0.03em;
}
.chat-actor-name {
font-weight: bold;
font-family: GreatPrimer;
font-size: 1.1rem;
color: rgba(80, 50, 15, 0.95);
line-height: 1;
letter-spacing: 0.03em;
margin: 0;
padding: 0;
}
.chat-actor-name-opposition {
font-weight: bold;
font-family: GreatPrimer;
font-size: 1.1rem;
color: rgba(80, 50, 15, 0.95);
padding: 0.1rem 0.3rem;
letter-spacing: 0.03em;
}
.chat-result-success {
color: #2d5016;
background: linear-gradient(
135deg,
rgba(144, 238, 144, 0.3),
rgba(107, 186, 107, 0.25)
);
padding: 0.25rem 0.6rem;
border-radius: 4px;
border-left: 3px solid #4a7c2c;
display: inline-block;
margin: 0.15rem 0;
box-shadow: 0 1px 3px rgba(45, 80, 22, 0.2);
font-weight: bold;
}
.chat-result-failure {
color: #7a1a1a;
background: linear-gradient(
135deg,
rgba(255, 160, 160, 0.3),
rgba(205, 120, 120, 0.25)
);
padding: 0.25rem 0.6rem;
border-radius: 4px;
border-left: 3px solid #a82020;
display: inline-block;
margin: 0.15rem 0;
box-shadow: 0 1px 3px rgba(122, 26, 26, 0.2);
font-weight: bold;
}
.chat-img {
width: 64px;
height: 64px;
}
.chat-command-img {
border: 0px;
width: 32px;
height: 32px;
transition:
transform 0.2s ease,
box-shadow 0.2s ease;
border-radius: 4px;
&:hover {
transform: scale(1.1);
box-shadow: 0 3px 8px rgba(255, 102, 0, 0.4);
}
}
.chat-result-column {
min-width: 70%;
width: 70%;
}
.roll-dialog-header {
background: linear-gradient(
135deg,
rgba(226, 226, 222, 0.95) 0%,
rgba(196, 186, 166, 0.9) 100%
);
min-height: 48px;
padding: 0.4rem 0.6rem;
border-radius: 6px 6px 0 0;
border-bottom: 2px solid rgba(139, 115, 85, 0.6);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
.actor-icon {
width: 40px;
height: 40px;
border-radius: 50%;
border: 2px solid rgba(139, 115, 85, 0.5);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.dialog-roll-title {
font-family: GreatPrimer;
font-size: 1.1rem;
margin: 0;
color: #3d3a2e;
text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.5);
}
}
.actor-icon {
width: 28px;
height: 28px;
padding: 1px;
border-radius: 50%;
border: 2px solid rgba(139, 115, 85, 0.5);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
flex-shrink: 0;
margin-right: 0.4rem;
}
.padding-dice {
padding-top: 0.2rem;
padding-bottom: 0.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: 0.2rem;
padding-bottom: 0.2rem;
}
.div-center {
align-self: center;
}
.chat-message {
background: rgba(240, 235, 225, 0.95);
font-size: 0.9rem;
border-radius: 8px;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
border: 1px solid rgba(139, 115, 85, 0.3);
&.whisper {
background: rgba(220, 220, 210, 0.85);
border: 2px solid #545469;
box-shadow: 0 3px 10px rgba(84, 84, 105, 0.3);
}
.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);
& > .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);
}
}
}
#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-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-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;
}
.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;
}
}
.bar-controls {
background: rgba(30, 25, 20, 1);
border: 1px solid rgba(72, 46, 28, 1);
}
}
#players {
border-image-width: 4px;
border-image-outset: 0px;
background: rgba(30, 25, 20, 1);
}
#navigation {
#scene-list {
.scene {
&.nav-item {
background: rgba(30, 25, 20, 1);
background-origin: padding-box;
border-image-width: 4px;
border-image-outset: 0px;
&.active {
background: rgba(72, 46, 28, 1);
}
}
}
}
#nav-toggle {
background: rgba(30, 25, 20, 1);
background-origin: padding-box;
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-width: 4px;
border-image-outset: 0px;
box-shadow: 0 0 3px #ff6400;
}
.chat-card-button {
box-shadow: inset 0px 1px 0px 0px #a6827e;
background: linear-gradient(
to bottom,
rgba(33, 55, 74, 0.98824) 5%,
rgba(21, 40, 51, 0.67059) 100%
);
background-color: rgba(125, 93, 59, 0);
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;
&:hover {
background: linear-gradient(to bottom, #800000 5%, #3e0101 100%);
background-color: red;
}
&:active {
position: relative;
top: 1px;
}
}
.plus-minus-button {
box-shadow: inset 0px 1px 0px 0px #a6827e;
background: linear-gradient(
to bottom,
rgba(33, 55, 74, 0.98824) 5%,
rgba(21, 40, 51, 0.67059) 100%
);
background-color: rgba(125, 93, 59, 0);
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;
}
#pause {
font-size: 2rem;
& > h3 {
color: #ccc;
}
& > img {
content: url(../images/ui/logo_tedeum_pause.webp);
height: 200px;
width: 200px;
top: -200px;
left: calc(50% - 132px);
}
}
#logo {
content: url(../images/ui/logo_tedeum_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;
}
// Améliorations esthétiques pour les messages de chat
.chat-roll-details {
background: rgba(255, 255, 255, 0.4);
border-radius: 4px;
padding: 0.4rem 0.5rem;
margin: 0.25rem 0;
border: 1px solid rgba(139, 115, 85, 0.25);
ul {
margin: 0;
padding-left: 0.8rem;
li {
padding: 0.05rem 0;
line-height: 1.25;
strong {
color: rgba(70, 67, 49, 0.9);
}
}
}
}
.chat-roll-result-section {
background: linear-gradient(
135deg,
rgba(255, 250, 240, 0.6),
rgba(245, 240, 230, 0.5)
);
border-radius: 4px;
padding: 0.35rem;
margin: 0.2rem 0;
border: 2px solid rgba(139, 115, 85, 0.3);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
.chat-total-result {
font-size: 1rem;
font-weight: bold;
color: #3d3a2e;
text-align: center;
padding: 0.25rem;
background: rgba(226, 226, 222, 0.5);
border-radius: 4px;
margin-bottom: 0.25rem;
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.5);
}
}
.chat-dice-formula {
display: inline-block;
background: rgba(139, 115, 85, 0.15);
padding: 0.15rem 0.4rem;
border-radius: 3px;
font-family: monospace;
font-size: 0.9em;
border: 1px solid rgba(139, 115, 85, 0.3);
color: #5a4a3a;
font-weight: 600;
}
.chat-difficulty-badge {
display: inline-block;
background: linear-gradient(
135deg,
rgba(180, 160, 130, 0.4),
rgba(160, 140, 110, 0.35)
);
padding: 0.15rem 0.5rem;
border-radius: 10px;
font-weight: bold;
border: 1px solid rgba(139, 115, 85, 0.4);
color: #4a3a2a;
font-size: 0.85em;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
.chat-info-badge {
display: inline-block;
background: rgba(100, 149, 237, 0.15);
padding: 0.15rem 0.4rem;
border-radius: 3px;
border-left: 2px solid rgba(70, 130, 180, 0.6);
margin: 0.08rem 0;
font-size: 0.8em;
color: #2c4a6a;
}
.chat-warning-badge {
display: inline-block;
background: rgba(255, 200, 100, 0.2);
padding: 0.15rem 0.4rem;
border-radius: 3px;
border-left: 2px solid rgba(218, 165, 32, 0.7);
margin: 0.08rem 0;
font-size: 0.8em;
color: #7a5a1a;
}
.chat-actions-bar {
display: flex;
justify-content: center;
gap: 0.3rem;
padding: 0.4rem;
background: rgba(226, 226, 222, 0.4);
border-top: 1px solid rgba(139, 115, 85, 0.25);
border-radius: 0 0 6px 6px;
margin-top: 0.25rem;
a {
transition: all 0.2s ease;
&:hover {
transform: translateY(-2px);
}
}
}
.chat-negative-dice {
display: inline-block;
background: linear-gradient(
135deg,
rgba(255, 100, 100, 0.2),
rgba(220, 80, 80, 0.15)
);
padding: 0.2rem 0.5rem;
border-radius: 4px;
border: 2px solid rgba(178, 34, 34, 0.4);
font-weight: bold;
color: #8b0000;
margin: 0.15rem 0;
box-shadow: 0 1px 3px rgba(178, 34, 34, 0.2);
}

235
less/dialogs.less Normal file
View File

@@ -0,0 +1,235 @@
.te-deum-roll-dialog {
.window-header {
border-radius: 10px 10px 0% 0%;
}
.window-content {
border-radius: 0% 0% 10px 10px;
}
}
.skill-roll-dialog {
background: linear-gradient(
135deg,
rgba(240, 235, 225, 0.98) 0%,
rgba(250, 245, 235, 0.95) 100%
);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
border: 1px solid rgba(139, 115, 85, 0.3);
overflow: hidden;
.flexcol {
padding: 0.6rem 0.8rem;
gap: 0.4rem;
}
.flexrow {
margin: 0.3rem 0;
padding: 0.4rem 0.5rem;
background: rgba(255, 255, 255, 0.4);
border-radius: 4px;
border-left: 3px solid rgba(139, 115, 85, 0.3);
align-items: center;
gap: 0.5rem;
transition: all 0.2s ease;
&:hover {
background: rgba(255, 255, 255, 0.6);
border-left-color: rgba(139, 115, 85, 0.6);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
}
.roll-dialog-label {
font-family: GreatPrimer;
font-size: 0.85rem;
font-weight: 600;
color: #3d3a2e;
min-width: 140px;
&:first-child {
color: rgba(70, 67, 49, 0.9);
font-weight: 700;
}
}
input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
div {
margin-top: 4px;
margin-bottom: 4px;
}
.sheet-footer {
padding: 0.5rem 0.8rem;
gap: 0.5rem;
border-top: 1px solid rgba(139, 115, 85, 0.3);
margin-top: 0.5rem;
button {
flex: 1;
padding: 0.4rem 0.8rem;
border-radius: 4px;
font-family: "GreatPrimer";
font-size: 0.9rem;
cursor: pointer;
border: 1px solid rgba(139, 115, 85, 0.5);
background: linear-gradient(135deg, rgba(196, 186, 166, 0.8) 0%, rgba(226, 226, 222, 0.9) 100%);
color: rgba(50, 35, 15, 0.95);
transition: all 0.2s ease;
&:hover {
background: linear-gradient(135deg, rgba(196, 186, 166, 1) 0%, rgba(210, 205, 195, 1) 100%);
border-color: rgba(139, 115, 85, 0.8);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
}
}
}
}
.confront-dice {
border-width: 0px;
}
.bonus-spec {
max-width: 48px;
}
.confront-bonus-container {
position: relative;
flex-grow: 1;
text-align: center;
color: black;
}
.pool-list {
align-items: center;
justify-content: center;
}
.corps-combat-block {
position: relative;
width: 600px;
height: 600px;
}
.silhouette-combat-picture {
width: 250px;
max-width: 250px;
border: 0;
}
.silhouette-combat-block {
position: absolute;
background: white;
border-color: darkgray;
border-style: ridge;
border: 1;
width: 180px;
min-height: 84px;
height: auto;
margin: 0 auto;
font-size: 0.8rem;
align-self: center;
overflow: visible;
}
.silhouette-combat-space {
width: 60px;
min-width: 60px;
}
.center-content {
display: flex;
justify-content: center;
align-items: center;
}
.chat-message .message {
font-family: "GreatPrimer";
font-size: 0.9rem;
}
.fvtt-te-deum-character-creator {
/*background: rgba(226, 226, 222, 0.95);*/
font-family: "GreatPrimer";
font-size: 0.9rem;
.field-title {
font-weight: bold;
}
.form-group label {
color: rgba(30, 25, 15, 0.9);
}
.status-section {
display: block;
max-width: 34rem;
}
.creator-finished-section {
display: block;
max-width: 34rem;
text-align: center;
margin-bottom: 1rem;
}
.stage-main-details {
text-align: center;
margin-top: 1rem;
}
}
.grace-texte {
margin-top: 1rem;
}
.chat-welcome {
text-align: center;
}
.item-name-label {
min-width: 12rem;
}
.compendium-sidebar .directory-item.compendium.locked .compendium-name {
background: rgba(0, 0, 0, 0.1);
}
.compendium-sidebar .directory-item.compendium .compendium-name {
background: rgba(0, 0, 0, 0.1);
}
.compendium-sidebar .directory-item.compendium:hover .compendium-name {
text-shadow: 0 0 8px var(--color-shadow-primary);
background: rgba(0, 0, 0, 0.9);
}
.compendium-sidebar .directory-item.compendium .compendium-footer .source {
display: inline-block;
font-size: var(--font-size-12);
padding: 1px 0.5rem 0 0.25rem;
border-radius: 0 3px 0 0;
background: rgba(0, 0, 0, 0.1);
}
.tedeum-create-character {
align-self: anchor-center;
}
.message-chat-center {
text-align: center;
}
.welcome-message-h3 {
font-size: 1.2rem;
text-align: center;
margin-bottom: 0.5rem;
color: darkred;
}

414
less/items.less Normal file
View File

@@ -0,0 +1,414 @@
.padd-right {
margin-right: 8px;
}
.padd-left {
margin-left: 8px;
}
.fortune-row {
flex-wrap: nowrap;
align-items: center;
gap: 0.4rem;
padding: 0.3rem 0.5rem;
margin-bottom: 0.5rem;
label {
flex: 0 0 auto;
min-width: unset;
font-weight: 600;
color: rgba(70, 67, 49, 0.9);
}
input {
flex: 0 0 auto;
}
}
.stack-left {
align-items: center;
flex-shrink: 1;
flex-grow: 0;
}
.packed-left {
white-space: nowrap;
flex-grow: 0;
}
.input-numeric-short {
width: 52px;
max-width: 52px;
flex-grow: 0;
flex-shrink: 0;
flex-basis: 52px;
margin-right: 0.15rem;
margin-left: 0.15rem;
}
.abilities-table {
align-content: flex-start;
}
.tokenhudext {
display: flex;
flex: 0 !important;
font-weight: 600;
&.left {
justify-content: flex-start;
flex-direction: column;
position: absolute;
top: 2.75rem;
right: 4rem;
}
&.right {
justify-content: flex-start;
flex-direction: column;
position: absolute;
top: 2.75rem;
left: 4rem;
}
}
.control-icon {
&.tokenhudicon {
width: -moz-fit-content;
width: fit-content;
height: -moz-fit-content;
height: fit-content;
min-width: 6rem;
flex-basis: auto;
padding: 0;
line-height: 1rem;
margin: 0.25rem;
&.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 rgba(115, 105, 83, 0.65098);
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 {
font-size: 1rem;
background-color: #f5f5f5;
background-position: 0px 35px;
background-repeat: no-repeat;
background: rgba(226, 226, 222, 0.95);
color: rgba(19, 18, 18, 0.95);
&.collapsed {
height: 470px !important;
}
#sidebar-tabs {
i {
display: inline-block;
background-position: center;
background-size: cover;
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.75);
}
}
}
#sidebar-tabs > .collapsed,
#chat-controls .chat-control-icon {
color: rgba(19, 18, 18, 0.95);
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.75);
}
.sidebar-tab {
.directory-list {
.entity {
border-top: 1px rgba(0, 0, 0, 0.25);
border-bottom: 0 none;
padding: 0.25rem 0;
&:hover {
background: rgba(0, 0, 0, 0.05);
cursor: pointer;
}
}
}
}
.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: rgba(19, 18, 18, 0.95);
background: linear-gradient(
135deg,
rgba(196, 186, 166, 0.6) 0%,
rgba(226, 226, 222, 0.5) 100%
);
border-radius: 4px;
padding: 0.3rem 0.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border-left: 4px solid rgba(139, 115, 85, 0.6);
h3 {
margin: 0;
padding: 0;
font-size: 1.15rem;
font-weight: 600;
color: rgba(19, 18, 18, 0.95);
}
}
.item-name-label-header-long2 {
flex-grow: 1;
max-width: 14rem;
min-width: 14rem;
}
.impact-box {
border-width: 2px;
border-color: #000000;
border-radius: 6px;
border: 2px ridge #443307;
margin: 4px;
padding: 4px;
}
.impact-title {
font-size: bold;
display: flex;
align-items: center;
justify-content: center;
margin-left: auto;
margin-right: auto;
text-align: center;
}
.items-title-text {
text-align: center;
font-family: MailartRubberstamp;
margin-left: 4px;
}
.lock-icon {
width: 16px;
height: 16px;
}
.item-sheet-img {
width: 64px;
height: 64px;
border: 2px solid rgba(139, 115, 85, 0.4);
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
object-fit: cover;
}
.item-name-img {
flex-grow: 1;
max-width: 2rem;
min-width: 2rem;
}
.item-field {
margin-top: 4px;
}
.item-field-xp {
margin-top: 4px;
min-width: 8rem;
max-width: 8rem;
}
.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-label-long,
.item-name-label-long {
font-weight: 600;
color: rgba(70, 67, 49, 0.9);
min-width: 160px;
}
.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-field-title-long {
flex-grow: 1;
max-width: 12rem;
min-width: 12rem;
}
.item-field-label-long14 {
flex-grow: 1;
max-width: 14rem;
min-width: 14rem;
color: rgba(70, 67, 49, 0.9);
font-size: 1.0rem;
font-weight: 600;
}
.carac-box {
background: linear-gradient(
135deg,
rgba(226, 226, 222, 0.4) 0%,
rgba(240, 235, 225, 0.3) 100%
);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border-radius: 0.45rem;
padding: 0.3rem 0.5rem;
border: 1px solid rgba(139, 115, 85, 0.2);
transition: all 0.2s ease;
&:hover {
border-color: rgba(139, 115, 85, 0.4);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
}
}
.comp-box {
max-width: 16rem;
min-width: 16rem;
width: 16rem;
min-height: 1.6rem;
background: linear-gradient(
135deg,
rgba(250, 245, 235, 0.5) 0%,
rgba(255, 255, 255, 0.3) 100%
);
padding: 0.25rem 0.4rem;
margin: 0.15rem;
border-radius: 3px;
border-left: 2px solid rgba(139, 115, 85, 0.3);
transition: all 0.2s ease;
&:hover {
background: rgba(226, 226, 222, 0.4);
border-left-color: rgba(139, 115, 85, 0.6);
transform: translateX(2px);
}
}
.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;
}
.item-controls-fixed-full {
min-width: 3rem;
max-width: 3rem;
}
.item-left-pad {
margin-left: 4px;
}
.attribute-label {
font-weight: bold;
}
.flexrow-no-expand {
flex-grow: 0;
}
.item-input-small {
max-width: 16px;
max-height: 12px;
}
.character-summary-rollable {
text-decoration: underline;
}

240
less/layout.less Normal file
View File

@@ -0,0 +1,240 @@
.fvtt-te-deum.window-app.sheet .window-content,
.fvtt-te-deum.application.sheet .window-content {
overflow: hidden;
display: flex;
flex-direction: column;
}
// AppV2: le part wrapper et la section root du template doivent propager la hauteur
// Uniquement pour les fiches (.sheet) — les autres fenêtres (dialogs) gardent un layout block normal
.application.fvtt-te-deum.sheet .window-content > [data-application-part],
.application.fvtt-te-deum.sheet .window-content > [data-application-part] > section {
flex: 1 1 0;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.fvtt-te-deum {
display: flex;
flex-direction: column;
overflow: hidden;
.sheet-header {
flex: 0 0 auto;
overflow: hidden;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
margin-bottom: 10px;
background: linear-gradient(
135deg,
rgba(226, 226, 222, 0.95) 0%,
rgba(240, 235, 225, 0.9) 100%
);
padding: 0.3rem 0.8rem;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
border: 1px solid rgba(139, 115, 85, 0.3);
.profile-img {
flex: 0 0 128px;
width: 128px;
height: auto;
max-height: 128px;
margin-top: 0px;
margin-right: 10px;
-o-object-fit: cover;
object-fit: cover;
-o-object-position: 50% 0;
object-position: 50% 0;
border-width: 0px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
border: 2px solid rgba(139, 115, 85, 0.4);
}
.header-fields {
flex: 1;
}
h1 {
&.charname {
height: 50px;
padding: 0px;
margin: 5px 0;
border-bottom: 0;
}
}
.header-identity-fields {
flex-wrap: nowrap;
align-items: center;
gap: 0.3rem;
margin-top: 0.2rem;
input, select {
flex: 1 1 auto;
font-size: 0.75rem;
height: 1.6rem;
}
}
.header-identity-label {
flex: 0 0 auto;
font-size: 0.7rem;
font-weight: 600;
color: rgba(70, 67, 49, 0.9);
white-space: nowrap;
}
}
.sheet-tabs {
flex: 0;
font-family: "MailartRubberstamp";
font-size: 2.2rem;
}
.tox {
.tox-editor-container {
background: #fff;
}
.tox-edit-area {
padding: 0 8px;
}
}
.resource-label {
font-weight: bold;
text-transform: uppercase;
}
.tabs {
height: 40px;
border-top: 1px solid rgba(139, 115, 85, 0.4);
border-bottom: 1px solid rgba(139, 115, 85, 0.4);
background: linear-gradient(
180deg,
rgba(226, 226, 222, 0.5) 0%,
rgba(240, 235, 225, 0.3) 100%
);
color: #000000;
font-family: "GreatPrimer";
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
.item {
line-height: 40px;
font-weight: bold;
padding: 0 1rem;
transition: all 0.2s ease;
&:hover {
background: rgba(226, 226, 222, 0.6);
color: rgba(70, 67, 49, 0.9);
}
&.active {
text-decoration: underline;
text-shadow: none;
background: rgba(196, 186, 166, 0.4);
border-bottom: 3px solid rgba(139, 115, 85, 0.8);
}
}
}
.items-list {
list-style: none;
margin: 1px 0;
padding: 0;
overflow-y: auto;
.item-header {
font-weight: bold;
}
.item {
height: 30px;
line-height: 24px;
padding: 1px 0;
border-bottom: 1px solid #bbb;
.item-image {
flex: 0 0 24px;
margin-right: 5px;
}
img {
display: block;
}
}
.item-name {
margin: 0;
}
.item-controls {
flex: 0 0 86px;
text-align: right;
}
}
}
.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);
&: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-te-deum .sheet-body {
flex: 1 1 0;
min-height: 0;
overflow: hidden;
font-size: 0.8rem;
font-family: "GreatPrimer";
}
.fvtt-te-deum .sheet-body .tab {
height: 100%;
overflow-y: auto;
font-size: 0.8rem;
font-family: "GreatPrimer";
}
.fvtt-te-deum .sheet-body .tab .editor {
height: 100%;
font-size: 0.8rem;
font-family: "GreatPrimer";
}

7
less/tedeum.less Normal file
View File

@@ -0,0 +1,7 @@
@import "variables";
@import "base";
@import "layout";
@import "actor-sheet";
@import "items";
@import "chat";
@import "dialogs";

30
less/variables.less Normal file
View File

@@ -0,0 +1,30 @@
@font-face {
font-family: "MailartRubberstamp";
src: url("../fonts/MailartRubberstamp-Regular.woff") format("woff");
font-family: "GreatPrimer";
src: url("../fonts/IM_FELL_Great_Primer_Roman.woff") format("woff");
}
// Variables LESS
@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: 1rem;
@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: rgba(70, 67, 49, 0.76863);
@debug-background-color-red: rgba(255, 0, 0, 0.32941);
@debug-background-color-blue: rgba(29, 0, 255, 0.32941);
@debug-background-color-green: rgba(84, 255, 0, 0.32941);
@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;

View File

@@ -1,170 +1,227 @@
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
* Feuille de personnage Te Deum - AppV2
*/
import { TeDeumUtility } from "../common/tedeum-utility.js";
const { HandlebarsApplicationMixin } = foundry.applications.api
/* -------------------------------------------- */
export class TeDeumActorPJSheet extends ActorSheet {
export class TeDeumActorPJSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
constructor(options = {}) {
super(options)
this._sheetMode = this.constructor.SHEET_MODES.PLAY
}
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
/** @override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
static DEFAULT_OPTIONS = {
classes: ["fvtt-te-deum", "sheet", "actor"],
template: "systems/fvtt-te-deum/templates/actors/actor-sheet.hbs",
position: {
width: 860,
height:680,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "skills" }],
height: 680,
},
form: {
submitOnChange: true,
closeOnSubmit: false,
},
window: {
resizable: true,
},
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
editScore: true
});
actions: {
editImage: TeDeumActorPJSheet.#onEditImage,
toggleSheet: TeDeumActorPJSheet.#onToggleSheet,
editItem: TeDeumActorPJSheet.#onEditItem,
deleteItem: TeDeumActorPJSheet.#onDeleteItem,
createItem: TeDeumActorPJSheet.#onCreateItem,
createBlessure: TeDeumActorPJSheet.#onCreateBlessure,
createCompetence: TeDeumActorPJSheet.#onCreateCompetence,
equipItem: TeDeumActorPJSheet.#onEquipItem,
modifyQuantity: TeDeumActorPJSheet.#onModifyQuantity,
rollCompetence: TeDeumActorPJSheet.#onRollCompetence,
rollArme: TeDeumActorPJSheet.#onRollArme,
rollDegats: TeDeumActorPJSheet.#onRollDegats,
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/fvtt-te-deum/templates/actors/actor-sheet.hbs",
},
}
tabGroups = { primary: "principal" }
get isEditMode() {
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
}
get isPlayMode() {
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
}
/* -------------------------------------------- */
async getData() {
let formData = {
/** @override */
async _prepareContext() {
const actor = this.document
return {
title: this.title,
id: this.actor.id,
type: this.actor.type,
img: this.actor.img,
name: this.actor.name,
id: actor.id,
type: actor.type,
img: actor.img,
name: actor.name,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
system: foundry.utils.duplicate(this.object.system),
limited: this.object.limited,
competences: this.actor.getCompetences(),
system: foundry.utils.duplicate(actor.system),
systemFields: actor.system.schema.fields,
limited: actor.limited,
competences: actor.getCompetences(),
config: foundry.utils.duplicate(game.system.tedeum.config),
armes: this.actor.getArmes(),
caracList: this.actor.prepareCaracteristiques(),
providence: this.actor.prepareProvidence(),
arbreCompetences: this.actor.prepareArbreCompetences(),
equipements: this.actor.getEquipements(),
armures: this.actor.getArmures(),
graces: this.actor.getGraces(),
blessures: this.actor.getBlessures(),
maladies: this.actor.getMaladies(),
poisons: this.actor.getPoisons(),
combat: this.actor.prepareCombat(),
bonusDegats: this.actor.getBonusDegats(),
nbActions: this.actor.getNbActions(),
initiative: this.actor.getInitiative(),
pointsArmuresLourdes: this.actor.getNbArmures(),
nbArmuresLourdes: this.actor.getNbArmuresLourdesActuel(),
santeModifier: this.actor.getSanteModifier(),
educations: this.actor.getEducations(),
description: await TextEditor.enrichHTML(this.object.system.description, { async: true }),
equipmentfree: await TextEditor.enrichHTML(this.object.system.equipmentfree, { async: true }),
notes: await TextEditor.enrichHTML(this.object.system.notes, { async: true }),
histoire: await TextEditor.enrichHTML(this.object.system.histoire, { async: true }),
options: this.options,
owner: this.document.isOwner,
editScore: this.options.editScore,
isGM: game.user.isGM
armes: actor.getArmes(),
caracList: actor.prepareCaracteristiques(),
providence: actor.prepareProvidence(),
arbreCompetences: actor.prepareArbreCompetences(),
equipements: actor.getEquipements(),
simples: actor.getSimples(),
armures: actor.getArmures(),
graces: actor.getGraces(),
blessures: actor.getBlessures(),
maladies: actor.getMaladies(),
poisons: actor.getPoisons(),
combat: actor.prepareCombat(),
bonusDegats: actor.getBonusDegats(),
nbActions: actor.getNbActions(),
initiative: actor.getInitiative(),
pointsArmuresLourdes: actor.getNbArmures(),
nbArmuresLourdes: actor.getNbArmuresLourdesActuel(),
santeModifier: actor.getSanteModifier(),
educations: actor.getEducations(),
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.description, { async: true }),
enrichedEquipmentFree: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.equipmentfree, { async: true }),
enrichedNotes: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.notes, { async: true }),
enrichedHistoire: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.histoire, { async: true }),
owner: actor.isOwner,
isEditMode: this.isEditMode,
isPlayMode: this.isPlayMode,
isGM: game.user.isGM,
}
this.formData = formData;
console.log("PC : ", formData, this.object);
return formData;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
_onRender(context, options) {
super._onRender(context, options)
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
html.bind("keydown", function(e) { // Ignore Enter in actores sheet
if (e.keyCode === 13) return false;
});
// Update Inventory Item
html.find('.item-edit').click(ev => {
const li = $(ev.currentTarget).parents(".item-id")
let itemId = li.data("item-id")
const item = this.actor.items.get( itemId );
item.sheet.render(true);
});
// Delete Inventory Item
html.find('.item-delete').click(ev => {
const li = $(ev.currentTarget).parents(".item-id")
TeDeumUtility.confirmDelete(this, li).catch("Error : No deletion confirmed")
// Tab navigation
const nav = this.element.querySelector('nav.tabs[data-group]')
if (nav) {
const group = nav.dataset.group
const activeTab = this.tabGroups[group] || "principal"
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()
})
html.find('.item-add').click(ev => {
let dataType = $(ev.currentTarget).data("type")
this.actor.createEmbeddedDocuments('Item', [{ name: "NewItem", type: dataType }], { renderSheet: true })
})
this.element.querySelectorAll(`[data-group="${group}"][data-tab]`).forEach(content => {
content.classList.toggle('active', content.dataset.tab === activeTab)
})
}
html.find('.subactor-edit').click(ev => {
const li = $(ev.currentTarget).parents(".item");
let actorId = li.data("actor-id");
let actor = game.actors.get( actorId );
actor.sheet.render(true);
});
html.find('.subactor-delete').click(ev => {
const li = $(ev.currentTarget).parents(".item");
let actorId = li.data("actor-id");
this.actor.delSubActor(actorId);
});
html.find('.quantity-minus').click(event => {
const li = $(event.currentTarget).parents(".item");
this.actor.incDecQuantity( li.data("item-id"), -1 );
} );
html.find('.quantity-plus').click(event => {
const li = $(event.currentTarget).parents(".item");
this.actor.incDecQuantity( li.data("item-id"), +1 );
} );
html.find('.roll-competence').click((event) => {
let compId = $(event.currentTarget).data("comp-id")
this.actor.rollCompetence(compId)
});
html.find('.roll-arme').click((event) => {
const armeId = $(event.currentTarget).data("arme-id")
this.actor.rollArme(armeId)
});
html.find('.roll-degats').click((event) => {
const armeId = $(event.currentTarget).data("arme-id")
this.actor.rollDegatsArme(armeId)
});
html.find('.lock-unlock-sheet').click((event) => {
this.options.editScore = !this.options.editScore;
this.render(true);
});
html.find('.item-equip').click(ev => {
const li = $(ev.currentTarget).parents(".item");
this.actor.equipItem( li.data("item-id") );
this.render(true);
});
html.find('.update-field').change(ev => {
const fieldName = $(ev.currentTarget).data("field-name");
let value = Number(ev.currentTarget.value);
this.actor.update( { [`${fieldName}`]: value } );
});
// Ignore Enter key in sheet inputs
this.element.addEventListener('keydown', e => {
if (e.keyCode === 13 && e.target.tagName !== 'TEXTAREA') e.preventDefault()
})
}
/* -------------------------------------------- */
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - 192;
sheetBody.css("height", bodyHeight);
return position;
// #region Static action handlers
static async #onEditImage(event) {
const fp = new FilePicker({
type: "image",
current: this.document.img,
callback: path => this.document.update({ img: path }),
})
fp.browse()
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.object.update(formData);
static async #onToggleSheet(event) {
this._sheetMode = this.isEditMode
? this.constructor.SHEET_MODES.PLAY
: this.constructor.SHEET_MODES.EDIT
this.render()
}
static async #onEditItem(event, target) {
const li = target.closest("[data-item-id]")
const item = this.actor.items.get(li?.dataset.itemId)
if (item) item.sheet.render(true)
}
static async #onDeleteItem(event, target) {
const li = target.closest("[data-item-id]")
await TeDeumUtility.confirmDelete(this, li)
}
static async #onCreateItem(event, target) {
const type = target.dataset.type
await this.actor.createEmbeddedDocuments('Item', [{ name: "Nouveau " + type, type }], { renderSheet: true })
}
static async #onCreateBlessure(event, target) {
const type = target.dataset.type
await this.actor.createEmbeddedDocuments('Item', [{
name: "Nouvelle " + type, type,
system: { typeBlessure: "estafilade", localisation: "corps", value: 0, appliquee: true, description: "" }
}], { renderSheet: true })
}
static async #onCreateCompetence(event, target) {
const type = target.dataset.type
const caracKey = target.dataset.caracKey
await this.actor.createEmbeddedDocuments('Item', [{ name: "Nouvelle " + type, type, system: { caracteristique: caracKey } }], { renderSheet: true })
}
static async #onEquipItem(event, target) {
const li = target.closest("[data-item-id]")
if (!li?.dataset.itemId) return
await this.actor.equipItem(li.dataset.itemId)
}
static async #onModifyQuantity(event, target) {
const li = target.closest("[data-item-id]")
if (!li?.dataset.itemId) return
const item = this.actor.items.get(li.dataset.itemId)
if (!item) return
const delta = parseInt(target.dataset.qty) || 0
await this.actor.incDecQuantity(li.dataset.itemId, delta)
}
static async #onRollCompetence(event, target) {
const li = target.closest("[data-item-id]")
if (!li?.dataset.itemId) return
await this.actor.rollCompetence(li.dataset.itemId)
}
static async #onRollArme(event, target) {
const li = target.closest("[data-item-id]")
if (!li?.dataset.itemId) return
await this.actor.rollArme(li.dataset.itemId)
}
static async #onRollDegats(event, target) {
const li = target.closest("[data-item-id]")
if (!li?.dataset.itemId) return
await this.actor.rollDegatsArme(li.dataset.itemId)
}
// #endregion
}

View File

@@ -65,14 +65,43 @@ export class TeDeumActor extends Actor {
super._preUpdate(changed, options, user);
}
/* -------------------------------------------- */
getCompetenceScore(compName) {
let competence = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == compName.toLowerCase())
if (competence) {
if (competence.system.isBase) {
return this.system.caracteristiques[competence.system.caracteristique].value
}
return competence.system.score
}
return 0
}
/* -------------------------------------------- */
getMeilleureCompetenceMainGauche(comp) {
let compScore = this.getCompetenceScore(comp.name)
let mainGaucheScore = this.getCompetenceScore("main gauche")
if (mainGaucheScore < compScore) {
ui.notifications.info(`${this.name} : Utilisation de la compétence Main Gauche au lieu de ${comp.name}`)
let mainGaucheComp = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "main gauche")
if (!mainGaucheComp) {
// Create a fake competence object
mainGaucheComp = foundry.utils.duplicate(comp)
mainGaucheComp.name = "Main Gauche"
mainGaucheComp.system.isBase = false
mainGaucheComp.system.score = 0
mainGaucheComp.system.caracteristique = "adresse"
mainGaucheComp.system.description = "Compétence Main Gauche (automatique)"
mainGaucheComp.system.isMainGauche = true
return mainGaucheComp
} else {
return mainGaucheComp
}
} else {
return comp
}
}
/* -------------------------------------------- */
_onUpdate(changed, options, userId) {
let updates = []
@@ -112,12 +141,6 @@ export class TeDeumActor extends Actor {
updates.push({ _id: initiative.id, "system.score": Number(newScore) })
}
let actionsTour = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "actions/tour")
newScore = this.getCommonBaseValue(this.system.caracteristiques.adresse.value)
if (actionsTour && actionsTour?.system.score != newScore) {
updates.push({ _id: actionsTour.id, "system.score": Number(newScore) })
}
let effort = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "effort")
newScore = this.getCommonBaseValue(this.system.caracteristiques.puissance.value)
if (effort && effort?.system.score != newScore) {
@@ -127,6 +150,8 @@ export class TeDeumActor extends Actor {
if (updates.length > 0) {
this.updateEmbeddedDocuments('Item', updates)
}
super._onUpdate(changed, options, userId);
}
/* -------------------------------------------- */
@@ -145,7 +170,7 @@ export class TeDeumActor extends Actor {
getCommonBaseValue(value) {
return game.system.tedeum.config.COMMON_VALUE[value]?.value || 0
}
getInitiative() {
getInitiativeValue() {
return game.system.tedeum.config.COMMON_VALUE[this.system.caracteristiques.adresse.value]?.value || 0
}
/* -------------------------------------------- */
@@ -153,6 +178,24 @@ export class TeDeumActor extends Actor {
return game.system.tedeum.config.BONUS_DEGATS[this.system.caracteristiques.puissance.value]
}
/* -------------------------------------------- */
getAttaqueBonusDegats(rollData = undefined) {
let base = game.system.tedeum.config.BONUS_DEGATS[this.system.caracteristiques.puissance.value].value
let additionalBonus = 0
if (rollData) {
// Spécificité armes naturelle avec gantelet
if (rollData?.arme?.system.specificites?.poing?.hasSpec && this.items.find(item => item.type == "armure" && item.name.toLowerCase() == "gantelet" && item.system.equipe)) {
additionalBonus += 1
rollData.gantelet = true
}
if (rollData.isChargeAPied) {
additionalBonus += this.getCompetenceScore("course")
} else if (rollData.isChargeACheval) {
additionalBonus += this.getCompetenceScore("equitation")
}
}
return base + additionalBonus
}
/* -------------------------------------------- */
getNbArmures() {
return game.system.tedeum.config.MAX_ARMURES_LOURDES[this.system.caracteristiques.puissance.value]
}
@@ -198,6 +241,11 @@ export class TeDeumActor extends Actor {
TeDeumUtility.sortArrayObjectsByName(comp)
return comp;
}
getSimples() {
let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'simple') || [])
TeDeumUtility.sortArrayObjectsByName(comp)
return comp;
}
getArmures() {
let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'armure') || [])
TeDeumUtility.sortArrayObjectsByName(comp)
@@ -259,41 +307,105 @@ export class TeDeumActor extends Actor {
modTotal += blessDef.modifier
}
// Si le nombre de blessures est supérieur au score d'endurance, alors malus supplémentaire
let endurance = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "endurance")
if (blessures.length > endurance.system.score) {
let enduranceScore = this.getCompetenceScore("endurance")
if (blessures.length > enduranceScore) {
modTotal += -1
}
return modTotal
}
/* -------------------------------------------- */
async appliquerDegats(rollData) {
let combat = this.prepareCombat()
rollData.defenderName = this.name
let touche = combat[rollData.loc.id].touche
if (rollData.degats > 0 && rollData.degats > touche) {
let diff = rollData.degats - touche
for (let bId in game.system.tedeum.config.blessures) {
let blessure = game.system.tedeum.config.blessures[bId]
if (diff >= blessure.degatsMin && diff <= blessure.degatsMax) {
async appliquerBlessure(blessureId, locId, comment = "") {
let blessure = game.system.tedeum.config.blessures[blessureId]
if (!blessure) {
ui.notifications.warn("Type de blessure inconnu : " + blessureId)
console.error("Type de blessure inconnu : " + blessureId)
return
}
// Create a new blessure object
let blessureObj = {
name: blessure.label,
type: "blessure",
system: {
typeBlessure: bId,
typeBlessure: blessureId,
localisation: locId || "maindroite",
value: blessure.value,
appliquee: true,
description: comment,
}
}
this.createEmbeddedDocuments('Item', [blessureObj]);
}
/* -------------------------------------------- */
getArmorDegatsModifier(rollData, combat) {
let loc = combat[rollData.loc.id]
// Sans armure
if (loc.armures.length == 0) {
return rollData.arme.system.degatsArmure.sansarmure
}
// Avec armure de cuir
if (loc.armures.find(a => a.system.typeArmure == "cuir")) {
return rollData.arme.system.degatsArmure.cuir
}
// Avec armure de maille
if (loc.armures.find(a => a.system.typeArmure == "maille")) {
return rollData.arme.system.degatsArmure.mailles
}
// Avec armure de plate
if (loc.armures.find(a => a.system.typeArmure == "plate")) {
return rollData.arme.system.degatsArmure.plates
}
return 0
}
/* -------------------------------------------- */
async appliquerDegats(rollData) {
let combat = this.prepareCombat()
rollData.defenderName = this.name
let touche = combat[rollData.loc.id].touche
let armorDegatModifier = this.getArmorDegatsModifier(rollData, combat)
rollData.degats += armorDegatModifier
rollData.armorDegatModifier = armorDegatModifier
let blessureId = "indemne"
if (rollData.degats > 0 && rollData.degats > touche) {
let diff = rollData.degats - touche
for (let bId in game.system.tedeum.config.blessures) {
let blessure = game.system.tedeum.config.blessures[bId]
if (diff >= blessure.degatsMin && diff <= blessure.degatsMax) {
if (rollData.isReussiteCritique) {
bId = game.system.tedeum.config.blessuresOrder[blessure.value + 1]
}
blessureId = bId
break
}
}
}
if (rollData.isReussiteCritique && blessureId == "indemne") { // Critical success without degats => lightest blessure
blessureId = "estafilade"
}
console.log("Appliquer dégats", rollData, combat, blessureId)
if (blessureId != "indemne") {
let blessure = game.system.tedeum.config.blessures[blessureId]
// Create a new blessure object
let blessureObj = {
name: blessure.label,
type: "blessure",
system: {
typeBlessure: blessureId,
localisation: rollData.loc.id,
value: blessure.value,
appliquee: true,
description: "Blessure infligée par un coup de " + rollData.arme.name + " de " + rollData.alias,
}
}
rollData.blessure = blessureObj
rollData.touche = touche
this.createEmbeddedDocuments('Item', [blessureObj]);
}
}
}
// Display the relevant chat message
let msg = await TeDeumUtility.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-te-deum/templates/chat/chat-blessure-result.hbs`, rollData)
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-te-deum/templates/chat/chat-blessure-result.hbs`, rollData)
})
await msg.setFlag("world", "te-deum-rolldata", rollData)
}
@@ -303,7 +415,11 @@ export class TeDeumActor extends Actor {
c.key = key
c.name = game.system.tedeum.config.caracteristiques[key].label
c.generalqualite = game.system.tedeum.config.descriptionValeur[c.value].qualite
if (this.system.genre.toLowerCase() == "homme") {
c.qualite = game.system.tedeum.config.descriptionValeur[c.value][key]
} else {
c.qualite = game.system.tedeum.config.descriptionValeurFemme[c.value][key]
}
c.dice = game.system.tedeum.config.descriptionValeur[c.value].dice
c.negativeDice = game.system.tedeum.config.descriptionValeur[c.value].negativeDice
}
@@ -322,7 +438,11 @@ export class TeDeumActor extends Actor {
prepareProvidence() {
let providence = foundry.utils.deepClone(this.system.providence)
providence.name = "Providence"
if (this.system.genre.toLowerCase() == "homme") {
providence.qualite = game.system.tedeum.config.providence[providence.value].labelM
} else {
providence.qualite = game.system.tedeum.config.providence[providence.value].labelF
}
providence.dice = game.system.tedeum.config.providence[providence.value].diceValue
providence.description = "La Providence représente la Volonté Divine à l'œuvre pour guider ou sauver un être humain. Les PJ montent dans léchelle de la Providence en menant à bien leurs missions et en se montrant vertueux. Les points de Providence peuvent servir à augmenter temporairement une caractéris- tique, à modifier la gravité d'une blessure, et à résister au vieillissement. Chaque person- nage commence avec un score initial de 1 en Providence (au niveau Pauvre pécheur)."
return providence
@@ -367,6 +487,7 @@ export class TeDeumActor extends Actor {
xp = Math.max(xp + value, 0)
await this.update({ [`system.caracteristiques.${key}.experience`]: xp })
this.sheet?.render(true)
ui.notifications.info(`+${value} XP en ${game.system.tedeum.config.caracteristiques[key].label}`)
}
/* -------------------------------------------- */
@@ -514,12 +635,16 @@ export class TeDeumActor extends Actor {
/* -------------------------------------------- */
getInitiativeScore() {
let initiative = this.items.find(it => it.type == "competence" && it.name.toLowerCase() == "initiative")
if (initiative) {
return initiative.system.score
let initiative = this.getInitiativeValue()
// Vérifie les armes avec bonus d'initiative
let armes = this.getArmes()
for (let arme of armes) {
if (arme.system.equipe && Number(arme.system.initiativeBonus) && Number(arme.system.initiativeBonus) != 0) {
ui.notifications.info("L'arme " + arme.name + " vous confère un bonus d'initiative de " + arme.system.initiativeBonus)
initiative += arme.system.initiativeBonus
}
ui.notifications.warn("Impossible de trouver la compétence Initiative pour l'acteur " + this.name)
return -1;
}
return initiative
}
/* -------------------------------------------- */
@@ -580,6 +705,7 @@ export class TeDeumActor extends Actor {
let rollData = this.getCommonCompetence(compId)
rollData.mode = "competence"
rollData.title = rollData.competence.name
rollData.compScore = rollData.competence.system.isBase ? this.system.caracteristiques[rollData.competence.system.caracteristique].value : rollData.competence.system.score
this.startRoll(rollData).catch("Error on startRoll")
}
@@ -587,13 +713,13 @@ export class TeDeumActor extends Actor {
async rollDegatsArme(armeId) {
let weapon = this.items.get(armeId)
if (weapon) {
let bDegats = 0
if ( weapon.system.typeArme == "melee" ) {
let bDegats = { value: 0 }
if (weapon.system.typeArme == "melee") {
bDegats = this.getBonusDegats()
}
let formula = weapon.system.degats + "+" + bDegats.value
let degatsRoll = await new Roll(formula).roll()
await TeDeumUtility.showDiceSoNice(degatsRoll, game.settings.get("core", "rollMode") )
await TeDeumUtility.showDiceSoNice(degatsRoll, game.settings.get("core", "rollMode"))
let rollData = this.getCommonRollData()
rollData.mode = "degats"
rollData.formula = formula
@@ -602,7 +728,7 @@ export class TeDeumActor extends Actor {
rollData.degats = degatsRoll.total
let msg = await TeDeumUtility.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-te-deum/templates/chat/chat-degats-result.hbs`, rollData)
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-te-deum/templates/chat/chat-degats-result.hbs`, rollData)
})
await msg.setFlag("world", "te-deum-rolldata", rollData)
console.log("Rolldata result", rollData)
@@ -638,9 +764,14 @@ export class TeDeumActor extends Actor {
let competence = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == compName.toLowerCase())
if (competence) {
rollData.competence = competence
rollData.compScore = rollData.competence.system.isBase ? this.system.caracteristiques[rollData.competence.system.caracteristique].value : rollData.competence.system.score
let c = foundry.utils.duplicate(this.system.caracteristiques[competence.system.caracteristique])
this.updateCarac(c, competence.system.caracteristique)
rollData.carac = c
rollData.allongeLabel = game.system.tedeum.config.armeAllonges[weapon.system.allonge].label
rollData.allongeId = "courte"
rollData.allonges = foundry.utils.duplicate(game.system.tedeum.config.allonges[weapon.system.allonge])
} else {
ui.notifications.warn("Impossible de trouver la compétence " + compName)
return
@@ -654,8 +785,7 @@ export class TeDeumActor extends Actor {
/* -------------------------------------------- */
async startRoll(rollData) {
console.log("startRoll", rollData)
let rollDialog = await TeDeumRollDialog.create(this, rollData)
rollDialog.render(true)
await TeDeumRollDialog.create(this, rollData)
}
}

View File

@@ -6,13 +6,14 @@ export class TeDeumCharacterCreator {
async init() {
this.stages = {}
this.currentStage = "origineSociale"
this.sex = undefined
this.sexe = undefined
this.origineSociale = undefined
this.religion = undefined
this.caracBonus = {}
this.competenceBonus = {}
this.suiviReponses = []
this.competences = TeDeumUtility.getCompetencesForDropDown()
this.choiceSummary = {}
for (let k in game.system.tedeum.config.caracteristiques) {
this.caracBonus[k] = { value: 0 }
@@ -39,6 +40,7 @@ export class TeDeumCharacterCreator {
} else {
this.competenceBonus[compName].value += 1
}
this.choiceSummary[this.currentStage].competences[compName] = 1
}
/*--------------------------------------------*/
@@ -69,7 +71,7 @@ export class TeDeumCharacterCreator {
/*--------------------------------------------*/
async askStageName(context) {
const content = await renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-select-stage-name.hbs", context)
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-select-stage-name.hbs", context)
const choiceResult = await foundry.applications.api.DialogV2.wait({
window: { title: context.title },
classes: ["fvtt-te-deum"],
@@ -116,6 +118,7 @@ export class TeDeumCharacterCreator {
/*--------------------------------------------*/
async askQuestionnaire(stage, context) {
context.subtitle = "Questionnaire"
this.choiceSummary[this.currentStage].questionnaire = {}
for (let key in stage.system.questionnaire) {
let question = stage.system.questionnaire[key]
@@ -127,7 +130,7 @@ export class TeDeumCharacterCreator {
context.competences = {}
context.responseKey = "reponse1" // By default
const content = await renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-select-questions.hbs", context)
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-select-questions.hbs", context)
const choiceResult = await foundry.applications.api.DialogV2.wait({
window: { title: context.title },
classes: ["fvtt-te-deum"],
@@ -170,13 +173,14 @@ export class TeDeumCharacterCreator {
let compName = context.competences[context.responseKey] || selectedResponse.compName
this.increaseCompetence(compName)
this.suiviReponses.push({ etape: stage.name, question: question.question, reponse: selectedResponse.reponse, compName: compName })
this.suiviReponses.push({ key: this.currentStage, etape: stage.name, question: question.question, reponse: selectedResponse.reponse, compName: compName })
}
}
/*------------- -------------------------------*/
async askCompetences(stage, context) {
context.subtitle = "Choix des Compétences"
this.choiceSummary[this.currentStage].competences = {}
context.fixedCompetences = {}
context.selectCompetences = {}
@@ -192,7 +196,7 @@ export class TeDeumCharacterCreator {
}
}
const content = await renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-select-competences.hbs", context)
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-select-competences.hbs", context)
const choiceResult = await foundry.applications.api.DialogV2.wait({
window: { title: context.title },
classes: ["fvtt-te-deum"],
@@ -236,7 +240,7 @@ export class TeDeumCharacterCreator {
}
}
const content = await renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-select-competences.hbs", context)
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-select-competences.hbs", context)
const choiceResult = await foundry.applications.api.DialogV2.wait({
window: { title: context.title },
classes: ["fvtt-te-deum"],
@@ -273,6 +277,10 @@ export class TeDeumCharacterCreator {
/*------------- -------------------------------*/
async askCarac(stage, context) {
context.subtitle = "Choix des Caractéristiques"
this.choiceSummary[this.currentStage] = {
caracBonus : {},
competences : {}
}
let selected = []
for (let i = 0; i < stage.system.nbChoixCarac; i++) {
@@ -283,7 +291,7 @@ export class TeDeumCharacterCreator {
context.caracList.push(game.system.tedeum.config.caracteristiques[carac.caracId])
}
const content = await renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-select-carac.hbs", context)
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-select-carac.hbs", context)
const choiceResult = await foundry.applications.api.DialogV2.wait({
window: { title: context.title },
classes: ["fvtt-te-deum"],
@@ -312,6 +320,7 @@ export class TeDeumCharacterCreator {
}
this.caracBonus[choiceResult.carac].value += 1
selected.push(choiceResult.carac)
this.choiceSummary[this.currentStage].caracBonus[choiceResult.carac] = 1
}
}
@@ -325,7 +334,7 @@ export class TeDeumCharacterCreator {
origineChoice: game.system.tedeum.config.origineSociale
}
const content = await renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-origine.hbs", context)
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-origine.hbs", context)
const label = "Valider le choix de l'Origine Sociale"
const choiceResult = await foundry.applications.api.DialogV2.wait({
window: { title: context.title },
@@ -360,6 +369,12 @@ export class TeDeumCharacterCreator {
for (let key in this.origineSociale.caracteristiques) {
this.caracBonus[key].value += this.origineSociale.caracteristiques[key]
}
this.choiceSummary['origineSociale'] = {
sexe: this.sexe,
religion: this.religion,
origineSociale: this.origineSociale.label,
caracBonus: this.caracBonus,
}
this.currentStage = "pouponniere"
}
@@ -388,6 +403,7 @@ export class TeDeumCharacterCreator {
this.pouponniere = foundry.utils.duplicate(stage.items.find(item => item.id === choiceResult.selectedItem))
context.title = `La Pouponnière - ${this.pouponniere.name}`
TeDeumUtility.prepareEducationContent(this.pouponniere);
this.choiceSummary['pouponniere'] = {}
context.label = "Valider l'augmentation de caracteristique"
await this.askCarac(this.pouponniere, context)
@@ -559,7 +575,7 @@ export class TeDeumCharacterCreator {
for (let compName in this.competenceBonus) {
let comp = actor.items.find( i => i.type == "competence" && i.name.toLowerCase() === compName.toLowerCase())
if (comp) {
updateComp.push({ _id: comp._id, "system.score": this.competenceBonus[compName].value })
updateComp.push({ _id: comp._id, "system.score": comp.system.score + this.competenceBonus[compName].value })
} else {
toAdd.push( compName)
}
@@ -581,8 +597,36 @@ export class TeDeumCharacterCreator {
await actor.update({ [`system.fortune.${this.origineSociale.cagnotteUnit}`]: newArgent})
let histoire = ""
for (let reponse of this.suiviReponses) {
histoire += `<p>${reponse.question}<br>${reponse.reponse} (${reponse.compName})</p>`
for ( let key in this.choiceSummary) {
let stageSummary = this.choiceSummary[key]
if (stageSummary.sexe) {
histoire += `<h3>Origine Sociale</h3>`
histoire += `<p>${stageSummary.sexe} - ${stageSummary.religion} - ${stageSummary.origineSociale}</p>`
} else {
histoire += `<h3>${game.system.tedeum.config.etapesEducation[key].label}</h3>`
}
if (stageSummary.caracBonus) {
histoire += `<p><strong>Caractéristiques : </strong><ul>`
for (let caracKey in stageSummary.caracBonus) {
histoire += `<li>${TeDeumUtility.upperFirst(caracKey)} +1</li>`
}
histoire += `</ul></p>`
}
if (stageSummary.competences) {
histoire += `<p><strong>Compétences : </strong><ul>`
for (let compName in stageSummary.competences) {
histoire += `<li>${TeDeumUtility.upperFirst(compName)} +1</li>`
}
histoire += `</ul></p>`
}
let questions = this.suiviReponses.filter( r => r.key === key)
if (questions.length > 0) {
histoire += `<p><strong>Réponses au questionnaire : </strong><ul>`
for (let question of questions) {
histoire += `<li>${question.question} : <i>${question.reponse}</i> (${TeDeumUtility.upperFirst(question.compName)}+1)</li>`
}
histoire += `</ul></p>`
}
}
await actor.update({ "system.histoire": histoire})
actor.render(true)
@@ -596,7 +640,7 @@ export class TeDeumCharacterCreator {
"adresse": { score: actor.getCompetenceScore("Initiative"), label: "Adresse" },
}
const content = await renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-finished.hbs", context)
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-te-deum/templates/dialogs/character-creator-finished.hbs", context)
const label = "Terminer"
const choiceResult = await foundry.applications.api.DialogV2.wait({
window: { title: context.title },

View File

@@ -4,18 +4,42 @@ import { TeDeumUtility } from "../common/tedeum-utility.js";
export class TeDeumCombat extends Combat {
/* -------------------------------------------- */
async rollInitiative(ids, formula = undefined, messageOptions = {} ) {
async rollInitiative(ids, formula = undefined, messageOptions = {}) {
//console.log("Roll INIT !")
ids = typeof ids === "string" ? [ids] : ids;
for (let cId of ids) {
const c = this.combatants.get(cId);
let initBonus = c.actor ? c.actor.getInitiativeScore( this.id, cId ) : -1;
await this.updateEmbeddedDocuments("Combatant", [ { _id: cId, initiative: initBonus } ]);
let initBonus = c.actor ? c.actor.getInitiativeScore(this.id, cId) : -1;
console.log("Init Bonus : ", c.name, initBonus)
await this.updateEmbeddedDocuments("Combatant", [{ _id: cId, initiative: initBonus }]);
}
return this;
}
/* -------------------------------------------- */
async modifyAction(combatantId, delta, isMainGauche = false) {
let combatant = this.combatants.get(combatantId)
if (!combatant) return;
let ca = combatant.getFlag("world", "available-actions")
if (!ca) {
ca = { nbActions: 1, nbActionsMainGauche: 0 }
}
if (isMainGauche) {
ca.nbActionsMainGauche += delta
} else {
ca.nbActions += delta
}
if (ca.nbActionsMainGauche < 0) ca.nbActionsMainGauche = 0
if (ca.nbActions < 0) ca.nbActions = 0
console.log("Modify Action : ", combatant.name, ca)
if (game.user.isGM) {
await TeDeumUtility.updateCombatantActions(combatant, ca)
} else {
game.socket.emit("system.fvtt-te-deum", { msg: "msg_modify_combat_action", data: { combatantId: combatantId, ca: ca } })
}
}
/* -------------------------------------------- */
static async checkTurnPosition() {
while (game.combat.turn > 0) {

View File

@@ -15,19 +15,35 @@ export const TEDEUM_CONFIG = {
{ value: 10 }, { value: 30 }, { value: 50 }],
LOCALISATION: {
"pieddroit": { label: "Pied Droit", value: 1, locMod: 0, id: "pieddroit", nbArmure: 1, score: { min: 1, max: 1 }, coord: { top: 500, left: 0 } },
"jambedroite": { label: "Jambe Droite", value: 1, locMod: -1, id: "jambedroite", nbArmure: 1, score: { min: 3, max: 4 }, coord: { top: 400, left: 100 } },
"jambegauche": { label: "Jambe Gauche", value: 1, locMod: -1, id: "jambegauche", nbArmure: 1, score: { min: 5, max: 6 }, coord: { top: 400, left: 300 } },
"piedgauche": { label: "Pied Gauche", value: 1, locMod: 0, id: "piedgauche", nbArmure: 1, score: { min: 2, max: 2 }, coord: { top: 500, left: 400 } },
"maindroite": { label: "Main Droite", value: 1, locMod: 0, id: "maindroite", nbArmure: 1, score: { min: 7, max: 7 }, coord: { top: 0, left: 0 } },
"maingauche": { label: "Main Gauche", value: 1, locMod: 0, id: "maingauche", nbArmure: 1, score: { min: 8, max: 8 }, coord: { top: 0, left: 400 } },
"brasdroit": { label: "Bras Droit", value: 1, locMod: -1, id: "brasdroit", nbArmure: 2, score: { min: 9, max: 10 }, coord: { top: 200, left: 0 } },
"brasgauche": { label: "Bras Gauche", value: 1, locMod: -1, id: "brasgauche", nbArmure: 2, score: { min: 11, max: 12 }, coord: { top: 200, left: 400 } },
"corps": { label: "Corps", value: 1, id: "corps", locMod: -2, nbArmure: 2, score: { min: 13, max: 17 }, coord: { top: 200, left: 200 } },
"tete": { label: "Tête", value: 1, id: "tete", locMod: -2, nbArmure: 2, score: { min: 18, max: 20 }, coord: { top: 0, left: 200 } },
"pieddroit": { label: "Pied Droit", value: 1, locMod: 0, id: "pieddroit", categorie: "pied", nbArmure: 1, score: { min: 1, max: 1 }, coord: { top: 500, left: 0 } },
"jambedroite": { label: "Jambe Droite", value: 1, locMod: -1, id: "jambedroite", categorie: "jambe", nbArmure: 1, score: { min: 3, max: 4 }, coord: { top: 400, left: 100 } },
"jambegauche": { label: "Jambe Gauche", value: 1, locMod: -1, id: "jambegauche", categorie: "jambe", nbArmure: 1, score: { min: 5, max: 6 }, coord: { top: 400, left: 300 } },
"piedgauche": { label: "Pied Gauche", value: 1, locMod: 0, id: "piedgauche", categorie: "pied", nbArmure: 1, score: { min: 2, max: 2 }, coord: { top: 500, left: 400 } },
"maindroite": { label: "Main Droite", value: 1, locMod: 0, id: "maindroite", categorie: "main", nbArmure: 1, score: { min: 7, max: 7 }, coord: { top: 0, left: 0 } },
"maingauche": { label: "Main Gauche", value: 1, locMod: 0, id: "maingauche", categorie: "main", nbArmure: 1, score: { min: 8, max: 8 }, coord: { top: 0, left: 400 } },
"brasdroit": { label: "Bras Droit", value: 1, locMod: -1, id: "brasdroit", categorie: "bras", nbArmure: 2, score: { min: 9, max: 10 }, coord: { top: 200, left: 0 } },
"brasgauche": { label: "Bras Gauche", value: 1, locMod: -1, id: "brasgauche", categorie: "bras", nbArmure: 2, score: { min: 11, max: 12 }, coord: { top: 200, left: 400 } },
"corps": { label: "Corps", value: 1, id: "corps", categorie: "corps", locMod: -2, nbArmure: 2, score: { min: 13, max: 17 }, coord: { top: 200, left: 200 } },
"tete": { label: "Tête", value: 1, id: "tete", categorie: "tete", locMod: -2, nbArmure: 2, score: { min: 18, max: 20 }, coord: { top: 0, left: 200 } },
},
ATTAQUE_CIBLEES: {
"aucune": { label: "Aucune", id: "aucune", locMod: 0, description: "Attaque non ciblée" },
"pieddroit": { label: "Pied Droit", id: "pieddroit", locMod: 0, description: "Attaque ciblée sur le pied droit" },
"jambedroite": { label: "Jambe Droite", id: "jambedroite", locMod: -1, description: "Attaque ciblée sur la jambe droite" },
"jambegauche": { label: "Jambe Gauche", id: "jambegauche", locMod: -1, description: "Attaque ciblée sur la jambe gauche" },
"piedgauche": { label: "Pied Gauche", id: "piedgauche", locMod: 0, description: "Attaque ciblée sur le pied gauche" },
"maindroite": { label: "Main Droite", id: "maindroite", locMod: 0, description: "Attaque ciblée sur la main droite" },
"maingauche": { label: "Main Gauche", id: "maingauche", locMod: 0, description: "Attaque ciblée sur la main gauche" },
"brasdroit": { label: "Bras Droit", id: "brasdroit", locMod: -1, description: "Attaque ciblée sur le bras droit" },
"brasgauche": { label: "Bras Gauche", id: "brasgauche", locMod: -1, description: "Attaque ciblée sur le bras gauche" },
"corps": { label: "Corps", id: "corps", locMod: -2, description: "Attaque ciblée sur le corps" },
"tete": { label: "Tête", id: "tete", locMod: -2, description: "Attaque ciblée sur la tête" },
},
ARME_SPECIFICITE: {
"poing": { label: "Poings", id: "poing", melee: true, tir: false },
"pied": { label: "Pieds", id: "pied", melee: true, tir: false },
"encombrante": { label: "Encombrante", id: "encombrante", melee: true, tir: true },
"maintiendistance": { label: "Maintien à distance", id: "maintiendistance", melee: true, tir: false },
"coupassomant": { label: "Coup assomant", id: "coupassomant", melee: true, tir: false },
@@ -43,11 +59,11 @@ export const TEDEUM_CONFIG = {
},
ARME_PORTEES: {
"brulepourpoint": { label: "Brûle-pourpoint", difficulty: "facile", id: "brulepourpoint" },
"courte": { label: "Courte", difficulty: "pardefaut", id: "courte" },
"moyenne": { label: "Moyenne", difficulty: "difficile", id: "moyenne" },
"longue": { label: "Longue", difficulty: "perilleux", id: "longue" },
"extreme": { label: "Extrême", difficulty: "desespere", id: "extreme" },
"brulepourpoint": { label: "Brûle-pourpoint (5)", difficulty: "facile", id: "brulepourpoint" },
"courte": { label: "Courte (7)", difficulty: "pardefaut", id: "courte" },
"moyenne": { label: "Moyenne (11)", difficulty: "difficile", id: "moyenne" },
"longue": { label: "Longue (13)", difficulty: "perilleux", id: "longue" },
"extreme": { label: "Extrême (15)", difficulty: "desespere", id: "extreme" },
},
genre: {
@@ -57,7 +73,7 @@ export const TEDEUM_CONFIG = {
descriptionValeurOdd: {
1: { valeur: 1, qualite: "Mauvais", dice: "d4", negativeDice: "d20", savoir: "Sot", sensibilite: "Obtus", entregent: "Rustaud", puissance: "Menu", complexion: "Anémique", adresse: "Empesé" },
2: { valeur: 2, qualite: "Médiocre", dice: "d6", negativeDice: "d12", savoir: "Limité", sensibilite: "Etriqué", entregent: "Frustre", puissance: "Délicat", complexion: "Languide", adresse: "Gauche" },
2: { valeur: 2, qualite: "Médiocre", dice: "d6", negativeDice: "d12", savoir: "Limité", sensibilite: "Etriqué", entregent: "Fruste", puissance: "Délicat", complexion: "Languide", adresse: "Gauche" },
3: { valeur: 3, qualite: "Correct", dice: "d8", negativeDice: "d10", savoir: "Mêlé", sensibilite: "Ouvert", entregent: "Badin", puissance: "Membru", complexion: "Dispos", adresse: "Ingambe" },
4: { valeur: 4, qualite: "Bon", dice: "d10", negativeDice: "d8", savoir: "Lettré", sensibilite: "Fin", entregent: "Disert", puissance: "Vigoureux", complexion: "Gaillard", adresse: "Leste" },
5: { valeur: 5, qualite: "Bon", dice: "d10", negativeDice: "d8", savoir: "Lettré", sensibilite: "Fin", entregent: "Disert", puissance: "Vigoureux", complexion: "Gaillard", adresse: "Leste" },
@@ -71,12 +87,20 @@ export const TEDEUM_CONFIG = {
},
descriptionValeur: {
1: { valeur: 1, qualite: "Mauvais", dice: "d4", negativeDice: "d20", savoir: "Sot", sensibilite: "Obtus", entregent: "Rustaud", puissance: "Menu", complexion: "Anémique", adresse: "Empesé" },
2: { valeur: 2, qualite: "Médiocre", dice: "d6", negativeDice: "d12", savoir: "Limité", sensibilite: "Etriqué", entregent: "Frustre", puissance: "Délicat", complexion: "Languide", adresse: "Gauche" },
2: { valeur: 2, qualite: "Médiocre", dice: "d6", negativeDice: "d12", savoir: "Limité", sensibilite: "Etriqué", entregent: "Fruste", puissance: "Délicat", complexion: "Languide", adresse: "Gauche" },
3: { valeur: 3, qualite: "Correct", dice: "d8", negativeDice: "d10", savoir: "Mêlé", sensibilite: "Ouvert", entregent: "Badin", puissance: "Membru", complexion: "Dispos", adresse: "Ingambe" },
4: { valeur: 4, qualite: "Bon", dice: "d10", negativeDice: "d8", savoir: "Lettré", sensibilite: "Fin", entregent: "Disert", puissance: "Vigoureux", complexion: "Gaillard", adresse: "Leste" },
5: { valeur: 5, qualite: "Excellent", dice: "d12", negativeDice: "d6", savoir: "Docte", sensibilite: "Subtil", entregent: "Galant", puissance: "Musculeux", complexion: "Sanguin", adresse: "Preste" },
6: { valeur: 6, qualite: "Admirable", dice: "d20", negativeDice: "d4", savoir: "Humaniste", sensibilite: "Spirituel", entregent: "Sémillant", puissance: "Hercule", complexion: "Aguerri", adresse: "Alerte" },
},
descriptionValeurFemme: {
1: { valeur: 1, qualite: "Mauvais", dice: "d4", negativeDice: "d20", savoir: "Sotte", sensibilite: "Obtuse", entregent: "Rustaude", puissance: "Menue", complexion: "Anémique", adresse: "Empesée" },
2: { valeur: 2, qualite: "Médiocre", dice: "d6", negativeDice: "d12", savoir: "Limitée", sensibilite: "Etriquée", entregent: "Fruste", puissance: "Délicate", complexion: "Languide", adresse: "Gauche" },
3: { valeur: 3, qualite: "Correct", dice: "d8", negativeDice: "d10", savoir: "Mêlée", sensibilite: "Ouverte", entregent: "Badine", puissance: "Membrue", complexion: "Dispose", adresse: "Ingambe" },
4: { valeur: 4, qualite: "Bon", dice: "d10", negativeDice: "d8", savoir: "Lettrée", sensibilite: "Fine", entregent: "Diserte", puissance: "Vigoureuse", complexion: "Gaillarde", adresse: "Leste" },
5: { valeur: 5, qualite: "Excellent", dice: "d12", negativeDice: "d6", savoir: "Docte", sensibilite: "Subtile", entregent: "Galante", puissance: "Musculeuse", complexion: "Sanguine", adresse: "Preste" },
6: { valeur: 6, qualite: "Admirable", dice: "d20", negativeDice: "d4", savoir: "Humaniste", sensibilite: "Spirituelle", entregent: "Sémillante", puissance: "Hercule", complexion: "Aguerrie", adresse: "Alerte" },
},
diceValeur: ["d4", "d6", "d8", "d10", "d12", "d20"],
degatsArmure: {
sansarmure: { label: "Sans armure" },
@@ -86,7 +110,7 @@ export const TEDEUM_CONFIG = {
},
caracteristiques: {
savoir: { id: "savoir", value: "savoir", label: "Savoir", description:"Cette caractéristique correspond à la capacité d'abstraction intellectuelle ainsi qu'à la culture générale du personnage. Elle permet d'évaluer la compétence de base Mémoriser." },
savoir: { id: "savoir", value: "savoir", label: "Savoir", description: "Cette caractéristique correspond à la capacité d'abstraction intellectuelle ainsi qu'à la culture générale du personnage. Elle permet d'évaluer la compétence de base Mémoriser." },
sensibilite: { id: "sensibilite", value: "sensibilite", label: "Sensibilité", description: "Cette caractéristique correspond à l'ouverture du personnage sur le monde. Elle englobe l'altruisme, la spiritualité et la créativité du personnage. Elle permet d'évaluer la compétence de base Perception." },
entregent: { id: "entregent", value: "entregent", label: "Entregent", description: "Cette caractéristique correspond à l'ensemble des prédispositions sociales du personnage. Elle englobe le charisme et le respect des usages. Elle permet d'évaluer la compétence de base Charme." },
complexion: { id: "complexion", value: "complexion", label: "Complexion", description: "Cette caractéristique permet d'évaluer la santé et la résistance physique du per- sonnage. Elle permet de calculer la com- pétence de base Endurance, capitale dans la résolution des blessures, la résistance à la douleur, au poison et aux maladies." },
@@ -94,10 +118,10 @@ export const TEDEUM_CONFIG = {
adresse: { id: "adresse", value: "adresse", label: "Adresse", description: "Cette caractéristique correspond à la rapidité et la dextérité du personnage. Elle livre le nombre d'actions qu'un personnage peut accomplir en un tour de combat et permet d'évaluer les compétences de base Initiative & Course." },
},
allonges: {
courte: { courte: { malus: 0 }, moyenne: { malus: -1 }, longue: { malus: -2 }, treslongue: { malus: 0, esquive: 2 } },
moyenne: { courte: { malus: 0 }, moyenne: { malus: 0 }, longue: { malus: -1 }, treslongue: { malus: 0, esquive: 2 } },
longue: { courte: { malus: -2 }, moyenne: { malus: -1 }, longue: { malus: 0 }, treslongue: { malus: -1, esquive: 1 } },
treslongue: { courte: { malus: 0, esquive: 2 }, moyenne: { malus: 0, esquive: 2 }, longue: { malus: 0, esquive: 1 }, treslongue: { malus: 0 } },
courte: { courte: { label: "Courte (0)", malus: 0 }, moyenne: { label: "Moyenne (-1)", malus: -1 }, longue: { label: "Longue (-2)", malus: -2 }, treslongue: { label: "Très longue (0, 2 Esquives)", malus: 0, esquive: 2 } },
moyenne: { courte: { label: "Courte (0)", malus: 0 }, moyenne: { label: "Moyenne (0)", malus: 0 }, longue: { label: "Longue (-1)", malus: -1 }, treslongue: { label: "Très longue (0, 2 Esquives)", malus: 0, esquive: 2 } },
longue: { courte: { label: "Courte (-2)", malus: -2 }, moyenne: { label: "Moyenne (-1)", malus: -1 }, longue: { label: "Longue (0)", malus: 0 }, treslongue: { label: "Très longue (-1, 2 Esquives)", malus: -1, esquive: 1 } },
treslongue: { courte: { label: "Courte (0, 2 Esquives)", malus: 0, esquive: 2 }, moyenne: { label: "Moyenne (0, 2 Esquives)", malus: 0, esquive: 2 }, longue: { label: "Longue (0, 1 Esquive)", malus: 0, esquive: 1 }, treslongue: { label: "Très longue (0)", malus: 0 } },
},
providence: [
{ labelM: "Brebis égarée", labelF: "Brebis égarée", value: 0, diceValue: "0" },
@@ -144,19 +168,24 @@ export const TEDEUM_CONFIG = {
},
difficulte: {
aucune: { label: "Aucune", key: "aucune", value: 0 },
routine: { label: "Routine", key: "routine", value: 3 },
facile: { label: "Facile", key: "facile", value: 5 },
pardefaut: { label: "Par Défaut", key: "pardefaut", value: 7 },
malaise: { label: "Malaisé", key: "malaise", value: 9 },
difficile: { label: "Difficile", key: "difficile", value: 11 },
perilleux: { label: "Perilleux", key: "perilleux", value: 13 },
desespere: { label: "Désespéré", key: "desespere", value: 15 }
routine: { label: "Routine (3)", key: "routine", value: 3 },
facile: { label: "Facile (5)", key: "facile", value: 5 },
pardefaut: { label: "Par Défaut (7)", key: "pardefaut", value: 7 },
malaise: { label: "Malaisé (9)", key: "malaise", value: 9 },
difficile: { label: "Difficile (11)", key: "difficile", value: 11 },
perilleux: { label: "Perilleux (13)", key: "perilleux", value: 13 },
desespere: { label: "Désespéré (15)", key: "desespere", value: 15 }
},
monnaie: {
denier: { label: "Deniers", id: "denier", value: 1 },
sol: { label: "Sols", id: "sol", value: 10 },
livre: { label: "Livres", id: "livre", value: 100 }
},
monnaieUnit: {
"1": { label: "Deniers", id: "denier", value: 1 },
"10": { label: "Sols", id: "sol", value: 10 },
"100": { label: "Livres", id: "livre", value: 100 }
},
etapesEducation: {
pouponniere: { label: "La Pouponnière", value: "pouponniere", agemin: 0, agemax: 6, nbCompetences: 2, nbCaracteristiques: 3, hasGenre: false, hasQuestionnaire: true, hasDebouches: false, hasMultiplier: false, canCompetencesOpt: false },
petitsgrimauds: { label: "La classe des Petits Grimauds", value: "petitsgrimauds", agemin: 7, agemax: 12, nbCompetences: 10, hasGenre: true, nbCaracteristiques: 3, hasDebouches: false, hasQuestionnaire: true, hasMultiplier: false, canCompetencesOpt: false },
@@ -182,13 +211,14 @@ export const TEDEUM_CONFIG = {
{ value: "1", label: "+1 niveau" },
{ value: "2", label: "+2 niveaux" }
],
blessuresOrder: ["indemne", "estafilade", "plaie", "plaiebeante", "plaieatroce", "tuenet", "tuenet", "tuenet", "tuenet", "tuenet"],
blessures: {
indemne: { value: 0, label: "Indemne", key: "indemne", degatsMax: -1, count: 0, modifier: 0 },
estafilade: { value: 1, label: "Estafilade", key: "estafilade", degatsMin: 0, degatsMax: 2, count: 1, modifier: 0 },
plaie: { value: 2, label: "Plaie", key: "plaie", degatsMin: 3, degatsMax: 4, count: 1, modifier: -1 },
plaiebeante: { value: 3, label: "Plaie béante", key: "plaiebeante", degatsMin: 5, degatsMax: 6, count: 1, modifier: -2 },
plaieatroce: { value: 4, label: "Plaie atroce", key: "plaieatroce", degatsMin: 7, degatsMax: 8, count: 1, horsCombat: true, modifier: -12 },
tunenet: { value: 5, label: "Tué net", key: "tuenet", degatsMin: 9, degatsMax: 100, count: 1, horsCombat: true, mort: true, modifier: -12 }
tuenet: { value: 5, label: "Tué net", key: "tuenet", degatsMin: 9, degatsMax: 100, count: 1, horsCombat: true, mort: true, modifier: -100 }
},
virulence: {
aucune: { label: "Aucune", value: "aucune", modifier: 0 },

View File

@@ -1,6 +1,8 @@
/* -------------------------------------------- */
/* -------------------------------------------- */
const ECRYME_WELCOME_MESSAGE_URL = "https://www.uberwald.me/gitea/public/fvtt-te-deum/raw/branch/main/welcome-message-tedeum.html"
export class TeDeumUtility {
/* -------------------------------------------- */
@@ -12,22 +14,68 @@ export class TeDeumUtility {
CONFIG.JournalEntry.compendiumBanner = "systems/fvtt-te-deum/images/ui/compendium_banner.webp"
CONFIG.Macro.compendiumBanner = "systems/fvtt-te-deum/images/ui/compendium_banner.webp"
CONFIG.Adventure.compendiumBanner = "systems/fvtt-te-deum/images/ui/compendium_banner.webp"
}
Hooks.on('renderChatLog', (log, html, data) => TeDeumUtility.chatListeners(html));
static installHooks() {
Hooks.on('renderChatMessageHTML', (message, html) => {
TeDeumUtility.chatListeners(html);
TeDeumUtility.onRenderChatMessage(message, html);
});
Hooks.on("renderActorDirectory", (app, html, data) => {
if (game.user.can('ACTOR_CREATE')) {
const button = document.createElement('button');
button.style.width = '90%';
button.style.width = '60%';
button.classList.add('tedeum-create-character');
button.innerHTML = 'Créer un Personnage'
button.addEventListener('click', () => {
let cr = new game.system.tedeum.TeDeumCharacterCreator();
cr.init()
})
html.find('.header-actions').after(button)
$(html).find('.header-actions').after(button)
}
})
//Hooks.on("getChatLogEntryContext", (html, options) => TeDeumUtility.chatMenuManager(html, options));
Hooks.on("combatStart", async (combat, updateData, options) => {
this.resetCombatActions(combat)
});
Hooks.on("combatRound", (combat, updateData, updateOptions) => {
// List all actors related to combatant
if (game.user.isGM) {
this.resetCombatActions(combat)
}
})
Hooks.on("getCombatTrackerContextOptions", (html, options) => {
console.log("Get Combat Tracker Context", html, options)
this.pushCombatOptions(html, options);
});
}
/* -------------------------------------------- */
static pushCombatOptions(html, options) {
options.push({ name: "Actions +1", condition: true, icon: '<i class="fas fa-plus"></i>', callback: target => { game.combat.modifyAction($(target).data('combatant-id'), 1); } })
options.push({ name: "Actions -1", condition: true, icon: '<i class="fas fa-minus"></i>', callback: target => { game.combat.modifyAction($(target).data('combatant-id'), -1); } })
options.push({ name: "Actions MG +1", condition: true, icon: '<i class="fas fa-plus"></i>', callback: target => { game.combat.modifyAction($(target).data('combatant-id'), 1, true); } })
options.push({ name: "Actions MG -1", condition: true, icon: '<i class="fas fa-minus"></i>', callback: target => { game.combat.modifyAction($(target).data('combatant-id'), -1, true); } })
}
/* -------------------------------------------- */
static async resetCombatActions(combat) {
if (game.user.isGM) {
for (let c of combat.combatants) {
let actor = game.actors.get(c.actorId)
if (actor) {
let nbActions = actor.getNbActions()?.value || 0
let isMainGauche = (actor.getCompetenceScore("Main gauche") > 0)
let nbActionsMainGauche = isMainGauche ? nbActions : 0
await c.setFlag("world", "available-actions", { nbActions, nbActionsMainGauche })
await c.update({ name: `${c.token.name} (${nbActions} / ${nbActionsMainGauche})` })
}
}
}
}
/* -------------------------------------------- */
@@ -92,6 +140,21 @@ export class TeDeumUtility {
Handlebars.registerHelper('isGM', function () {
return game.user.isGM
})
Handlebars.registerHelper('monnaie', function (value) {
let monnaie = game.system.tedeum.config.monnaieUnit[String(value)]
if (monnaie) {
return monnaie.label
}
return value
})
// 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");
});
// Load compendium data
const competences = await TeDeumUtility.loadCompendium("fvtt-te-deum.competences")
@@ -119,14 +182,24 @@ export class TeDeumUtility {
/* -------------------------------------------- */
static welcomeMessage() {
if (game.user.isGM) {
// Try to fetch the welcome message from the github repo "welcome-message-ecryme.html"
fetch(ECRYME_WELCOME_MESSAGE_URL)
.then(response => response.text())
.then(html => {
ChatMessage.create({
user: game.user.id,
whisper: [game.user.id],
content: `<div id="chat-welcome welcome-message-tedeum"><span class="rdd-roll-part">
<strong>Bienvenu dans Te Deum Pour Un Massacre !</strong>
<div class="chat-welcome">Ce système vous est proposé par Open Sesame Games.<br>
Vous trouverez de l'aide dans @UUID[Compendium.fvtt-te-deum.aides.JournalEntry.uNwJgi4kXBCiZmAH]{Aide pour Te Deum}<br>
ainsi que sur le Discord de Foundry FR : https://discord.gg/pPSDNJk</div>` });
content: html
});
})
.catch(error => {
console.error("Error fetching welcome message:", error);
ChatMessage.create({
user: game.user.id,
whisper: [game.user.id],
content: "<b>Bienvenue dans Ecryme RPG !</b><br>Visitez le site officiel pour plus d'informations."
});
});
}
}
@@ -193,7 +266,7 @@ export class TeDeumUtility {
return actor
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/* -------------------------------------------- */
static async manageOpposition(rollData) {
if (!this.currentOpposition) {
// Store rollData as current GM opposition
@@ -202,39 +275,76 @@ export class TeDeumUtility {
} else {
// Perform the opposition
let isAttackWinner = true
let rWinner = this.currentOpposition
let rLooser = rollData
if (rWinner.total < rLooser.total) {
rWinner = rollData
rLooser = this.currentOpposition
let rWinner, rLooser
if (this.currentOpposition.total <= rollData.total) {
rWinner = foundry.utils.duplicate(rollData)
rLooser = foundry.utils.duplicate(this.currentOpposition)
isAttackWinner = false
} else {
rWinner = foundry.utils.duplicate(this.currentOpposition)
rLooser = foundry.utils.duplicate(rollData)
isAttackWinner = true
}
this.currentOpposition = undefined // Reset opposition
let oppositionData = {
winner: rWinner,
looser: rLooser
}
// Update difficulty
rWinner.difficulty = rLooser.total
rLooser.difficulty = rWinner.total
await this.computeResults(rWinner)
await this.computeResults(rLooser)
// Auto XP management when opposed
if (rWinner.isReussiteCritique) {
let actor = this.getActorFromRollData(rWinner)
actor.modifyXP(rWinner.carac.key, 1)
}
if (rLooser.isEchecCritique) {
let actor = this.getActorFromRollData(rLooser)
actor.modifyXP(rLooser.carac.key, 1)
}
let msg = await this.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-te-deum/templates/chat/chat-opposition-result.hbs`, oppositionData)
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-te-deum/templates/chat/chat-opposition-result.hbs`, oppositionData)
})
await msg.setFlag("world", "te-deum-rolldata", rollData)
// Si le gagnant est l'attaquant, appliquer les dégats sur la victime
if ( isAttackWinner && rWinner.isSuccess && rWinner.mode == "arme" && rWinner.arme?.system.typeArme == "melee" && rWinner.defenderTokenId) {
this.appliquerDegats(rWinner)
if (isAttackWinner && rWinner.isSuccess && rWinner.mode == "arme" && rWinner.arme?.system.typeArme == "melee" && rWinner.defenderTokenId) {
await this.appliquerDegats(rWinner)
}
console.log("Rolldata result", rollData)
console.log("Opposition result", rollData, isAttackWinner, oppositionData)
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/* -------------------------------------------- */
static getTokenActorFromId(tokenId) {
for (let scene of game.scenes) {
const tokenDoc = scene.tokens.get(tokenId)
if (tokenDoc) return tokenDoc.actor
}
return null
}
/* -------------------------------------------- */
static async appliquerDegats(rollData) {
await this.processAttaqueMelee(rollData)
let defenderToken = canvas.tokens.placeables.find(t => t.id == rollData.defenderTokenId)
if (defenderToken) {
let actor = defenderToken.actor
await actor.appliquerDegats(rollData)
let defenderActor = this.getTokenActorFromId(rollData.defenderTokenId)
if (defenderActor) {
if (game.user.isGM || defenderActor.isOwner) {
await defenderActor.appliquerDegats(rollData)
} else {
// Send a socket message — seul le premier MJ actif le traitera
game.socket.emit("system.fvtt-te-deum", { name: "msg_apply_damage", data: { rollData } });
}
// Attaque naturelle avec dégats inférieur à -2
if ((rollData?.arme?.system.specificites?.poing?.hasSpec || rollData?.arme?.system.specificites?.pied?.hasSpec) && rollData.degats < -2) {
let attacker = this.getActorFromRollData(rollData)
attacker.appliquerBlessure("estafilade", "maindroite", "Contusion suite à une attaque naturelle")
ui.notifications.info(`${attacker.name} subit 1 contusion en infligeant ${rollData.degats} dégâts à mains nues`)
}
} else {
ui.notifications.error("Impossible de trouver la cible de l'attaque, aucun degats appliqué")
}
@@ -243,7 +353,7 @@ export class TeDeumUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async chatListeners(html) {
html.on("click", '.chat-command-opposition', event => {
$(html).on("click", '.chat-command-opposition', event => {
let messageId = TeDeumUtility.findChatMessageId(event.currentTarget)
let message = game.messages.get(messageId)
let rollData = message.getFlag("world", "te-deum-rolldata")
@@ -251,7 +361,7 @@ export class TeDeumUtility {
TeDeumUtility.manageOpposition(rollData, messageId)
}
})
html.on("click", '.chat-command-appliquer-degats', event => {
$(html).on("click", '.chat-command-appliquer-degats', event => {
let messageId = TeDeumUtility.findChatMessageId(event.currentTarget)
let message = game.messages.get(messageId)
let rollData = message.getFlag("world", "te-deum-rolldata")
@@ -259,6 +369,25 @@ export class TeDeumUtility {
TeDeumUtility.appliquerDegats(rollData, messageId)
}
})
$(html).on("click", '.chat-command-gain-xp', async event => {
let messageId = TeDeumUtility.findChatMessageId(event.currentTarget)
let message = game.messages.get(messageId)
let rollData = message.getFlag("world", "te-deum-rolldata")
if (rollData) {
let actor = TeDeumUtility.getActorFromRollData(rollData)
actor.modifyXP(rollData.carac.key, 1)
event.currentTarget.style.display = 'none'; // feedback immédiat local
await message.setFlag("world", "te-deum-xp-used", true) // sync tous les clients
}
})
}
/* -------------------------------------------- */
static onRenderChatMessage(message, html) {
if (message.getFlag("world", "te-deum-xp-used")) {
const btn = html.querySelector('.chat-command-gain-xp');
if (btn) btn.style.display = 'none';
}
}
/* -------------------------------------------- */
@@ -268,9 +397,19 @@ export class TeDeumUtility {
'systems/fvtt-te-deum/templates/actors/editor-notes-gm.hbs',
'systems/fvtt-te-deum/templates/items/partial-item-nav.hbs',
'systems/fvtt-te-deum/templates/items/partial-item-description.hbs',
'systems/fvtt-te-deum/templates/dialogs/partial-creator-status.hbs'
'systems/fvtt-te-deum/templates/dialogs/partial-creator-status.hbs',
'systems/fvtt-te-deum/templates/items/item-arme-sheet.hbs',
'systems/fvtt-te-deum/templates/items/item-armure-sheet.hbs',
'systems/fvtt-te-deum/templates/items/item-blessure-sheet.hbs',
'systems/fvtt-te-deum/templates/items/item-competence-sheet.hbs',
'systems/fvtt-te-deum/templates/items/item-education-sheet.hbs',
'systems/fvtt-te-deum/templates/items/item-equipement-sheet.hbs',
'systems/fvtt-te-deum/templates/items/item-grace-sheet.hbs',
'systems/fvtt-te-deum/templates/items/item-maladie-sheet.hbs',
'systems/fvtt-te-deum/templates/items/item-origine-sheet.hbs',
'systems/fvtt-te-deum/templates/items/item-simple-sheet.hbs',
]
return loadTemplates(templatePaths);
return foundry.applications.handlebars.loadTemplates(templatePaths);
}
/* -------------------------------------------- */
@@ -347,12 +486,34 @@ export class TeDeumUtility {
let rollData = msg.data.rollData
if (game.user.isGM) {
let chatMsg = await this.createChatMessage(rollData.alias, "blindroll", {
content: await renderTemplate(msg.data.template, rollData),
content: await foundry.applications.handlebars.renderTemplate(msg.data.template, rollData),
whisper: game.user.id
})
chatMsg.setFlag("world", "tedeum-rolldata", rollData)
}
}
if (msg.name == "msg_modify_combat_action") {
if (game.user.isGM) {
let { combatantId, ca } = msg.data
let combatant = game.combat.combatants.get(combatantId)
if (combatant) {
console.log("sock - Modify Combat Action : ", combatant.name, ca)
await TeDeumUtility.updateCombatantActions(combatant, ca)
}
}
}
if (msg.name == "msg_apply_damage") {
const firstGM = game.users.find(u => u.isGM && u.active)
if (game.user === firstGM) {
let rollData = msg.data.rollData
let defenderActor = TeDeumUtility.getTokenActorFromId(rollData.defenderTokenId)
if (defenderActor) {
await defenderActor.appliquerDegats(rollData)
} else {
ui.notifications.error("Impossible de trouver la cible de l'attaque, aucun degats appliqué")
}
}
}
}
/* -------------------------------------------- */
@@ -438,9 +599,11 @@ export class TeDeumUtility {
}
if (rollData.diceSum == 1) {
let critiqueRoll = await new Roll(rollData.carac.negativeDice)
rollData.isSuccess = false
await critiqueRoll.evaluate()
await this.showDiceSoNice(critiqueRoll, game.settings.get("core", "rollMode"))
rollData.critiqueRoll = foundry.utils.duplicate(critiqueRoll)
rollData.critiqueTotal = critiqueRoll.total
if (critiqueRoll.total > rollData.competence.system.score) {
rollData.isEchecCritique = true
}
@@ -469,9 +632,18 @@ export class TeDeumUtility {
if (rollData.isMouvement) {
localModifier -= 1
}
if (rollData.arme && rollData.allongeId) {
localModifier += rollData.allonges[rollData.allongeId].malus
rollData.allongeMalus = rollData.allonges[rollData.allongeId].malus
rollData.nbEsquives = rollData.allonges[rollData.allongeId]?.esquive || 0
}
if (rollData.attaqueCiblee && rollData.attaqueCiblee != "aucune") {
localModifier -= 1
rollData.loc = foundry.utils.duplicate(game.system.tedeum.config.LOCALISATION[rollData.attaqueCiblee])
}
let diceBase = this.modifyDice(rollData.carac.dice, localModifier + Number(rollData.bonusMalus) + rollData.santeModifier)
if (!diceBase) return;
diceFormula = diceBase + "x + " + rollData.competence.system.score
diceFormula = diceBase + "x + " + rollData.compScore
}
if (rollData.enableProvidence) {
diceFormula += " + " + rollData.providence.dice
@@ -481,9 +653,12 @@ export class TeDeumUtility {
/* -------------------------------------------- */
static async getLocalisation(rollData) {
let locRoll = await new Roll("1d20").roll()
let locRoll
if (rollData.loc) {
locRoll = await new Roll(String(rollData.loc.score.min)).roll()
} else {
locRoll = await new Roll("1d20").roll()
await this.showDiceSoNice(locRoll, game.settings.get("core", "rollMode"))
rollData.locRoll = foundry.utils.duplicate(locRoll)
for (let key in game.system.tedeum.config.LOCALISATION) {
let loc = game.system.tedeum.config.LOCALISATION[key]
if (locRoll.total >= loc.score.min && locRoll.total <= loc.score.max) {
@@ -492,13 +667,16 @@ export class TeDeumUtility {
}
}
}
rollData.locRoll = foundry.utils.duplicate(locRoll)
}
/* -------------------------------------------- */
static async processAttaqueMelee(rollData) {
await this.getLocalisation(rollData)
let actor = game.actors.get(rollData.actorId)
let bDegats = actor.getBonusDegats()
let degatsRoll = await new Roll(rollData.arme.system.degats + "+" + bDegats.value).roll()
let bDegats = actor.getAttaqueBonusDegats(rollData)
rollData.degatsFormula = rollData.arme.system.degats + "+" + bDegats
let degatsRoll = await new Roll(rollData.degatsFormula).roll()
await this.showDiceSoNice(degatsRoll, game.settings.get("core", "rollMode"))
rollData.degatsRoll = foundry.utils.duplicate(degatsRoll)
rollData.degats = degatsRoll.total
@@ -514,12 +692,51 @@ export class TeDeumUtility {
await this.getLocalisation(rollData)
// Now the degats
let degatsRoll = await new Roll(rollData.arme.system.degats).roll()
await this.showDiceSoNice(rollData.locRoll, game.settings.get("core", "rollMode"))
await this.showDiceSoNice(degatsRoll, game.settings.get("core", "rollMode"))
rollData.degatsRoll = foundry.utils.duplicate(degatsRoll)
rollData.degats = degatsRoll.total
}
}
/* -------------------------------------------- */
static async updateCombatantActions(combatant, ca) {
await combatant.setFlag("world", "available-actions", ca)
await combatant.update({ name: `${combatant.token.name} (${ca.nbActions} / ${ca.nbActionsMainGauche})` })
}
/* -------------------------------------------- */
static async manageCombatActions(actor, rollData) {
let combat = game.combats.active
if (!combat) return;
let combatant = combat.getCombatantByActor(actor)
if (!combatant) return;
let ca = combatant.getFlag("world", "available-actions")
if (!ca) return;
if (rollData.mode == "arme" && rollData.isMainGauche) {
if (ca.nbActionsMainGauche > 0) {
ca.nbActionsMainGauche -= 1
ca.nbActions = Math.max(ca.nbActions - 1, 0)
} else {
ui.notifications.error(`${actor.name} n'a plus d'actions disponibles à la main gauche pour ce round`)
}
}
if (ca.nbActions > 0) {
ca.nbActions -= 1
} else {
ui.notifications.error(`${actor.name} n'a plus d'actions disponibles pour ce round`)
}
console.log("Manage combat actions 1", actor.name, combatant)
if (game.user.isGM) {
await this.updateCombatantActions(combatant, ca)
} else {
// Send a socket message
game.socket.emit("system.fvtt-te-deum", { name: "msg_modify_combat_action", data: { combatantId: combatant.id, ca } });
}
rollData.hasActions = true
rollData.remainingActions = ca.nbActions
rollData.remainingActionsMainGauche = ca.nbActionsMainGauche
}
/* -------------------------------------------- */
static async rollTeDeum(rollData) {
@@ -529,6 +746,18 @@ export class TeDeumUtility {
rollData.difficulty = "pardefaut"
}
rollData.difficulty = game.system.tedeum.config.difficulte[rollData.difficulty].value
// Compute the real competence score
if (rollData.competence) {
if (rollData.isMainGauche) {
rollData.competence = actor.getMeilleureCompetenceMainGauche(rollData.competence)
}
if (rollData.competence.system.isBase) {
rollData.compScore = actor.system.caracteristiques[rollData.competence.system.caracteristique].value
} else {
rollData.compScore = rollData.competence.system.score
}
}
let diceFormula = this.computeRollFormula(rollData, actor)
if (!diceFormula) return;
console.log("RollData", rollData, diceFormula)
@@ -545,8 +774,10 @@ export class TeDeumUtility {
await this.processAttaqueDistance(rollData)
await this.manageCombatActions(actor, rollData)
let msg = await this.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-te-deum/templates/chat/chat-generic-result.hbs`, rollData)
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-te-deum/templates/chat/chat-generic-result.hbs`, rollData)
})
await msg.setFlag("world", "te-deum-rolldata", rollData)
console.log("Rolldata result", rollData)
@@ -555,10 +786,6 @@ export class TeDeumUtility {
if (rollData.enableProvidence) {
actor.modifyProvidence(-1)
}
// Manage XP
if (rollData.isReussiteCritique || rollData.isEchecCritique) {
actor.modifyXP(rollData.carac.key, 1)
}
}
/* -------------------------------------------- */
@@ -676,7 +903,7 @@ export class TeDeumUtility {
/* -------------------------------------------- */
static async confirmDelete(actorSheet, li) {
let itemId = li.data("item-id");
let itemId = li.dataset ? li.dataset.itemId : li.data("item-id");
let msgTxt = "<p>Etes vous certain de supprimer cet item ?";
let buttons = {
delete: {
@@ -684,7 +911,12 @@ export class TeDeumUtility {
label: "Oui, aucun souci",
callback: () => {
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
if (li.slideUp) {
li.slideUp(200, () => actorSheet.render(false));
} else {
li.style.display = "none";
actorSheet.render(false);
}
}
},
cancel: {

View File

@@ -5,13 +5,13 @@ export class TeDeumArmeSchema extends foundry.abstract.TypeDataModel {
const requiredDouble = { required: true, nullable: false, integer: false };
const schema = {};
schema.typeArme = new fields.StringField({required: true, choices: ["melee", "tir"], initial: "melee"});
schema.allonge = new fields.StringField({required: true, choices: ["courte", "moyenne", "longue", "treslongue"], initial: "courte"});
schema.typeArme = new fields.StringField({ required: true, choices: ["melee", "tir"], initial: "melee" });
schema.allonge = new fields.StringField({ required: true, choices: ["courte", "moyenne", "longue", "treslongue"], initial: "courte" });
schema.specificites = new fields.SchemaField(
Object.values((game.system.tedeum.config.ARME_SPECIFICITE)).reduce((obj, spec) => {
obj[spec.id] = new fields.SchemaField({
hasSpec: new fields.BooleanField({initial: false}),
hasSpec: new fields.BooleanField({ initial: false }),
});
return obj;
}, {})
@@ -26,18 +26,18 @@ export class TeDeumArmeSchema extends foundry.abstract.TypeDataModel {
}, {})
);
schema.degatsArmure = new fields.SchemaField( {
sansarmure : new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
cuir : new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
plates : new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
mailles : new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
schema.degatsArmure = new fields.SchemaField({
sansarmure: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
cuir: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
plates: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
mailles: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
});
schema.tempsRecharge = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 });
schema.competenceRecharge = new fields.StringField({ required: false, choices:["aucune", "archerie", "arquebusade"], initial: "aucune", blank: true });
schema.competenceRecharge = new fields.StringField({ required: false, choices: ["aucune", "archerie", "arquebusade"], initial: "aucune", blank: true });
schema.valeurEchecCritique = new fields.NumberField({ ...requiredInteger, initial: 1, min: 1 });
schema.initiativeBonus = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 });
schema.initiativeBonus = new fields.NumberField({ ...requiredInteger, initial: 0 });
schema.degats = new fields.StringField({ required: false, blank: true, initial: "0" });
schema.degatscrosse = new fields.StringField({ required: false, blank: true, initial: "0" });
@@ -46,13 +46,13 @@ export class TeDeumArmeSchema extends foundry.abstract.TypeDataModel {
for (let key of Object.keys(game.system.tedeum.config.armeCompetences)) {
comp.push(key);
}
schema.competence = new fields.StringField({ required: true, choices:comp, initial: "bagarre" });
schema.competence2 = new fields.StringField({ required: false, choices:comp, initial: "", blank: true });
schema.competence = new fields.StringField({ required: true, choices: comp, initial: "bagarre" });
schema.competence2 = new fields.StringField({ required: false, choices: comp, initial: "", blank: true });
schema.prix = new fields.NumberField({ ...requiredDouble, initial: 0, min: 0 });
schema.monnaie = new fields.StringField({ required: true, blank: false, initial: "denier" });
schema.equipe = new fields.BooleanField({initial: false}),
schema.equipe = new fields.BooleanField({ initial: false }),
schema.description = new fields.HTMLField({ required: true, blank: true });

View File

@@ -4,8 +4,9 @@ export class TeDeumBlessureSchema extends foundry.abstract.TypeDataModel {
const requiredInteger = { required: true, nullable: false, integer: true };
const schema = {};
schema.typeBlessure = new fields.StringField({required: true, choices: ["indemne", "estafilade", "plaie", "plaiebeante", "plaieatroce", "tuenet"], initial: "estafilade"});
schema.localisation = new fields.StringField({required: true, choices: ["piedgauche", "pieddroit", "jambegauche", "jambedroite", "maingauche", "maindroite", "brasgauche", "brasdroit", "tete", "corps"], initial: "corps"});
schema.typeBlessure = new fields.StringField({ required: true, choices: ["indemne", "estafilade", "plaie", "plaiebeante", "plaieatroce", "tuenet"], initial: "estafilade" });
schema.value = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.localisation = new fields.StringField({ required: true, choices: ["piedgauche", "pieddroit", "jambegauche", "jambedroite", "maingauche", "maindroite", "brasgauche", "brasdroit", "tete", "corps"], initial: "corps" });
schema.description = new fields.HTMLField({ required: true, blank: true });

View File

@@ -51,7 +51,7 @@ export class TeDeumEducationSchema extends foundry.abstract.TypeDataModel {
reponse: new fields.StringField({ required: true, blank: true, initial: "" }),
compName: new fields.StringField({ required: true, blank: true, initial: "" }),
toSelect: new fields.BooleanField({ initial: false }),
compList: new fields.SchemaField(Array.fromRange(10, 1).reduce((comps, i) => {
compList: new fields.SchemaField(Array.fromRange(16, 1).reduce((comps, i) => {
comps[`comp${i}`] = new fields.SchemaField({
compName: new fields.StringField({ required: true, blank: true, initial: "" }),
});

View File

@@ -13,4 +13,10 @@ export class TeDeumEquipementSchema extends foundry.abstract.TypeDataModel {
return schema;
}
get monnaieLabel() {
console.log("monnaieLabel", this.monnaie,game.system.tedeum.config.monnaieUnit)
return game.system.tedeum.config.monnaieUnit[String(this.monnaie)]?.label;
}
}

View File

@@ -9,7 +9,8 @@ export class TeDeumMaladieSchema extends foundry.abstract.TypeDataModel {
schema.virulence = new fields.StringField({required: true, choices: ["fatigue", "epuisement", "souffrance", "agonie"], initial: "fatigue"});
schema.fievre = new fields.StringField({required: true, choices: ["aucune", "legere", "forte", "grave"], initial: "aucune"});
schema.symptomes = new fields.HTMLField({ required: true, blank: true });
schema.appliquee = new fields.BooleanField({initial: false}),
schema.complications = new fields.HTMLField({ required: true, blank: true });
schema.appliquee = new fields.BooleanField({initial: false});
schema.description = new fields.HTMLField({ required: true, blank: true });

View File

@@ -40,18 +40,20 @@ export class TeDeumPJSchema extends foundry.abstract.TypeDataModel {
});
schema.description = new fields.HTMLField({required: true, blank: true});
schema.notes = new fields.HTMLField({required: true, blank: true});
schema.connaissances = new fields.HTMLField({required: true, blank: true});
schema.histoire = new fields.HTMLField({required: true, blank: true});
schema.vetements = new fields.HTMLField({required: true, blank: true});
schema.equipmentfree = new fields.HTMLField({required: true, blank: true});
schema.genre = new fields.StringField({required: true, choices: game.system.tedeum.config.genre, initial: "Femme"});
schema.age = new fields.StringField({ required: false, blank: true, initial: undefined });
schema.statutocial = new fields.StringField({ required: false, blank: true, initial: undefined });
schema.chargestitre = new fields.StringField({ required: false, blank: true, initial: undefined });
schema.charges = new fields.StringField({ required: false, blank: true, initial: undefined });
schema.religion = new fields.StringField({ required: false, blank: true, initial: undefined });
schema.lieunaissance = new fields.StringField({ required: false, blank: true, initial: undefined });
schema.age = new fields.StringField({ required: false, blank: true, initial: "" });
schema.datenaissance = new fields.StringField({ required: false, blank: true, initial: "" });
schema.statutocial = new fields.StringField({ required: false, blank: true, initial: "" });
schema.chargestitre = new fields.StringField({ required: false, blank: true, initial: "" });
schema.charges = new fields.StringField({ required: false, blank: true, initial: "" });
schema.religion = new fields.StringField({ required: false, blank: true, initial: "" });
schema.lieunaissance = new fields.StringField({ required: false, blank: true, initial: "" });
return schema;
}

View File

@@ -1,87 +1,103 @@
import { TeDeumUtility } from "../common/tedeum-utility.js";
export class TeDeumRollDialog extends Dialog {
const { HandlebarsApplicationMixin } = foundry.applications.api
/* -------------------------------------------- */
export class TeDeumRollDialog extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {
static DEFAULT_OPTIONS = {
classes: ["fvtt-te-deum", "te-deum-roll-dialog"],
window: { title: "Lancer !", resizable: false },
position: { width: 540 },
actions: {
roll: TeDeumRollDialog.#onRoll,
cancel: TeDeumRollDialog.#onCancel,
}
}
static PARTS = {
content: { template: "systems/fvtt-te-deum/templates/dialogs/roll-dialog-generic.hbs" }
}
/* -------------------------------------------- */
constructor(actor, rollData, options = {}) {
super(options)
this.actor = actor
this.rollData = rollData
}
/* -------------------------------------------- */
static async create(actor, rollData) {
let options = { classes: ["tedeum-roll-dialog"], width: 540, height: 'fit-content', 'z-index': 99999 }
let html = await renderTemplate('systems/fvtt-te-deum/templates/dialogs/roll-dialog-generic.hbs', rollData);
return new TeDeumRollDialog(actor, rollData, html, options);
const dialog = new TeDeumRollDialog(actor, rollData)
dialog.render(true)
return dialog
}
/* -------------------------------------------- */
constructor(actor, rollData, html, options, close = undefined) {
let conf = {
title: "Lancer !",
content: html,
buttons: {
roll: {
icon: '<i class="fas fa-check"></i>',
label: "Lancer",
callback: () => { this.roll() }
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Annuler",
callback: () => { this.close() }
}
},
close: close
}
super(conf, options);
this.actor = actor;
this.rollData = rollData;
}
/* -------------------------------------------- */
roll() {
TeDeumUtility.rollTeDeum(this.rollData)
async _prepareContext() {
return { ...this.rollData }
}
/* -------------------------------------------- */
async refreshDialog() {
const content = await renderTemplate("systems/fvtt-te-deum/templates/dialogs/roll-dialog-generic.hbs", this.rollData)
this.data.content = content
this.render(true)
this.render()
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
let dialog = this;
function onLoad() {
static #onRoll(event, target) {
TeDeumUtility.rollTeDeum(this.rollData)
this.close()
}
$(function () { onLoad(); });
html.find('#bonusMalusPerso').change((event) => {
/* -------------------------------------------- */
static #onCancel(event, target) {
this.close()
}
/* -------------------------------------------- */
_onRender(context, options) {
super._onRender(context, options)
const html = this.element
html.querySelector('#bonusMalusPerso')?.addEventListener('change', (event) => {
this.rollData.bonusMalusPerso = Number(event.currentTarget.value)
})
html.find('#roll-difficulty').change((event) => {
html.querySelector('#roll-allonge')?.addEventListener('change', (event) => {
this.rollData.allongeId = event.currentTarget.value
})
html.querySelector('#roll-main-gauche')?.addEventListener('change', (event) => {
this.rollData.isMainGauche = event.currentTarget.checked
})
html.querySelector('#roll-difficulty')?.addEventListener('change', (event) => {
this.rollData.difficulty = String(event.currentTarget.value) || "pardefaut"
})
html.find('#roll-bonus-malus').change((event) => {
html.querySelector('#roll-attaque-ciblee')?.addEventListener('change', (event) => {
this.rollData.attaqueCiblee = event.currentTarget.value || "0"
})
html.querySelector('#roll-bonus-malus')?.addEventListener('change', (event) => {
this.rollData.bonusMalus = event.currentTarget.value || "0"
})
html.find('#roll-enable-providence').change((event) => {
html.querySelector('#roll-enable-providence')?.addEventListener('change', (event) => {
this.rollData.enableProvidence = event.currentTarget.checked
})
html.find('#roll-portee-tir').change((event) => {
html.querySelector('#roll-portee-tir')?.addEventListener('change', (event) => {
this.rollData.porteeTir = event.currentTarget.value
this.rollData.difficulty = game.system.tedeum.config.ARME_PORTEES[this.rollData.porteeTir].difficulty
this.rollData.porteeLabel = game.system.tedeum.config.ARME_PORTEES[this.rollData.porteeTir].label
this.refreshDialog()
})
html.find('#roll-tir-viser').change((event) => {
html.querySelector('#roll-tir-viser')?.addEventListener('change', (event) => {
this.rollData.isViser = event.currentTarget.checked
})
html.find('#roll-tir-mouvement').change((event) => {
html.querySelector('#roll-tir-mouvement')?.addEventListener('change', (event) => {
this.rollData.isMouvement = event.currentTarget.checked
})
html.querySelector('#roll-charge-a-pied')?.addEventListener('change', (event) => {
this.rollData.isChargeAPied = event.currentTarget.checked
})
html.querySelector('#roll-charge-a-cheval')?.addEventListener('change', (event) => {
this.rollData.isChargeACheval = event.currentTarget.checked
})
}
}

View File

@@ -1,173 +1,154 @@
import { TeDeumUtility } from "../common/tedeum-utility.js";
const { HandlebarsApplicationMixin } = foundry.applications.api
/**
* Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet}
* Feuille d'item Te Deum - AppV2
*/
export class TeDeumItemSheet extends ItemSheet {
export class TeDeumItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
/** @override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
static DEFAULT_OPTIONS = {
classes: ["fvtt-te-deum", "sheet", "item"],
template: "systems/fvtt-te-deum/templates/item-sheet.hbs",
dragDrop: [{ dragSelector: null, dropSelector: null }],
position: {
width: 620,
height: 580,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }]
});
},
form: {
submitOnChange: true,
closeOnSubmit: false,
},
window: {
resizable: true,
},
actions: {
editImage: TeDeumItemSheet.#onEditImage,
postItem: TeDeumItemSheet.#onPostItem,
deleteSubitem: TeDeumItemSheet.#onDeleteSubitem,
viewSubitem: TeDeumItemSheet.#onViewSubitem,
},
}
/* -------------------------------------------- */
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
// Add "Post to chat" button
// We previously restricted this to GM and editable items only. If you ever find this comment because it broke something: eh, sorry!
buttons.unshift(
{
class: "post",
icon: "fas fa-comment",
onclick: ev => { }
})
return buttons
// Static PARTS pointing to the dynamic wrapper template
static PARTS = {
sheet: { template: "systems/fvtt-te-deum/templates/items/item-sheet.hbs" },
}
/* -------------------------------------------- */
async getData() {
tabGroups = { primary: "description" }
let formData = {
/* -------------------------------------------- */
/** @override */
async _prepareContext() {
const item = this.document
const TextEditor = foundry.applications.ux.TextEditor.implementation
const enrich = async (val) => val !== undefined ? await TextEditor.enrichHTML(val ?? "", { async: true }) : ""
const context = {
title: this.title,
id: this.id,
type: this.object.type,
img: this.object.img,
name: this.object.name,
id: item.id,
type: item.type,
img: item.img,
name: item.name,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
system: foundry.utils.duplicate(this.object.system),
system: foundry.utils.duplicate(item.system),
systemFields: item.system.schema.fields,
config: foundry.utils.duplicate(game.system.tedeum.config),
competences: TeDeumUtility.getCompetencesForDropDown(),
limited: this.object.limited,
options: this.options,
owner: this.document.isOwner,
description: await TextEditor.enrichHTML(this.object.system.description, { async: true }),
notes: await TextEditor.enrichHTML(this.object.system.notes, { async: true }),
isGM: game.user.isGM
limited: item.limited,
owner: item.isOwner,
enrichedDescription: await enrich(item.system.description),
enrichedTransmission: await enrich(item.system.transmission),
enrichedSymptomes: await enrich(item.system.symptomes),
enrichedComplications: await enrich(item.system.complications),
enrichedVertus: await enrich(item.system.vertus),
enrichedToxicite: await enrich(item.system.toxicite),
isGM: game.user.isGM,
itemPartialName: `systems/fvtt-te-deum/templates/items/item-${item.type}-sheet.hbs`,
}
if (this.object.type == "education") {
TeDeumUtility.prepareEducationContent(formData);
if (item.type === "education") {
TeDeumUtility.prepareEducationContent(context)
}
this.options.editable = !(this.object.origin == "embeddedItem");
console.log("ITEM DATA", formData, this);
return formData;
}
/* -------------------------------------------- */
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
buttons.unshift({
class: "post",
icon: "fas fa-comment",
onclick: ev => this.postItem()
});
return buttons
return context
}
/* -------------------------------------------- */
postItem() {
let chatData = duplicate(this.item)
/** @override */
_onRender(context, options) {
super._onRender(context, options)
// 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)
})
}
// Ignore Enter key in inputs
this.element.addEventListener('keydown', e => {
if (e.keyCode === 13 && e.target.tagName !== 'TEXTAREA') e.preventDefault()
})
}
// #region Static action handlers
static async #onEditImage(event, target) {
const fp = new FilePicker({
type: "image",
current: this.document.img,
callback: path => this.document.update({ img: path }),
})
fp.browse()
}
static async #onPostItem(event, target) {
const chatData = foundry.utils.duplicate(this.item)
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")) {
chatData.img = null;
if (chatData.img?.includes("/blank.png")) {
chatData.img = null
}
// JSON object for easy creation
chatData.jsondata = JSON.stringify(
{
compendium: "postedItem",
payload: chatData,
});
renderTemplate('systems/fvtt-te-deum/templates/post-item.html', chatData).then(html => {
let chatOptions = TeDeumUtility.chatDataSetup(html);
ChatMessage.create(chatOptions)
});
chatData.jsondata = JSON.stringify({ compendium: "postedItem", payload: chatData })
const html = await foundry.applications.handlebars.renderTemplate(
'systems/fvtt-te-deum/templates/post-item.html', chatData
)
ChatMessage.create(TeDeumUtility.chatDataSetup(html))
}
/* -------------------------------------------- */
async viewSubitem(ev) {
let levelIndex = Number($(ev.currentTarget).parents(".item").data("level-index"))
let choiceIndex = Number($(ev.currentTarget).parents(".item").data("choice-index"))
let featureId = $(ev.currentTarget).parents(".item").data("feature-id")
let itemData = this.object.system.levels[levelIndex].choices[choiceIndex].features[featureId]
if (itemData.name != 'None') {
let item = await Item.create(itemData, { temporary: true });
item.system.origin = "embeddedItem";
new TeDeumItemSheet(item).render(true);
static async #onDeleteSubitem(event, target) {
const field = target.dataset.type
const idx = parseInt(target.dataset.index)
const oldArray = this.document.system[field]
if (Array.isArray(oldArray) && oldArray[idx]?.name !== 'None') {
const newArray = oldArray.filter((_, i) => i !== idx)
this.document.update({ [`system.${field}`]: newArray })
}
}
/* -------------------------------------------- */
async deleteSubitem(ev) {
let field = $(ev.currentTarget).data('type');
let idx = Number($(ev.currentTarget).data('index'));
let oldArray = this.object.system[field];
let itemData = this.object.system[field][idx];
if (itemData.name != 'None') {
let newArray = [];
for (let i = 0; i < oldArray.length; i++) {
if (i != idx) {
newArray.push(oldArray[i]);
}
}
this.object.update({ [`system.${field}`]: newArray });
static async #onViewSubitem(event, target) {
const li = target.closest(".item")
const levelIndex = parseInt(li?.dataset.levelIndex)
const choiceIndex = parseInt(li?.dataset.choiceIndex)
const featureId = li?.dataset.featureId
const itemData = this.document.system.levels?.[levelIndex]?.choices?.[choiceIndex]?.features?.[featureId]
if (itemData?.name !== 'None') {
const item = await Item.create(itemData, { temporary: true })
item.system.origin = "embeddedItem"
new TeDeumItemSheet(item).render(true)
}
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Update Inventory Item
html.find('.item-edit').click(ev => {
const li = $(ev.currentTarget).parents(".item");
const item = this.object.options.actor.getOwnedItem(li.data("item-id"));
item.sheet.render(true);
});
html.find('.delete-subitem').click(ev => {
this.deleteSubitem(ev);
});
// Update Inventory Item
html.find('.item-delete').click(ev => {
const li = $(ev.currentTarget).parents(".item");
let itemId = li.data("item-id");
let itemType = li.data("item-type");
});
}
/* -------------------------------------------- */
get template() {
let type = this.item.type;
return `systems/fvtt-te-deum/templates/items/item-${type}-sheet.hbs`
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
return this.object.update(formData)
}
// #endregion
}

View File

@@ -82,14 +82,16 @@ Hooks.once("init", async function () {
console.log("TeDeum RPG | Ready");
Actors.unregisterSheet("core", ActorSheet);
Actors.registerSheet("fvtt-te-deum", TeDeumActorPJSheet, { types: ["pj"], makeDefault: true });
Actors.registerSheet("fvtt-te-deum", TeDeumActorPJSheet, { types: ["pnj"], makeDefault: true });
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
foundry.documents.collections.Actors.registerSheet("fvtt-te-deum", TeDeumActorPJSheet, { types: ["pj"], makeDefault: true });
foundry.documents.collections.Actors.registerSheet("fvtt-te-deum", TeDeumActorPJSheet, { types: ["pnj"], makeDefault: true });
Items.unregisterSheet("core", ItemSheet);
Items.registerSheet("fvtt-te-deum", TeDeumItemSheet, { makeDefault: true });
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
foundry.documents.collections.Items.registerSheet("fvtt-te-deum", TeDeumItemSheet, { makeDefault: true });
TeDeumUtility.init()
TeDeumUtility.installHooks()
});
@@ -98,6 +100,7 @@ Hooks.once("init", async function () {
/* -------------------------------------------- */
Hooks.once("ready", function () {
// User warning
if (!game.user.isGM && game.user.character == undefined) {
ui.notifications.info("Attention ! Aucun personnage relié au joueur !");
@@ -107,10 +110,10 @@ Hooks.once("ready", function () {
});
}
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter=>{
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {
console.log("ClassCounter loaded", moduleCounter)
moduleCounter.ClassCounter.registerUsageCount()
}).catch(err=>
}).catch(err =>
console.log("No stats available, giving up.")
)
TeDeumUtility.ready();
@@ -134,4 +137,3 @@ Hooks.on("chatMessage", (html, content, msg) => {
}
return true;
});

21
package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "fvtt-te-deum",
"version": "1.0.0",
"description": "Système Te Deum pour FoundryVTT",
"private": true,
"scripts": {
"build:css": "gulp css",
"watch:css": "gulp watch"
},
"devDependencies": {
"gulp": "^4.0.2",
"gulp-less": "^5.0.0",
"less": "^4.2.0",
"autoprefixer": "^10.4.20",
"gulp-postcss": "^9.0.1",
"postcss": "^8.4.49"
},
"keywords": ["foundry-vtt", "te-deum"],
"author": "",
"license": "ISC"
}

Binary file not shown.

BIN
packs/aides/000236.ldb Normal file

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000081
MANIFEST-000253

View File

@@ -1,7 +1,7 @@
2025/03/18-20:02:55.715587 7fbe66ffd6c0 Recovering log #79
2025/03/18-20:02:55.727775 7fbe66ffd6c0 Delete type=3 #77
2025/03/18-20:02:55.727902 7fbe66ffd6c0 Delete type=0 #79
2025/03/18-20:34:57.401764 7fbe663ff6c0 Level-0 table #84: started
2025/03/18-20:34:57.401796 7fbe663ff6c0 Level-0 table #84: 0 bytes OK
2025/03/18-20:34:57.408016 7fbe663ff6c0 Delete type=0 #82
2025/03/18-20:34:57.415964 7fbe663ff6c0 Manual compaction at level-0 from '!journal!uNwJgi4kXBCiZmAH' @ 72057594037927935 : 1 .. '!journal.pages!uNwJgi4kXBCiZmAH.onhNU0mXhOpdNZJF' @ 0 : 0; will stop at (end)
2026/03/05-21:15:22.235257 7f78e3fff6c0 Recovering log #251
2026/03/05-21:15:22.291402 7f78e3fff6c0 Delete type=3 #249
2026/03/05-21:15:22.291561 7f78e3fff6c0 Delete type=0 #251
2026/03/05-21:15:43.404511 7f78e15b46c0 Level-0 table #256: started
2026/03/05-21:15:43.404555 7f78e15b46c0 Level-0 table #256: 0 bytes OK
2026/03/05-21:15:43.411486 7f78e15b46c0 Delete type=0 #254
2026/03/05-21:15:43.418602 7f78e15b46c0 Manual compaction at level-0 from '!journal!uNwJgi4kXBCiZmAH' @ 72057594037927935 : 1 .. '!journal.pages!uNwJgi4kXBCiZmAH.onhNU0mXhOpdNZJF' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/03/11-18:04:31.143055 7f24c6ffd6c0 Recovering log #75
2025/03/11-18:04:31.153035 7f24c6ffd6c0 Delete type=3 #73
2025/03/11-18:04:31.153146 7f24c6ffd6c0 Delete type=0 #75
2025/03/11-18:27:51.833675 7f24c4bff6c0 Level-0 table #80: started
2025/03/11-18:27:51.833743 7f24c4bff6c0 Level-0 table #80: 0 bytes OK
2025/03/11-18:27:51.840981 7f24c4bff6c0 Delete type=0 #78
2025/03/11-18:27:51.853984 7f24c4bff6c0 Manual compaction at level-0 from '!journal!uNwJgi4kXBCiZmAH' @ 72057594037927935 : 1 .. '!journal.pages!uNwJgi4kXBCiZmAH.onhNU0mXhOpdNZJF' @ 0 : 0; will stop at (end)
2026/03/05-21:13:15.670991 7f78e3fff6c0 Recovering log #247
2026/03/05-21:13:15.682171 7f78e3fff6c0 Delete type=3 #245
2026/03/05-21:13:15.682284 7f78e3fff6c0 Delete type=0 #247
2026/03/05-21:13:59.267947 7f78e15b46c0 Level-0 table #252: started
2026/03/05-21:13:59.267998 7f78e15b46c0 Level-0 table #252: 0 bytes OK
2026/03/05-21:13:59.275662 7f78e15b46c0 Delete type=0 #250
2026/03/05-21:13:59.282530 7f78e15b46c0 Manual compaction at level-0 from '!journal!uNwJgi4kXBCiZmAH' @ 72057594037927935 : 1 .. '!journal.pages!uNwJgi4kXBCiZmAH.onhNU0mXhOpdNZJF' @ 0 : 0; will stop at (end)

Binary file not shown.

BIN
packs/aides/MANIFEST-000253 Normal file

Binary file not shown.

Binary file not shown.

BIN
packs/armes/000280.ldb Normal file

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000183
MANIFEST-000331

View File

@@ -1,7 +1,5 @@
2025/03/18-20:02:55.593062 7fbe67fff6c0 Recovering log #181
2025/03/18-20:02:55.605860 7fbe67fff6c0 Delete type=3 #179
2025/03/18-20:02:55.605967 7fbe67fff6c0 Delete type=0 #181
2025/03/18-20:34:57.329715 7fbe663ff6c0 Level-0 table #186: started
2025/03/18-20:34:57.329814 7fbe663ff6c0 Level-0 table #186: 0 bytes OK
2025/03/18-20:34:57.336235 7fbe663ff6c0 Delete type=0 #184
2025/03/18-20:34:57.356288 7fbe663ff6c0 Manual compaction at level-0 from '!folders!InCQeTRdT5jXMX82' @ 72057594037927935 : 1 .. '!items!wxIHkrq98eQ3cOvp' @ 0 : 0; will stop at (end)
2026/03/05-21:15:21.839495 7f7930fff6c0 Delete type=3 #329
2026/03/05-21:15:43.331720 7f78e15b46c0 Level-0 table #334: started
2026/03/05-21:15:43.331839 7f78e15b46c0 Level-0 table #334: 0 bytes OK
2026/03/05-21:15:43.338988 7f78e15b46c0 Delete type=0 #332
2026/03/05-21:15:43.360287 7f78e15b46c0 Manual compaction at level-0 from '!folders!InCQeTRdT5jXMX82' @ 72057594037927935 : 1 .. '!items!wxIHkrq98eQ3cOvp' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,3 @@
2025/03/11-18:04:31.045395 7f24c5ffb6c0 Recovering log #177
2025/03/11-18:04:31.056356 7f24c5ffb6c0 Delete type=3 #175
2025/03/11-18:04:31.056467 7f24c5ffb6c0 Delete type=0 #177
2025/03/11-18:27:51.772304 7f24c4bff6c0 Level-0 table #182: started
2025/03/11-18:27:51.772338 7f24c4bff6c0 Level-0 table #182: 0 bytes OK
2025/03/11-18:27:51.778305 7f24c4bff6c0 Delete type=0 #180
2025/03/11-18:27:51.798039 7f24c4bff6c0 Manual compaction at level-0 from '!folders!InCQeTRdT5jXMX82' @ 72057594037927935 : 1 .. '!items!wxIHkrq98eQ3cOvp' @ 0 : 0; will stop at (end)
2026/02/25-16:17:49.007591 7f821f7fe6c0 Recovering log #327
2026/02/25-16:17:49.018485 7f821f7fe6c0 Delete type=3 #325
2026/02/25-16:17:49.018548 7f821f7fe6c0 Delete type=0 #327

Binary file not shown.

BIN
packs/armes/MANIFEST-000331 Normal file

Binary file not shown.

Binary file not shown.

BIN
packs/armures/000339.ldb Normal file

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000183
MANIFEST-000356

View File

@@ -1,7 +1,7 @@
2025/03/18-20:02:55.610587 7fbe66ffd6c0 Recovering log #181
2025/03/18-20:02:55.621230 7fbe66ffd6c0 Delete type=3 #179
2025/03/18-20:02:55.621425 7fbe66ffd6c0 Delete type=0 #181
2025/03/18-20:34:57.336450 7fbe663ff6c0 Level-0 table #186: started
2025/03/18-20:34:57.336506 7fbe663ff6c0 Level-0 table #186: 0 bytes OK
2025/03/18-20:34:57.343480 7fbe663ff6c0 Delete type=0 #184
2025/03/18-20:34:57.356316 7fbe663ff6c0 Manual compaction at level-0 from '!folders!2wTJBj3dicRKzNOE' @ 72057594037927935 : 1 .. '!items!ufvhWG5V8pX0qrtR' @ 0 : 0; will stop at (end)
2026/03/05-21:15:21.843260 7f78e2ffd6c0 Recovering log #354
2026/03/05-21:15:21.929269 7f78e2ffd6c0 Delete type=3 #352
2026/03/05-21:15:21.929400 7f78e2ffd6c0 Delete type=0 #354
2026/03/05-21:15:43.345888 7f78e15b46c0 Level-0 table #359: started
2026/03/05-21:15:43.345931 7f78e15b46c0 Level-0 table #359: 0 bytes OK
2026/03/05-21:15:43.352934 7f78e15b46c0 Delete type=0 #357
2026/03/05-21:15:43.360346 7f78e15b46c0 Manual compaction at level-0 from '!folders!2wTJBj3dicRKzNOE' @ 72057594037927935 : 1 .. '!items!ufvhWG5V8pX0qrtR' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/03/11-18:04:31.059498 7f24c6ffd6c0 Recovering log #177
2025/03/11-18:04:31.068855 7f24c6ffd6c0 Delete type=3 #175
2025/03/11-18:04:31.068919 7f24c6ffd6c0 Delete type=0 #177
2025/03/11-18:27:51.778439 7f24c4bff6c0 Level-0 table #182: started
2025/03/11-18:27:51.778466 7f24c4bff6c0 Level-0 table #182: 0 bytes OK
2025/03/11-18:27:51.784375 7f24c4bff6c0 Delete type=0 #180
2025/03/11-18:27:51.798058 7f24c4bff6c0 Manual compaction at level-0 from '!folders!2wTJBj3dicRKzNOE' @ 72057594037927935 : 1 .. '!items!ufvhWG5V8pX0qrtR' @ 0 : 0; will stop at (end)
2026/03/05-21:13:15.571789 7f78e2ffd6c0 Recovering log #350
2026/03/05-21:13:15.582373 7f78e2ffd6c0 Delete type=3 #348
2026/03/05-21:13:15.582493 7f78e2ffd6c0 Delete type=0 #350
2026/03/05-21:13:59.207314 7f78e15b46c0 Level-0 table #355: started
2026/03/05-21:13:59.207365 7f78e15b46c0 Level-0 table #355: 0 bytes OK
2026/03/05-21:13:59.213542 7f78e15b46c0 Delete type=0 #353
2026/03/05-21:13:59.227939 7f78e15b46c0 Manual compaction at level-0 from '!folders!2wTJBj3dicRKzNOE' @ 72057594037927935 : 1 .. '!items!ufvhWG5V8pX0qrtR' @ 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-000180
MANIFEST-000354

View File

@@ -1,7 +1,7 @@
2025/03/18-20:02:55.575835 7fbe677fe6c0 Recovering log #178
2025/03/18-20:02:55.586561 7fbe677fe6c0 Delete type=3 #176
2025/03/18-20:02:55.586685 7fbe677fe6c0 Delete type=0 #178
2025/03/18-20:34:57.343590 7fbe663ff6c0 Level-0 table #183: started
2025/03/18-20:34:57.343614 7fbe663ff6c0 Level-0 table #183: 0 bytes OK
2025/03/18-20:34:57.349686 7fbe663ff6c0 Delete type=0 #181
2025/03/18-20:34:57.356337 7fbe663ff6c0 Manual compaction at level-0 from '!folders!4OPhigzcPv46qbWW' @ 72057594037927935 : 1 .. '!items!yx4k7lQHGcom99mk' @ 0 : 0; will stop at (end)
2026/03/05-21:15:21.593711 7f78e3fff6c0 Recovering log #352
2026/03/05-21:15:21.647801 7f78e3fff6c0 Delete type=3 #350
2026/03/05-21:15:21.647916 7f78e3fff6c0 Delete type=0 #352
2026/03/05-21:15:43.353206 7f78e15b46c0 Level-0 table #357: started
2026/03/05-21:15:43.353251 7f78e15b46c0 Level-0 table #357: 0 bytes OK
2026/03/05-21:15:43.359989 7f78e15b46c0 Delete type=0 #355
2026/03/05-21:15:43.360369 7f78e15b46c0 Manual compaction at level-0 from '!folders!4OPhigzcPv46qbWW' @ 72057594037927935 : 1 .. '!items!yx4k7lQHGcom99mk' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/03/11-18:04:31.031690 7f24c57fa6c0 Recovering log #174
2025/03/11-18:04:31.041244 7f24c57fa6c0 Delete type=3 #172
2025/03/11-18:04:31.041294 7f24c57fa6c0 Delete type=0 #174
2025/03/11-18:27:51.791921 7f24c4bff6c0 Level-0 table #179: started
2025/03/11-18:27:51.791971 7f24c4bff6c0 Level-0 table #179: 0 bytes OK
2025/03/11-18:27:51.797892 7f24c4bff6c0 Delete type=0 #177
2025/03/11-18:27:51.798085 7f24c4bff6c0 Manual compaction at level-0 from '!folders!4OPhigzcPv46qbWW' @ 72057594037927935 : 1 .. '!items!yx4k7lQHGcom99mk' @ 0 : 0; will stop at (end)
2026/03/05-21:13:15.541026 7f7930fff6c0 Recovering log #348
2026/03/05-21:13:15.552276 7f7930fff6c0 Delete type=3 #346
2026/03/05-21:13:15.552407 7f7930fff6c0 Delete type=0 #348
2026/03/05-21:13:59.200610 7f78e15b46c0 Level-0 table #353: started
2026/03/05-21:13:59.200697 7f78e15b46c0 Level-0 table #353: 0 bytes OK
2026/03/05-21:13:59.207089 7f78e15b46c0 Delete type=0 #351
2026/03/05-21:13:59.227917 7f78e15b46c0 Manual compaction at level-0 from '!folders!4OPhigzcPv46qbWW' @ 72057594037927935 : 1 .. '!items!yx4k7lQHGcom99mk' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000191
MANIFEST-000366

View File

@@ -1,7 +1,7 @@
2025/03/18-20:02:55.625718 7fbe6c9fa6c0 Recovering log #189
2025/03/18-20:02:55.637706 7fbe6c9fa6c0 Delete type=3 #187
2025/03/18-20:02:55.637826 7fbe6c9fa6c0 Delete type=0 #189
2025/03/18-20:34:57.349890 7fbe663ff6c0 Level-0 table #194: started
2025/03/18-20:34:57.349944 7fbe663ff6c0 Level-0 table #194: 0 bytes OK
2025/03/18-20:34:57.356029 7fbe663ff6c0 Delete type=0 #192
2025/03/18-20:34:57.356358 7fbe663ff6c0 Manual compaction at level-0 from '!folders!9PQi3Lv54rpcxavo' @ 72057594037927935 : 1 .. '!items!zGlRtP7zSnkjuuue' @ 0 : 0; will stop at (end)
2026/03/05-21:15:21.932980 7f78e3fff6c0 Recovering log #364
2026/03/05-21:15:21.994263 7f78e3fff6c0 Delete type=3 #362
2026/03/05-21:15:21.994412 7f78e3fff6c0 Delete type=0 #364
2026/03/05-21:15:43.360527 7f78e15b46c0 Level-0 table #369: started
2026/03/05-21:15:43.360574 7f78e15b46c0 Level-0 table #369: 0 bytes OK
2026/03/05-21:15:43.367363 7f78e15b46c0 Delete type=0 #367
2026/03/05-21:15:43.390217 7f78e15b46c0 Manual compaction at level-0 from '!folders!9PQi3Lv54rpcxavo' @ 72057594037927935 : 1 .. '!items!zGlRtP7zSnkjuuue' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/03/11-18:04:31.072608 7f24c67fc6c0 Recovering log #184
2025/03/11-18:04:31.084241 7f24c67fc6c0 Delete type=3 #182
2025/03/11-18:04:31.084398 7f24c67fc6c0 Delete type=0 #184
2025/03/11-18:27:51.784510 7f24c4bff6c0 Level-0 table #190: started
2025/03/11-18:27:51.784541 7f24c4bff6c0 Level-0 table #190: 0 bytes OK
2025/03/11-18:27:51.791721 7f24c4bff6c0 Delete type=0 #188
2025/03/11-18:27:51.798072 7f24c4bff6c0 Manual compaction at level-0 from '!folders!9PQi3Lv54rpcxavo' @ 72057594037927935 : 1 .. '!items!zGlRtP7zSnkjuuue' @ 0 : 0; will stop at (end)
2026/03/05-21:13:15.586853 7f7930fff6c0 Recovering log #360
2026/03/05-21:13:15.598391 7f7930fff6c0 Delete type=3 #358
2026/03/05-21:13:15.598515 7f7930fff6c0 Delete type=0 #360
2026/03/05-21:13:59.220322 7f78e15b46c0 Level-0 table #365: started
2026/03/05-21:13:59.220368 7f78e15b46c0 Level-0 table #365: 0 bytes OK
2026/03/05-21:13:59.227684 7f78e15b46c0 Delete type=0 #363
2026/03/05-21:13:59.227977 7f78e15b46c0 Manual compaction at level-0 from '!folders!9PQi3Lv54rpcxavo' @ 72057594037927935 : 1 .. '!items!zGlRtP7zSnkjuuue' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

Binary file not shown.

BIN
packs/graces/000338.ldb Normal file

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000182
MANIFEST-000355

View File

@@ -1,7 +1,7 @@
2025/03/18-20:02:55.645262 7fbe677fe6c0 Recovering log #180
2025/03/18-20:02:55.657308 7fbe677fe6c0 Delete type=3 #178
2025/03/18-20:02:55.657414 7fbe677fe6c0 Delete type=0 #180
2025/03/18-20:34:57.370748 7fbe663ff6c0 Level-0 table #185: started
2025/03/18-20:34:57.370813 7fbe663ff6c0 Level-0 table #185: 0 bytes OK
2025/03/18-20:34:57.377323 7fbe663ff6c0 Delete type=0 #183
2025/03/18-20:34:57.384883 7fbe663ff6c0 Manual compaction at level-0 from '!items!17mjvwS8R3B6LloG' @ 72057594037927935 : 1 .. '!items!zUYIVOuFpRur9aAR' @ 0 : 0; will stop at (end)
2026/03/05-21:15:22.000326 7f78e37fe6c0 Recovering log #353
2026/03/05-21:15:22.058297 7f78e37fe6c0 Delete type=3 #351
2026/03/05-21:15:22.058416 7f78e37fe6c0 Delete type=0 #353
2026/03/05-21:15:43.339182 7f78e15b46c0 Level-0 table #358: started
2026/03/05-21:15:43.339230 7f78e15b46c0 Level-0 table #358: 0 bytes OK
2026/03/05-21:15:43.345648 7f78e15b46c0 Delete type=0 #356
2026/03/05-21:15:43.360322 7f78e15b46c0 Manual compaction at level-0 from '!items!17mjvwS8R3B6LloG' @ 72057594037927935 : 1 .. '!items!zUYIVOuFpRur9aAR' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/03/11-18:04:31.089058 7f24c57fa6c0 Recovering log #176
2025/03/11-18:04:31.099719 7f24c57fa6c0 Delete type=3 #174
2025/03/11-18:04:31.099830 7f24c57fa6c0 Delete type=0 #176
2025/03/11-18:27:51.804396 7f24c4bff6c0 Level-0 table #181: started
2025/03/11-18:27:51.804421 7f24c4bff6c0 Level-0 table #181: 0 bytes OK
2025/03/11-18:27:51.810625 7f24c4bff6c0 Delete type=0 #179
2025/03/11-18:27:51.823828 7f24c4bff6c0 Manual compaction at level-0 from '!items!17mjvwS8R3B6LloG' @ 72057594037927935 : 1 .. '!items!zUYIVOuFpRur9aAR' @ 0 : 0; will stop at (end)
2026/03/05-21:13:15.605872 7f78e3fff6c0 Recovering log #349
2026/03/05-21:13:15.616782 7f78e3fff6c0 Delete type=3 #347
2026/03/05-21:13:15.616908 7f78e3fff6c0 Delete type=0 #349
2026/03/05-21:13:59.234495 7f78e15b46c0 Level-0 table #354: started
2026/03/05-21:13:59.234538 7f78e15b46c0 Level-0 table #354: 0 bytes OK
2026/03/05-21:13:59.240802 7f78e15b46c0 Delete type=0 #352
2026/03/05-21:13:59.254802 7f78e15b46c0 Manual compaction at level-0 from '!items!17mjvwS8R3B6LloG' @ 72057594037927935 : 1 .. '!items!zUYIVOuFpRur9aAR' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

Binary file not shown.

BIN
packs/maladies/000338.ldb Normal file

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000182
MANIFEST-000355

View File

@@ -1,7 +1,7 @@
2025/03/18-20:02:55.662488 7fbe67fff6c0 Recovering log #180
2025/03/18-20:02:55.673613 7fbe67fff6c0 Delete type=3 #178
2025/03/18-20:02:55.673721 7fbe67fff6c0 Delete type=0 #180
2025/03/18-20:34:57.356547 7fbe663ff6c0 Level-0 table #185: started
2025/03/18-20:34:57.356623 7fbe663ff6c0 Level-0 table #185: 0 bytes OK
2025/03/18-20:34:57.363717 7fbe663ff6c0 Delete type=0 #183
2025/03/18-20:34:57.384818 7fbe663ff6c0 Manual compaction at level-0 from '!items!1icaxIywAwDXQcMz' @ 72057594037927935 : 1 .. '!items!ysGehYm1VkMWrI22' @ 0 : 0; will stop at (end)
2026/03/05-21:15:22.062292 7f7930fff6c0 Recovering log #353
2026/03/05-21:15:22.114101 7f7930fff6c0 Delete type=3 #351
2026/03/05-21:15:22.114232 7f7930fff6c0 Delete type=0 #353
2026/03/05-21:15:43.382516 7f78e15b46c0 Level-0 table #358: started
2026/03/05-21:15:43.382559 7f78e15b46c0 Level-0 table #358: 0 bytes OK
2026/03/05-21:15:43.389985 7f78e15b46c0 Delete type=0 #356
2026/03/05-21:15:43.390278 7f78e15b46c0 Manual compaction at level-0 from '!items!1icaxIywAwDXQcMz' @ 72057594037927935 : 1 .. '!items!ysGehYm1VkMWrI22' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/03/11-18:04:31.102877 7f24c5ffb6c0 Recovering log #176
2025/03/11-18:04:31.112795 7f24c5ffb6c0 Delete type=3 #174
2025/03/11-18:04:31.112850 7f24c5ffb6c0 Delete type=0 #176
2025/03/11-18:27:51.798175 7f24c4bff6c0 Level-0 table #181: started
2025/03/11-18:27:51.798273 7f24c4bff6c0 Level-0 table #181: 0 bytes OK
2025/03/11-18:27:51.804291 7f24c4bff6c0 Delete type=0 #179
2025/03/11-18:27:51.823814 7f24c4bff6c0 Manual compaction at level-0 from '!items!1icaxIywAwDXQcMz' @ 72057594037927935 : 1 .. '!items!ysGehYm1VkMWrI22' @ 0 : 0; will stop at (end)
2026/03/05-21:13:15.621606 7f78e2ffd6c0 Recovering log #349
2026/03/05-21:13:15.632595 7f78e2ffd6c0 Delete type=3 #347
2026/03/05-21:13:15.632713 7f78e2ffd6c0 Delete type=0 #349
2026/03/05-21:13:59.240992 7f78e15b46c0 Level-0 table #354: started
2026/03/05-21:13:59.241037 7f78e15b46c0 Level-0 table #354: 0 bytes OK
2026/03/05-21:13:59.247668 7f78e15b46c0 Delete type=0 #352
2026/03/05-21:13:59.254820 7f78e15b46c0 Manual compaction at level-0 from '!items!1icaxIywAwDXQcMz' @ 72057594037927935 : 1 .. '!items!ysGehYm1VkMWrI22' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

View File

@@ -1 +1 @@
MANIFEST-000120
MANIFEST-000292

View File

@@ -1,7 +1,7 @@
2025/03/18-20:02:55.697082 7fbe6c9fa6c0 Recovering log #118
2025/03/18-20:02:55.708073 7fbe6c9fa6c0 Delete type=3 #116
2025/03/18-20:02:55.708254 7fbe6c9fa6c0 Delete type=0 #118
2025/03/18-20:34:57.377570 7fbe663ff6c0 Level-0 table #123: started
2025/03/18-20:34:57.377636 7fbe663ff6c0 Level-0 table #123: 0 bytes OK
2025/03/18-20:34:57.384565 7fbe663ff6c0 Delete type=0 #121
2025/03/18-20:34:57.384911 7fbe663ff6c0 Manual compaction at level-0 from '!scenes!FJXugdbkBpEJEdR6' @ 72057594037927935 : 1 .. '!scenes!FJXugdbkBpEJEdR6' @ 0 : 0; will stop at (end)
2026/03/05-21:15:22.179895 7f78e37fe6c0 Recovering log #290
2026/03/05-21:15:22.230205 7f78e37fe6c0 Delete type=3 #288
2026/03/05-21:15:22.230319 7f78e37fe6c0 Delete type=0 #290
2026/03/05-21:15:43.367589 7f78e15b46c0 Level-0 table #295: started
2026/03/05-21:15:43.367631 7f78e15b46c0 Level-0 table #295: 0 bytes OK
2026/03/05-21:15:43.375171 7f78e15b46c0 Delete type=0 #293
2026/03/05-21:15:43.390241 7f78e15b46c0 Manual compaction at level-0 from '!scenes!FJXugdbkBpEJEdR6' @ 72057594037927935 : 1 .. '!scenes!FJXugdbkBpEJEdR6' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/03/11-18:04:31.129320 7f24c67fc6c0 Recovering log #114
2025/03/11-18:04:31.139477 7f24c67fc6c0 Delete type=3 #112
2025/03/11-18:04:31.139583 7f24c67fc6c0 Delete type=0 #114
2025/03/11-18:27:51.817747 7f24c4bff6c0 Level-0 table #119: started
2025/03/11-18:27:51.817804 7f24c4bff6c0 Level-0 table #119: 0 bytes OK
2025/03/11-18:27:51.823741 7f24c4bff6c0 Delete type=0 #117
2025/03/11-18:27:51.823845 7f24c4bff6c0 Manual compaction at level-0 from '!scenes!FJXugdbkBpEJEdR6' @ 72057594037927935 : 1 .. '!scenes!FJXugdbkBpEJEdR6' @ 0 : 0; will stop at (end)
2026/03/05-21:13:15.654627 7f7930fff6c0 Recovering log #286
2026/03/05-21:13:15.665089 7f7930fff6c0 Delete type=3 #284
2026/03/05-21:13:15.665217 7f7930fff6c0 Delete type=0 #286
2026/03/05-21:13:59.247815 7f78e15b46c0 Level-0 table #291: started
2026/03/05-21:13:59.247859 7f78e15b46c0 Level-0 table #291: 0 bytes OK
2026/03/05-21:13:59.254589 7f78e15b46c0 Delete type=0 #289
2026/03/05-21:13:59.254839 7f78e15b46c0 Manual compaction at level-0 from '!scenes!FJXugdbkBpEJEdR6' @ 72057594037927935 : 1 .. '!scenes!FJXugdbkBpEJEdR6' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

Binary file not shown.

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