29 Commits

Author SHA1 Message Date
b0265a7089 Foundryv14 migration
All checks were successful
Release Creation / build (release) Successful in 54s
2026-04-01 22:30:02 +02:00
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
129 changed files with 7270 additions and 3261 deletions

View File

@@ -8,45 +8,56 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner." - run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner."
#- uses: actions/checkout@v3 #- 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` # get part of the tag after the `v`
- name: Extract tag version number - name: Extract tag version number
id: get_version 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 # Substitute the Manifest and Download URLs in the module.json
- name: Substitute Manifest and Download Links For Versioned Ones - name: Substitute Manifest and Download Links For Versioned Ones
id: sub_manifest_link_version id: sub_manifest_link_version
uses: microsoft/variable-substitution@v1 uses: https://github.com/microsoft/variable-substitution@v1
with: with:
files: 'system.json' files: "system.json"
env: env:
version: ${{steps.get_version.outputs.version-without-v}} version: ${{steps.get_version.outputs.version-without-v}}
url: https://www.uberwald.me/gitea/${{gitea.repository}} url: https://www.uberwald.me/gitea/${{gitea.repository}}
manifest: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/latest/system.json manifest: https://www.uberwald.me/gitea/public/fvtt-te-deum/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 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 # Create a zip file with all files required by the module to add to the release
- run: | - run: |
apt update -y apt update -y
apt install -y zip 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 - name: setup go
uses: https://github.com/actions/setup-go@v4 uses: https://github.com/actions/setup-go@v4
with: with:
go-version: '>=1.20.1' go-version: ">=1.20.1"
- name: Use Go Action - name: Use Go Action
id: use-go-action id: use-go-action
uses: https://gitea.com/actions/release-action@main uses: https://gitea.com/actions/release-action@main
with: with:
files: |- files: |-
./fvtt-te-deum-${{github.event.release.tag_name}}.zip ./fvtt-te-deum.zip
system.json 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 # 12.0.23
- Correction sur les jets réussie en tir - Correction sur les jets réussie en tir

View File

@@ -1,25 +1,17 @@
var gulp = require('gulp'); var gulp = require('gulp');
var less = require('gulp-less');
var postcss = require('gulp-postcss'); var postcss = require('gulp-postcss');
var autoprefixer = require('autoprefixer'); var autoprefixer = require('autoprefixer');
var cssnext = require('postcss-preset-env');
var precss = require('precss');
gulp.task('css', function () { gulp.task('css', function () {
return gulp.src('./less/*.less')
var processors = [ .pipe(less())
autoprefixer, .pipe(postcss([autoprefixer]))
cssnext,
precss
];
return gulp.src('./postcss/*.css')
.pipe(postcss(processors))
.pipe(gulp.dest('./styles')); .pipe(gulp.dest('./styles'));
}); });
gulp.task('watch', function () {
gulp.watch('./less/*.less', gulp.series('css'));
});
function watchUpdates() { gulp.task('default', gulp.series('css'));
gulp.watch('./postcss/*.css', 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,176 +1,227 @@
/** /**
* Extend the basic ActorSheet with some very simple modifications * Feuille de personnage Te Deum - AppV2
* @extends {ActorSheet}
*/ */
import { TeDeumUtility } from "../common/tedeum-utility.js"; import { TeDeumUtility } from "../common/tedeum-utility.js";
const { HandlebarsApplicationMixin } = foundry.applications.api
/* -------------------------------------------- */ /* -------------------------------------------- */
export class TeDeumActorPJSheet extends foundry.appv1.sheets.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 */ /** @override */
static get defaultOptions() { static DEFAULT_OPTIONS = {
classes: ["fvtt-te-deum", "sheet", "actor"],
return foundry.utils.mergeObject(super.defaultOptions, { position: {
classes: ["fvtt-te-deum", "sheet", "actor"],
template: "systems/fvtt-te-deum/templates/actors/actor-sheet.hbs",
width: 860, width: 860,
height:680, height: 680,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "skills" }], },
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], form: {
editScore: true submitOnChange: true,
}); closeOnSubmit: false,
},
window: {
resizable: true,
},
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
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() { /** @override */
async _prepareContext() {
let formData = { const actor = this.document
return {
title: this.title, title: this.title,
id: this.actor.id, id: actor.id,
type: this.actor.type, type: actor.type,
img: this.actor.img, img: actor.img,
name: this.actor.name, name: actor.name,
editable: this.isEditable, editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked", cssClass: this.isEditable ? "editable" : "locked",
system: foundry.utils.duplicate(this.object.system), system: foundry.utils.duplicate(actor.system),
limited: this.object.limited, systemFields: actor.system.schema.fields,
competences: this.actor.getCompetences(), limited: actor.limited,
competences: actor.getCompetences(),
config: foundry.utils.duplicate(game.system.tedeum.config), config: foundry.utils.duplicate(game.system.tedeum.config),
armes: this.actor.getArmes(), armes: actor.getArmes(),
caracList: this.actor.prepareCaracteristiques(), caracList: actor.prepareCaracteristiques(),
providence: this.actor.prepareProvidence(), providence: actor.prepareProvidence(),
arbreCompetences: this.actor.prepareArbreCompetences(), arbreCompetences: actor.prepareArbreCompetences(),
equipements: this.actor.getEquipements(), equipements: actor.getEquipements(),
simples: this.actor.getSimples(), simples: actor.getSimples(),
armures: this.actor.getArmures(), armures: actor.getArmures(),
graces: this.actor.getGraces(), graces: actor.getGraces(),
blessures: this.actor.getBlessures(), blessures: actor.getBlessures(),
maladies: this.actor.getMaladies(), maladies: actor.getMaladies(),
poisons: this.actor.getPoisons(), poisons: actor.getPoisons(),
combat: this.actor.prepareCombat(), combat: actor.prepareCombat(),
bonusDegats: this.actor.getBonusDegats(), bonusDegats: actor.getBonusDegats(),
nbActions: this.actor.getNbActions(), nbActions: actor.getNbActions(),
initiative: this.actor.getInitiative(), initiative: actor.getInitiative(),
pointsArmuresLourdes: this.actor.getNbArmures(), pointsArmuresLourdes: actor.getNbArmures(),
nbArmuresLourdes: this.actor.getNbArmuresLourdesActuel(), nbArmuresLourdes: actor.getNbArmuresLourdesActuel(),
santeModifier: this.actor.getSanteModifier(), santeModifier: actor.getSanteModifier(),
educations: this.actor.getEducations(), educations: actor.getEducations(),
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.description, { async: true }), enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.description, { async: true }),
equipmentfree: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.equipmentfree, { async: true }), enrichedEquipmentFree: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.equipmentfree, { async: true }),
notes: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.notes, { async: true }), enrichedNotes: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.notes, { async: true }),
histoire: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.histoire, { async: true }), enrichedHistoire: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.histoire, { async: true }),
options: this.options, owner: actor.isOwner,
owner: this.document.isOwner, isEditMode: this.isEditMode,
editScore: this.options.editScore, isPlayMode: this.isPlayMode,
isGM: game.user.isGM isGM: game.user.isGM,
} }
this.formData = formData;
console.log("PC : ", formData, this.object);
return formData;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
// 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")
})
html.find('.item-add').click(ev => {
let dataType = $(ev.currentTarget).data("type")
this.actor.createEmbeddedDocuments('Item', [{ name: "Nouveau " + dataType, type: dataType }], { renderSheet: true })
})
html.find('.competence-add').click(ev => {
let dataType = $(ev.currentTarget).data("type")
let caracKey = $(ev.currentTarget).data("carac-key")
this.actor.createEmbeddedDocuments('Item', [{ name: "Nouvelle " + dataType, type: dataType, system: {caracteristique: caracKey} }], { renderSheet: true })
})
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 } );
});
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
/** @override */ /** @override */
setPosition(options = {}) { _onRender(context, options) {
const position = super.setPosition(options); super._onRender(context, options)
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - 192; // Tab navigation
sheetBody.css("height", bodyHeight); const nav = this.element.querySelector('nav.tabs[data-group]')
return position; 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()
})
})
this.element.querySelectorAll(`[data-group="${group}"][data-tab]`).forEach(content => {
content.classList.toggle('active', content.dataset.tab === activeTab)
})
}
// Ignore Enter key in sheet inputs
this.element.addEventListener('keydown', e => {
if (e.keyCode === 13 && e.target.tagName !== 'TEXTAREA') e.preventDefault()
})
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
/** @override */ // #region Static action handlers
_updateObject(event, formData) {
// Update the Actor static async #onEditImage(event) {
return this.object.update(formData); const fp = new FilePicker({
type: "image",
current: this.document.img,
callback: path => this.document.update({ img: path }),
})
fp.browse()
} }
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

@@ -34,7 +34,7 @@ export class TeDeumActor extends Actor {
return actor; return actor;
} }
if (data.type == 'pj' || data.type == 'pnj') { if (data.type == 'pj' || data.type == 'pnj') {
const skills = await TeDeumUtility.loadCompendium("fvtt-te-deum.competences") const skills = await TeDeumUtility.loadCompendium("fvtt-te-deum.competences")
data.items = data.items || [] data.items = data.items || []
for (let skill of skills) { for (let skill of skills) {
@@ -65,14 +65,43 @@ export class TeDeumActor extends Actor {
super._preUpdate(changed, options, user); super._preUpdate(changed, options, user);
} }
/* -------------------------------------------- */
getCompetenceScore(compName) { getCompetenceScore(compName) {
let competence = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == compName.toLowerCase()) let competence = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == compName.toLowerCase())
if (competence) { if (competence) {
if (competence.system.isBase) {
return this.system.caracteristiques[competence.system.caracteristique].value
}
return competence.system.score return competence.system.score
} }
return 0 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) { _onUpdate(changed, options, userId) {
let updates = [] let updates = []
@@ -112,12 +141,6 @@ export class TeDeumActor extends Actor {
updates.push({ _id: initiative.id, "system.score": Number(newScore) }) 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") let effort = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "effort")
newScore = this.getCommonBaseValue(this.system.caracteristiques.puissance.value) newScore = this.getCommonBaseValue(this.system.caracteristiques.puissance.value)
if (effort && effort?.system.score != newScore) { if (effort && effort?.system.score != newScore) {
@@ -147,7 +170,7 @@ export class TeDeumActor extends Actor {
getCommonBaseValue(value) { getCommonBaseValue(value) {
return game.system.tedeum.config.COMMON_VALUE[value]?.value || 0 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 return game.system.tedeum.config.COMMON_VALUE[this.system.caracteristiques.adresse.value]?.value || 0
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -155,6 +178,24 @@ export class TeDeumActor extends Actor {
return game.system.tedeum.config.BONUS_DEGATS[this.system.caracteristiques.puissance.value] 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() { getNbArmures() {
return game.system.tedeum.config.MAX_ARMURES_LOURDES[this.system.caracteristiques.puissance.value] return game.system.tedeum.config.MAX_ARMURES_LOURDES[this.system.caracteristiques.puissance.value]
} }
@@ -266,41 +307,105 @@ export class TeDeumActor extends Actor {
modTotal += blessDef.modifier modTotal += blessDef.modifier
} }
// Si le nombre de blessures est supérieur au score d'endurance, alors malus supplémentaire // 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") let enduranceScore = this.getCompetenceScore("endurance")
if (blessures.length > endurance.system.score) { if (blessures.length > enduranceScore) {
modTotal += -1 modTotal += -1
} }
return modTotal return modTotal
} }
/* -------------------------------------------- */
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: 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) { async appliquerDegats(rollData) {
let combat = this.prepareCombat() let combat = this.prepareCombat()
rollData.defenderName = this.name rollData.defenderName = this.name
let touche = combat[rollData.loc.id].touche 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) { if (rollData.degats > 0 && rollData.degats > touche) {
let diff = rollData.degats - touche let diff = rollData.degats - touche
for (let bId in game.system.tedeum.config.blessures) { for (let bId in game.system.tedeum.config.blessures) {
let blessure = game.system.tedeum.config.blessures[bId] let blessure = game.system.tedeum.config.blessures[bId]
if (diff >= blessure.degatsMin && diff <= blessure.degatsMax) { if (diff >= blessure.degatsMin && diff <= blessure.degatsMax) {
// Create a new blessure object if (rollData.isReussiteCritique) {
let blessureObj = { bId = game.system.tedeum.config.blessuresOrder[blessure.value + 1]
name: blessure.label,
type: "blessure",
system: {
typeBlessure: bId,
localisation: rollData.loc.id,
appliquee: true,
description: "Blessure infligée par un coup de " + rollData.arme.name + " de " + rollData.alias,
}
} }
rollData.blessure = blessureObj blessureId = bId
this.createEmbeddedDocuments('Item', [blessureObj]); 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 // Display the relevant chat message
let msg = await TeDeumUtility.createChatWithRollMode(rollData.alias, { 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) await msg.setFlag("world", "te-deum-rolldata", rollData)
} }
@@ -334,7 +439,7 @@ export class TeDeumActor extends Actor {
let providence = foundry.utils.deepClone(this.system.providence) let providence = foundry.utils.deepClone(this.system.providence)
providence.name = "Providence" providence.name = "Providence"
if (this.system.genre.toLowerCase() == "homme") { if (this.system.genre.toLowerCase() == "homme") {
providence.qualite = game.system.tedeum.config.providence[providence.value].labelH providence.qualite = game.system.tedeum.config.providence[providence.value].labelM
} else { } else {
providence.qualite = game.system.tedeum.config.providence[providence.value].labelF providence.qualite = game.system.tedeum.config.providence[providence.value].labelF
} }
@@ -382,6 +487,7 @@ export class TeDeumActor extends Actor {
xp = Math.max(xp + value, 0) xp = Math.max(xp + value, 0)
await this.update({ [`system.caracteristiques.${key}.experience`]: xp }) await this.update({ [`system.caracteristiques.${key}.experience`]: xp })
this.sheet?.render(true) this.sheet?.render(true)
ui.notifications.info(`+${value} XP en ${game.system.tedeum.config.caracteristiques[key].label}`)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -454,7 +560,7 @@ export class TeDeumActor extends Actor {
flag = armure.system.superposableCuir flag = armure.system.superposableCuir
} }
if (item.system.typeArmure == "maille") { if (item.system.typeArmure == "maille") {
flag = armure.system.superposableMaille flag = armure.system.superposableMaille
} }
if (item.system.typeArmure == "plate") { if (item.system.typeArmure == "plate") {
flag = armure.system.superposablePlate flag = armure.system.superposablePlate
@@ -529,12 +635,16 @@ export class TeDeumActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
getInitiativeScore() { getInitiativeScore() {
let initiative = this.items.find(it => it.type == "competence" && it.name.toLowerCase() == "initiative") let initiative = this.getInitiativeValue()
if (initiative) { // Vérifie les armes avec bonus d'initiative
return initiative.system.score 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 initiative
return -1;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -595,6 +705,7 @@ export class TeDeumActor extends Actor {
let rollData = this.getCommonCompetence(compId) let rollData = this.getCommonCompetence(compId)
rollData.mode = "competence" rollData.mode = "competence"
rollData.title = rollData.competence.name 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") this.startRoll(rollData).catch("Error on startRoll")
} }
@@ -602,13 +713,13 @@ export class TeDeumActor extends Actor {
async rollDegatsArme(armeId) { async rollDegatsArme(armeId) {
let weapon = this.items.get(armeId) let weapon = this.items.get(armeId)
if (weapon) { if (weapon) {
let bDegats = 0 let bDegats = { value: 0 }
if ( weapon.system.typeArme == "melee" ) { if (weapon.system.typeArme == "melee") {
bDegats = this.getBonusDegats() bDegats = this.getBonusDegats()
} }
let formula = weapon.system.degats + "+" + bDegats.value let formula = weapon.system.degats + "+" + bDegats.value
let degatsRoll = await new Roll(formula).roll() 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() let rollData = this.getCommonRollData()
rollData.mode = "degats" rollData.mode = "degats"
rollData.formula = formula rollData.formula = formula
@@ -617,7 +728,7 @@ export class TeDeumActor extends Actor {
rollData.degats = degatsRoll.total rollData.degats = degatsRoll.total
let msg = await TeDeumUtility.createChatWithRollMode(rollData.alias, { 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) await msg.setFlag("world", "te-deum-rolldata", rollData)
console.log("Rolldata result", rollData) console.log("Rolldata result", rollData)
@@ -653,9 +764,14 @@ export class TeDeumActor extends Actor {
let competence = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == compName.toLowerCase()) let competence = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == compName.toLowerCase())
if (competence) { if (competence) {
rollData.competence = 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]) let c = foundry.utils.duplicate(this.system.caracteristiques[competence.system.caracteristique])
this.updateCarac(c, competence.system.caracteristique) this.updateCarac(c, competence.system.caracteristique)
rollData.carac = c 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 { } else {
ui.notifications.warn("Impossible de trouver la compétence " + compName) ui.notifications.warn("Impossible de trouver la compétence " + compName)
return return
@@ -669,8 +785,7 @@ export class TeDeumActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async startRoll(rollData) { async startRoll(rollData) {
console.log("startRoll", rollData) console.log("startRoll", rollData)
let rollDialog = await TeDeumRollDialog.create(this, rollData) await TeDeumRollDialog.create(this, rollData)
rollDialog.render(true)
} }
} }

View File

@@ -6,13 +6,14 @@ export class TeDeumCharacterCreator {
async init() { async init() {
this.stages = {} this.stages = {}
this.currentStage = "origineSociale" this.currentStage = "origineSociale"
this.sex = undefined this.sexe = undefined
this.origineSociale = undefined this.origineSociale = undefined
this.religion = undefined this.religion = undefined
this.caracBonus = {} this.caracBonus = {}
this.competenceBonus = {} this.competenceBonus = {}
this.suiviReponses = [] this.suiviReponses = []
this.competences = TeDeumUtility.getCompetencesForDropDown() this.competences = TeDeumUtility.getCompetencesForDropDown()
this.choiceSummary = {}
for (let k in game.system.tedeum.config.caracteristiques) { for (let k in game.system.tedeum.config.caracteristiques) {
this.caracBonus[k] = { value: 0 } this.caracBonus[k] = { value: 0 }
@@ -39,6 +40,7 @@ export class TeDeumCharacterCreator {
} else { } else {
this.competenceBonus[compName].value += 1 this.competenceBonus[compName].value += 1
} }
this.choiceSummary[this.currentStage].competences[compName] = 1
} }
/*--------------------------------------------*/ /*--------------------------------------------*/
@@ -116,6 +118,7 @@ export class TeDeumCharacterCreator {
/*--------------------------------------------*/ /*--------------------------------------------*/
async askQuestionnaire(stage, context) { async askQuestionnaire(stage, context) {
context.subtitle = "Questionnaire" context.subtitle = "Questionnaire"
this.choiceSummary[this.currentStage].questionnaire = {}
for (let key in stage.system.questionnaire) { for (let key in stage.system.questionnaire) {
let question = stage.system.questionnaire[key] let question = stage.system.questionnaire[key]
@@ -170,13 +173,14 @@ export class TeDeumCharacterCreator {
let compName = context.competences[context.responseKey] || selectedResponse.compName let compName = context.competences[context.responseKey] || selectedResponse.compName
this.increaseCompetence(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) { async askCompetences(stage, context) {
context.subtitle = "Choix des Compétences" context.subtitle = "Choix des Compétences"
this.choiceSummary[this.currentStage].competences = {}
context.fixedCompetences = {} context.fixedCompetences = {}
context.selectCompetences = {} context.selectCompetences = {}
@@ -273,6 +277,10 @@ export class TeDeumCharacterCreator {
/*------------- -------------------------------*/ /*------------- -------------------------------*/
async askCarac(stage, context) { async askCarac(stage, context) {
context.subtitle = "Choix des Caractéristiques" context.subtitle = "Choix des Caractéristiques"
this.choiceSummary[this.currentStage] = {
caracBonus : {},
competences : {}
}
let selected = [] let selected = []
for (let i = 0; i < stage.system.nbChoixCarac; i++) { for (let i = 0; i < stage.system.nbChoixCarac; i++) {
@@ -312,6 +320,7 @@ export class TeDeumCharacterCreator {
} }
this.caracBonus[choiceResult.carac].value += 1 this.caracBonus[choiceResult.carac].value += 1
selected.push(choiceResult.carac) selected.push(choiceResult.carac)
this.choiceSummary[this.currentStage].caracBonus[choiceResult.carac] = 1
} }
} }
@@ -360,6 +369,12 @@ export class TeDeumCharacterCreator {
for (let key in this.origineSociale.caracteristiques) { for (let key in this.origineSociale.caracteristiques) {
this.caracBonus[key].value += this.origineSociale.caracteristiques[key] 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" this.currentStage = "pouponniere"
} }
@@ -388,6 +403,7 @@ export class TeDeumCharacterCreator {
this.pouponniere = foundry.utils.duplicate(stage.items.find(item => item.id === choiceResult.selectedItem)) this.pouponniere = foundry.utils.duplicate(stage.items.find(item => item.id === choiceResult.selectedItem))
context.title = `La Pouponnière - ${this.pouponniere.name}` context.title = `La Pouponnière - ${this.pouponniere.name}`
TeDeumUtility.prepareEducationContent(this.pouponniere); TeDeumUtility.prepareEducationContent(this.pouponniere);
this.choiceSummary['pouponniere'] = {}
context.label = "Valider l'augmentation de caracteristique" context.label = "Valider l'augmentation de caracteristique"
await this.askCarac(this.pouponniere, context) await this.askCarac(this.pouponniere, context)
@@ -559,7 +575,7 @@ export class TeDeumCharacterCreator {
for (let compName in this.competenceBonus) { for (let compName in this.competenceBonus) {
let comp = actor.items.find( i => i.type == "competence" && i.name.toLowerCase() === compName.toLowerCase()) let comp = actor.items.find( i => i.type == "competence" && i.name.toLowerCase() === compName.toLowerCase())
if (comp) { 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 { } else {
toAdd.push( compName) toAdd.push( compName)
} }
@@ -581,8 +597,36 @@ export class TeDeumCharacterCreator {
await actor.update({ [`system.fortune.${this.origineSociale.cagnotteUnit}`]: newArgent}) await actor.update({ [`system.fortune.${this.origineSociale.cagnotteUnit}`]: newArgent})
let histoire = "" let histoire = ""
for (let reponse of this.suiviReponses) { for ( let key in this.choiceSummary) {
histoire += `<p>${reponse.question}<br>${reponse.reponse} (${reponse.compName})</p>` 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}) await actor.update({ "system.histoire": histoire})
actor.render(true) actor.render(true)

View File

@@ -4,18 +4,42 @@ import { TeDeumUtility } from "../common/tedeum-utility.js";
export class TeDeumCombat extends Combat { export class TeDeumCombat extends Combat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollInitiative(ids, formula = undefined, messageOptions = {} ) { async rollInitiative(ids, formula = undefined, messageOptions = {}) {
//console.log("Roll INIT !") //console.log("Roll INIT !")
ids = typeof ids === "string" ? [ids] : ids; ids = typeof ids === "string" ? [ids] : ids;
for (let cId of ids) { for (let cId of ids) {
const c = this.combatants.get(cId); const c = this.combatants.get(cId);
let initBonus = c.actor ? c.actor.getInitiativeScore( this.id, cId ) : -1; let initBonus = c.actor ? c.actor.getInitiativeScore(this.id, cId) : -1;
await this.updateEmbeddedDocuments("Combatant", [ { _id: cId, initiative: initBonus } ]); console.log("Init Bonus : ", c.name, initBonus)
await this.updateEmbeddedDocuments("Combatant", [{ _id: cId, initiative: initBonus }]);
} }
return this; 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() { static async checkTurnPosition() {
while (game.combat.turn > 0) { while (game.combat.turn > 0) {

View File

@@ -4,30 +4,46 @@ export const SYSTEM_ID = "fvtt-te-deum";
export const TEDEUM_CONFIG = { export const TEDEUM_CONFIG = {
BONUS_DEGATS: [{}, { label: "1d4", value: -2 }, { label: "1d6", value: -1 }, { label: "1d8", value: 0 }, BONUS_DEGATS: [{}, { label: "1d4", value: -2 }, { label: "1d6", value: -1 }, { label: "1d8", value: 0 },
{ label: "1d10", value: 1 }, { label: "1d12", value: 2 }, { label: "1d20", value: 3 }], { label: "1d10", value: 1 }, { label: "1d12", value: 2 }, { label: "1d20", value: 3 }],
MAX_ARMURES_LOURDES: [{}, { value: 1 }, { value: 3 }, { value: 5 }, MAX_ARMURES_LOURDES: [{}, { value: 1 }, { value: 3 }, { value: 5 },
{ value: 7 }, { value: 9 }, { value: 11 }], { value: 7 }, { value: 9 }, { value: 11 }],
ACTIONS_PAR_TOUR: [{}, { value: 1 }, { value: 2 }, { value: 2 }, ACTIONS_PAR_TOUR: [{}, { value: 1 }, { value: 2 }, { value: 2 },
{ value: 3 }, { value: 3 }, { value: 4 }], { value: 3 }, { value: 3 }, { value: 4 }],
COMMON_VALUE: [{}, { value: 1 }, { value: 2 }, { value: 3 }, COMMON_VALUE: [{}, { value: 1 }, { value: 2 }, { value: 3 },
{ value: 4 }, { value: 5 }, { value: 6 }], { value: 4 }, { value: 5 }, { value: 6 }],
COUT_XP: [{}, { value: 10 }, { value: 10 }, { value: 10 }, COUT_XP: [{}, { value: 10 }, { value: 10 }, { value: 10 },
{ value: 10 }, { value: 30 }, { value: 50 }], { value: 10 }, { value: 30 }, { value: 50 }],
LOCALISATION: { LOCALISATION: {
"pieddroit": { label: "Pied Droit", value: 1, locMod: 0, id: "pieddroit", nbArmure: 1, score: { min: 1, max: 1 }, coord: { top: 500, left: 0 } }, "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", nbArmure: 1, score: { min: 3, max: 4 }, coord: { top: 400, left: 100 } }, "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", nbArmure: 1, score: { min: 5, max: 6 }, coord: { top: 400, left: 300 } }, "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", nbArmure: 1, score: { min: 2, max: 2 }, coord: { top: 500, left: 400 } }, "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", nbArmure: 1, score: { min: 7, max: 7 }, coord: { top: 0, left: 0 } }, "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", nbArmure: 1, score: { min: 8, max: 8 }, coord: { top: 0, left: 400 } }, "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", nbArmure: 2, score: { min: 9, max: 10 }, coord: { top: 200, left: 0 } }, "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", nbArmure: 2, score: { min: 11, max: 12 }, coord: { top: 200, left: 400 } }, "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", locMod: -2, nbArmure: 2, score: { min: 13, max: 17 }, coord: { top: 200, left: 200 } }, "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", locMod: -2, nbArmure: 2, score: { min: 18, max: 20 }, coord: { top: 0, 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: { 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 }, "encombrante": { label: "Encombrante", id: "encombrante", melee: true, tir: true },
"maintiendistance": { label: "Maintien à distance", id: "maintiendistance", melee: true, tir: false }, "maintiendistance": { label: "Maintien à distance", id: "maintiendistance", melee: true, tir: false },
"coupassomant": { label: "Coup assomant", id: "coupassomant", melee: true, tir: false }, "coupassomant": { label: "Coup assomant", id: "coupassomant", melee: true, tir: false },
@@ -43,11 +59,11 @@ export const TEDEUM_CONFIG = {
}, },
ARME_PORTEES: { ARME_PORTEES: {
"brulepourpoint": { label: "Brûle-pourpoint", difficulty: "facile", id: "brulepourpoint" }, "brulepourpoint": { label: "Brûle-pourpoint (5)", difficulty: "facile", id: "brulepourpoint" },
"courte": { label: "Courte", difficulty: "pardefaut", id: "courte" }, "courte": { label: "Courte (7)", difficulty: "pardefaut", id: "courte" },
"moyenne": { label: "Moyenne", difficulty: "difficile", id: "moyenne" }, "moyenne": { label: "Moyenne (11)", difficulty: "difficile", id: "moyenne" },
"longue": { label: "Longue", difficulty: "perilleux", id: "longue" }, "longue": { label: "Longue (13)", difficulty: "perilleux", id: "longue" },
"extreme": { label: "Extrême", difficulty: "desespere", id: "extreme" }, "extreme": { label: "Extrême (15)", difficulty: "desespere", id: "extreme" },
}, },
genre: { genre: {
@@ -94,7 +110,7 @@ export const TEDEUM_CONFIG = {
}, },
caracteristiques: { 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." }, 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." }, 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." }, 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." },
@@ -102,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." }, 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: { allonges: {
courte: { courte: { malus: 0 }, moyenne: { malus: -1 }, longue: { malus: -2 }, treslongue: { malus: 0, esquive: 2 } }, 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: { malus: 0 }, moyenne: { malus: 0 }, longue: { malus: -1 }, treslongue: { 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: { malus: -2 }, moyenne: { malus: -1 }, longue: { malus: 0 }, treslongue: { malus: -1, esquive: 1 } }, 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: { malus: 0, esquive: 2 }, moyenne: { malus: 0, esquive: 2 }, longue: { malus: 0, esquive: 1 }, treslongue: { malus: 0 } }, 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: [ providence: [
{ labelM: "Brebis égarée", labelF: "Brebis égarée", value: 0, diceValue: "0" }, { labelM: "Brebis égarée", labelF: "Brebis égarée", value: 0, diceValue: "0" },
@@ -152,13 +168,13 @@ export const TEDEUM_CONFIG = {
}, },
difficulte: { difficulte: {
aucune: { label: "Aucune", key: "aucune", value: 0 }, aucune: { label: "Aucune", key: "aucune", value: 0 },
routine: { label: "Routine", key: "routine", value: 3 }, routine: { label: "Routine (3)", key: "routine", value: 3 },
facile: { label: "Facile", key: "facile", value: 5 }, facile: { label: "Facile (5)", key: "facile", value: 5 },
pardefaut: { label: "Par Défaut", key: "pardefaut", value: 7 }, pardefaut: { label: "Par Défaut (7)", key: "pardefaut", value: 7 },
malaise: { label: "Malaisé", key: "malaise", value: 9 }, malaise: { label: "Malaisé (9)", key: "malaise", value: 9 },
difficile: { label: "Difficile", key: "difficile", value: 11 }, difficile: { label: "Difficile (11)", key: "difficile", value: 11 },
perilleux: { label: "Perilleux", key: "perilleux", value: 13 }, perilleux: { label: "Perilleux (13)", key: "perilleux", value: 13 },
desespere: { label: "Désespéré", key: "desespere", value: 15 } desespere: { label: "Désespéré (15)", key: "desespere", value: 15 }
}, },
monnaie: { monnaie: {
denier: { label: "Deniers", id: "denier", value: 1 }, denier: { label: "Deniers", id: "denier", value: 1 },
@@ -195,13 +211,14 @@ export const TEDEUM_CONFIG = {
{ value: "1", label: "+1 niveau" }, { value: "1", label: "+1 niveau" },
{ value: "2", label: "+2 niveaux" } { value: "2", label: "+2 niveaux" }
], ],
blessuresOrder: ["indemne", "estafilade", "plaie", "plaiebeante", "plaieatroce", "tuenet", "tuenet", "tuenet", "tuenet", "tuenet"],
blessures: { blessures: {
indemne: { value: 0, label: "Indemne", key: "indemne", degatsMax: -1, count: 0, modifier: 0 }, 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 }, 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 }, 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 }, 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 }, 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: { virulence: {
aucune: { label: "Aucune", value: "aucune", modifier: 0 }, 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 { export class TeDeumUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -12,13 +14,19 @@ export class TeDeumUtility {
CONFIG.JournalEntry.compendiumBanner = "systems/fvtt-te-deum/images/ui/compendium_banner.webp" 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.Macro.compendiumBanner = "systems/fvtt-te-deum/images/ui/compendium_banner.webp"
CONFIG.Adventure.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) => { Hooks.on("renderActorDirectory", (app, html, data) => {
if (game.user.can('ACTOR_CREATE')) { if (game.user.can('ACTOR_CREATE')) {
const button = document.createElement('button'); 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.innerHTML = 'Créer un Personnage'
button.addEventListener('click', () => { button.addEventListener('click', () => {
let cr = new game.system.tedeum.TeDeumCharacterCreator(); let cr = new game.system.tedeum.TeDeumCharacterCreator();
@@ -27,7 +35,47 @@ export class TeDeumUtility {
$(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})` })
}
}
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -99,6 +147,14 @@ export class TeDeumUtility {
} }
return value 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 // Load compendium data
const competences = await TeDeumUtility.loadCompendium("fvtt-te-deum.competences") const competences = await TeDeumUtility.loadCompendium("fvtt-te-deum.competences")
@@ -126,14 +182,24 @@ export class TeDeumUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static welcomeMessage() { static welcomeMessage() {
if (game.user.isGM) { if (game.user.isGM) {
ChatMessage.create({ // Try to fetch the welcome message from the github repo "welcome-message-ecryme.html"
user: game.user.id, fetch(ECRYME_WELCOME_MESSAGE_URL)
whisper: [game.user.id], .then(response => response.text())
content: `<div id="chat-welcome welcome-message-tedeum"><span class="rdd-roll-part"> .then(html => {
<strong>Bienvenu dans Te Deum Pour Un Massacre !</strong> ChatMessage.create({
<div class="chat-welcome">Ce système vous est proposé par Open Sesame Games.<br> user: game.user.id,
Vous trouverez de l'aide dans @UUID[Compendium.fvtt-te-deum.aides.JournalEntry.uNwJgi4kXBCiZmAH]{Aide pour Te Deum}<br> whisper: [game.user.id],
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."
});
});
} }
} }
@@ -200,7 +266,7 @@ export class TeDeumUtility {
return actor return actor
} }
/* -------------------------------------------- */ /* -------------------------------------------- */ /* -------------------------------------------- */
static async manageOpposition(rollData) { static async manageOpposition(rollData) {
if (!this.currentOpposition) { if (!this.currentOpposition) {
// Store rollData as current GM opposition // Store rollData as current GM opposition
@@ -209,39 +275,76 @@ export class TeDeumUtility {
} else { } else {
// Perform the opposition // Perform the opposition
let isAttackWinner = true let isAttackWinner = true
let rWinner = this.currentOpposition let rWinner, rLooser
let rLooser = rollData if (this.currentOpposition.total <= rollData.total) {
if (rWinner.total < rLooser.total) { rWinner = foundry.utils.duplicate(rollData)
rWinner = rollData rLooser = foundry.utils.duplicate(this.currentOpposition)
rLooser = this.currentOpposition
isAttackWinner = false isAttackWinner = false
} else {
rWinner = foundry.utils.duplicate(this.currentOpposition)
rLooser = foundry.utils.duplicate(rollData)
isAttackWinner = true
} }
this.currentOpposition = undefined // Reset opposition this.currentOpposition = undefined // Reset opposition
let oppositionData = { let oppositionData = {
winner: rWinner, winner: rWinner,
looser: rLooser 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, { let msg = await this.createChatWithRollMode(rollData.alias, {
content: await foundry.applications.handlebars.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) await msg.setFlag("world", "te-deum-rolldata", rollData)
// Si le gagnant est l'attaquant, appliquer les dégats sur la victime // 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) { if (isAttackWinner && rWinner.isSuccess && rWinner.mode == "arme" && rWinner.arme?.system.typeArme == "melee" && rWinner.defenderTokenId) {
this.appliquerDegats(rWinner) 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) { static async appliquerDegats(rollData) {
await this.processAttaqueMelee(rollData) await this.processAttaqueMelee(rollData)
let defenderToken = canvas.tokens.placeables.find(t => t.id == rollData.defenderTokenId) let defenderActor = this.getTokenActorFromId(rollData.defenderTokenId)
if (defenderToken) { if (defenderActor) {
let actor = defenderToken.actor if (game.user.isGM || defenderActor.isOwner) {
await actor.appliquerDegats(rollData) 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 { } else {
ui.notifications.error("Impossible de trouver la cible de l'attaque, aucun degats appliqué") ui.notifications.error("Impossible de trouver la cible de l'attaque, aucun degats appliqué")
} }
@@ -266,6 +369,25 @@ export class TeDeumUtility {
TeDeumUtility.appliquerDegats(rollData, messageId) 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';
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -275,7 +397,17 @@ export class TeDeumUtility {
'systems/fvtt-te-deum/templates/actors/editor-notes-gm.hbs', '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-nav.hbs',
'systems/fvtt-te-deum/templates/items/partial-item-description.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 foundry.applications.handlebars.loadTemplates(templatePaths); return foundry.applications.handlebars.loadTemplates(templatePaths);
} }
@@ -360,6 +492,28 @@ export class TeDeumUtility {
chatMsg.setFlag("world", "tedeum-rolldata", rollData) 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é")
}
}
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -445,9 +599,11 @@ export class TeDeumUtility {
} }
if (rollData.diceSum == 1) { if (rollData.diceSum == 1) {
let critiqueRoll = await new Roll(rollData.carac.negativeDice) let critiqueRoll = await new Roll(rollData.carac.negativeDice)
rollData.isSuccess = false
await critiqueRoll.evaluate() await critiqueRoll.evaluate()
await this.showDiceSoNice(critiqueRoll, game.settings.get("core", "rollMode")) await this.showDiceSoNice(critiqueRoll, game.settings.get("core", "rollMode"))
rollData.critiqueRoll = foundry.utils.duplicate(critiqueRoll) rollData.critiqueRoll = foundry.utils.duplicate(critiqueRoll)
rollData.critiqueTotal = critiqueRoll.total
if (critiqueRoll.total > rollData.competence.system.score) { if (critiqueRoll.total > rollData.competence.system.score) {
rollData.isEchecCritique = true rollData.isEchecCritique = true
} }
@@ -476,9 +632,18 @@ export class TeDeumUtility {
if (rollData.isMouvement) { if (rollData.isMouvement) {
localModifier -= 1 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) let diceBase = this.modifyDice(rollData.carac.dice, localModifier + Number(rollData.bonusMalus) + rollData.santeModifier)
if (!diceBase) return; if (!diceBase) return;
diceFormula = diceBase + "x + " + rollData.competence.system.score diceFormula = diceBase + "x + " + rollData.compScore
} }
if (rollData.enableProvidence) { if (rollData.enableProvidence) {
diceFormula += " + " + rollData.providence.dice diceFormula += " + " + rollData.providence.dice
@@ -488,24 +653,30 @@ export class TeDeumUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async getLocalisation(rollData) { static async getLocalisation(rollData) {
let locRoll = await new Roll("1d20").roll() let locRoll
await this.showDiceSoNice(locRoll, game.settings.get("core", "rollMode")) if (rollData.loc) {
rollData.locRoll = foundry.utils.duplicate(locRoll) locRoll = await new Roll(String(rollData.loc.score.min)).roll()
for (let key in game.system.tedeum.config.LOCALISATION) { } else {
let loc = game.system.tedeum.config.LOCALISATION[key] locRoll = await new Roll("1d20").roll()
if (locRoll.total >= loc.score.min && locRoll.total <= loc.score.max) { await this.showDiceSoNice(locRoll, game.settings.get("core", "rollMode"))
rollData.loc = foundry.utils.duplicate(loc) for (let key in game.system.tedeum.config.LOCALISATION) {
break let loc = game.system.tedeum.config.LOCALISATION[key]
if (locRoll.total >= loc.score.min && locRoll.total <= loc.score.max) {
rollData.loc = foundry.utils.duplicate(loc)
break
}
} }
} }
rollData.locRoll = foundry.utils.duplicate(locRoll)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async processAttaqueMelee(rollData) { static async processAttaqueMelee(rollData) {
await this.getLocalisation(rollData) await this.getLocalisation(rollData)
let actor = game.actors.get(rollData.actorId) let actor = game.actors.get(rollData.actorId)
let bDegats = actor.getBonusDegats() let bDegats = actor.getAttaqueBonusDegats(rollData)
let degatsRoll = await new Roll(rollData.arme.system.degats + "+" + bDegats.value).roll() rollData.degatsFormula = rollData.arme.system.degats + "+" + bDegats
let degatsRoll = await new Roll(rollData.degatsFormula).roll()
await this.showDiceSoNice(degatsRoll, game.settings.get("core", "rollMode")) await this.showDiceSoNice(degatsRoll, game.settings.get("core", "rollMode"))
rollData.degatsRoll = foundry.utils.duplicate(degatsRoll) rollData.degatsRoll = foundry.utils.duplicate(degatsRoll)
rollData.degats = degatsRoll.total rollData.degats = degatsRoll.total
@@ -521,12 +692,51 @@ export class TeDeumUtility {
await this.getLocalisation(rollData) await this.getLocalisation(rollData)
// Now the degats // Now the degats
let degatsRoll = await new Roll(rollData.arme.system.degats).roll() 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.degatsRoll = foundry.utils.duplicate(degatsRoll)
rollData.degats = degatsRoll.total 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) { static async rollTeDeum(rollData) {
@@ -536,6 +746,18 @@ export class TeDeumUtility {
rollData.difficulty = "pardefaut" rollData.difficulty = "pardefaut"
} }
rollData.difficulty = game.system.tedeum.config.difficulte[rollData.difficulty].value 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) let diceFormula = this.computeRollFormula(rollData, actor)
if (!diceFormula) return; if (!diceFormula) return;
console.log("RollData", rollData, diceFormula) console.log("RollData", rollData, diceFormula)
@@ -552,6 +774,8 @@ export class TeDeumUtility {
await this.processAttaqueDistance(rollData) await this.processAttaqueDistance(rollData)
await this.manageCombatActions(actor, rollData)
let msg = await this.createChatWithRollMode(rollData.alias, { let msg = await this.createChatWithRollMode(rollData.alias, {
content: await foundry.applications.handlebars.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)
}) })
@@ -562,10 +786,6 @@ export class TeDeumUtility {
if (rollData.enableProvidence) { if (rollData.enableProvidence) {
actor.modifyProvidence(-1) actor.modifyProvidence(-1)
} }
// Manage XP
if (rollData.isReussiteCritique || rollData.isEchecCritique) {
actor.modifyXP(rollData.carac.key, 1)
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -683,7 +903,7 @@ export class TeDeumUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async confirmDelete(actorSheet, li) { 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 msgTxt = "<p>Etes vous certain de supprimer cet item ?";
let buttons = { let buttons = {
delete: { delete: {
@@ -691,7 +911,12 @@ export class TeDeumUtility {
label: "Oui, aucun souci", label: "Oui, aucun souci",
callback: () => { callback: () => {
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]); actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
li.slideUp(200, () => actorSheet.render(false)); if (li.slideUp) {
li.slideUp(200, () => actorSheet.render(false));
} else {
li.style.display = "none";
actorSheet.render(false);
}
} }
}, },
cancel: { cancel: {

View File

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

View File

@@ -4,8 +4,9 @@ export class TeDeumBlessureSchema extends foundry.abstract.TypeDataModel {
const requiredInteger = { required: true, nullable: false, integer: true }; const requiredInteger = { required: true, nullable: false, integer: true };
const schema = {}; const schema = {};
schema.typeBlessure = new fields.StringField({required: true, choices: ["indemne", "estafilade", "plaie", "plaiebeante", "plaieatroce", "tuenet"], initial: "estafilade"}); 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.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 }); 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: "" }), reponse: new fields.StringField({ required: true, blank: true, initial: "" }),
compName: new fields.StringField({ required: true, blank: true, initial: "" }), compName: new fields.StringField({ required: true, blank: true, initial: "" }),
toSelect: new fields.BooleanField({ initial: false }), 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({ comps[`comp${i}`] = new fields.SchemaField({
compName: new fields.StringField({ required: true, blank: true, initial: "" }), compName: new fields.StringField({ required: true, blank: true, initial: "" }),
}); });

View File

@@ -1,87 +1,103 @@
import { TeDeumUtility } from "../common/tedeum-utility.js"; 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) { static async create(actor, rollData) {
const dialog = new TeDeumRollDialog(actor, rollData)
let options = { classes: ["tedeum-roll-dialog"], width: 540, height: 'fit-content', 'z-index': 99999 } dialog.render(true)
let html = await renderTemplate('systems/fvtt-te-deum/templates/dialogs/roll-dialog-generic.hbs', rollData); return dialog
return new TeDeumRollDialog(actor, rollData, html, options);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
constructor(actor, rollData, html, options, close = undefined) { async _prepareContext() {
let conf = { return { ...this.rollData }
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 refreshDialog() { async refreshDialog() {
const content = await renderTemplate("systems/fvtt-te-deum/templates/dialogs/roll-dialog-generic.hbs", this.rollData) this.render()
this.data.content = content
this.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
activateListeners(html) { static #onRoll(event, target) {
super.activateListeners(html); TeDeumUtility.rollTeDeum(this.rollData)
this.close()
}
let dialog = this; /* -------------------------------------------- */
function onLoad() { static #onCancel(event, target) {
} this.close()
$(function () { onLoad(); }); }
html.find('#bonusMalusPerso').change((event) => { /* -------------------------------------------- */
_onRender(context, options) {
super._onRender(context, options)
const html = this.element
html.querySelector('#bonusMalusPerso')?.addEventListener('change', (event) => {
this.rollData.bonusMalusPerso = Number(event.currentTarget.value) 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" 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" 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 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.porteeTir = event.currentTarget.value
this.rollData.difficulty = game.system.tedeum.config.ARME_PORTEES[this.rollData.porteeTir].difficulty 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.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 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 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"; import { TeDeumUtility } from "../common/tedeum-utility.js";
const { HandlebarsApplicationMixin } = foundry.applications.api
/** /**
* Extend the basic ItemSheet with some very simple modifications * Feuille d'item Te Deum - AppV2
* @extends {ItemSheet}
*/ */
export class TeDeumItemSheet extends foundry.appv1.sheets.ItemSheet { export class TeDeumItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
/** @override */ /** @override */
static get defaultOptions() { static DEFAULT_OPTIONS = {
return foundry.utils.mergeObject(super.defaultOptions, { classes: ["fvtt-te-deum", "sheet", "item"],
classes: ["fvtt-te-deum", "sheet", "item"], position: {
template: "systems/fvtt-te-deum/templates/item-sheet.hbs",
dragDrop: [{ dragSelector: null, dropSelector: null }],
width: 620, width: 620,
height: 580, 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,
},
} }
/* -------------------------------------------- */ // Static PARTS pointing to the dynamic wrapper template
_getHeaderButtons() { static PARTS = {
let buttons = super._getHeaderButtons(); sheet: { template: "systems/fvtt-te-deum/templates/items/item-sheet.hbs" },
// 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
} }
/* -------------------------------------------- */ tabGroups = { primary: "description" }
async getData() {
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, title: this.title,
id: this.id, id: item.id,
type: this.object.type, type: item.type,
img: this.object.img, img: item.img,
name: this.object.name, name: item.name,
editable: this.isEditable, editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked", 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), config: foundry.utils.duplicate(game.system.tedeum.config),
competences: TeDeumUtility.getCompetencesForDropDown(), competences: TeDeumUtility.getCompetencesForDropDown(),
limited: this.object.limited, limited: item.limited,
options: this.options, owner: item.isOwner,
owner: this.document.isOwner, enrichedDescription: await enrich(item.system.description),
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.description, { async: true }), enrichedTransmission: await enrich(item.system.transmission),
notes: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.notes, { async: true }), enrichedSymptomes: await enrich(item.system.symptomes),
isGM: game.user.isGM 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 (item.type === "education") {
if (this.object.type == "education") { TeDeumUtility.prepareEducationContent(context)
TeDeumUtility.prepareEducationContent(formData);
} }
return 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
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
postItem() { /** @override */
let chatData = duplicate(this.item) _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) { if (this.actor) {
chatData.actor = { id: this.actor.id }; chatData.actor = { id: this.actor.id }
} }
// Don't post any image for the item (which would leave a large gap) if the default image is used if (chatData.img?.includes("/blank.png")) {
if (chatData.img.includes("/blank.png")) { chatData.img = null
chatData.img = null;
} }
// JSON object for easy creation chatData.jsondata = JSON.stringify({ compendium: "postedItem", payload: chatData })
chatData.jsondata = JSON.stringify( const html = await foundry.applications.handlebars.renderTemplate(
{ 'systems/fvtt-te-deum/templates/post-item.html', chatData
compendium: "postedItem", )
payload: chatData, ChatMessage.create(TeDeumUtility.chatDataSetup(html))
});
renderTemplate('systems/fvtt-te-deum/templates/post-item.html', chatData).then(html => {
let chatOptions = TeDeumUtility.chatDataSetup(html);
ChatMessage.create(chatOptions)
});
} }
/* -------------------------------------------- */ static async #onDeleteSubitem(event, target) {
async viewSubitem(ev) { const field = target.dataset.type
let levelIndex = Number($(ev.currentTarget).parents(".item").data("level-index")) const idx = parseInt(target.dataset.index)
let choiceIndex = Number($(ev.currentTarget).parents(".item").data("choice-index")) const oldArray = this.document.system[field]
let featureId = $(ev.currentTarget).parents(".item").data("feature-id") if (Array.isArray(oldArray) && oldArray[idx]?.name !== 'None') {
const newArray = oldArray.filter((_, i) => i !== idx)
let itemData = this.object.system.levels[levelIndex].choices[choiceIndex].features[featureId] this.document.update({ [`system.${field}`]: newArray })
if (itemData.name != 'None') {
let item = await Item.create(itemData, { temporary: true });
item.system.origin = "embeddedItem";
new TeDeumItemSheet(item).render(true);
} }
} }
/* -------------------------------------------- */ static async #onViewSubitem(event, target) {
async deleteSubitem(ev) { const li = target.closest(".item")
let field = $(ev.currentTarget).data('type'); const levelIndex = parseInt(li?.dataset.levelIndex)
let idx = Number($(ev.currentTarget).data('index')); const choiceIndex = parseInt(li?.dataset.choiceIndex)
let oldArray = this.object.system[field]; const featureId = li?.dataset.featureId
let itemData = this.object.system[field][idx]; const itemData = this.document.system.levels?.[levelIndex]?.choices?.[choiceIndex]?.features?.[featureId]
if (itemData.name != 'None') { if (itemData?.name !== 'None') {
let newArray = []; const item = await Item.create(itemData, { temporary: true })
for (let i = 0; i < oldArray.length; i++) { item.system.origin = "embeddedItem"
if (i != idx) { new TeDeumItemSheet(item).render(true)
newArray.push(oldArray[i]);
}
}
this.object.update({ [`system.${field}`]: newArray });
} }
} }
/* -------------------------------------------- */ // #endregion
/** @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)
}
} }

View File

@@ -90,6 +90,8 @@ Hooks.once("init", async function () {
foundry.documents.collections.Items.registerSheet("fvtt-te-deum", TeDeumItemSheet, { makeDefault: true }); foundry.documents.collections.Items.registerSheet("fvtt-te-deum", TeDeumItemSheet, { makeDefault: true });
TeDeumUtility.init() TeDeumUtility.init()
TeDeumUtility.installHooks()
}); });
@@ -98,6 +100,7 @@ Hooks.once("init", async function () {
/* -------------------------------------------- */ /* -------------------------------------------- */
Hooks.once("ready", function () { Hooks.once("ready", function () {
// User warning // User warning
if (!game.user.isGM && game.user.character == undefined) { if (!game.user.isGM && game.user.character == undefined) {
ui.notifications.info("Attention ! Aucun personnage relié au joueur !"); 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) console.log("ClassCounter loaded", moduleCounter)
moduleCounter.ClassCounter.registerUsageCount() moduleCounter.ClassCounter.registerUsageCount()
}).catch(err=> }).catch(err =>
console.log("No stats available, giving up.") console.log("No stats available, giving up.")
) )
TeDeumUtility.ready(); TeDeumUtility.ready();

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"
}

View File

@@ -1 +1 @@
MANIFEST-000099 MANIFEST-000257

View File

@@ -1,14 +1,7 @@
2025/05/09-10:15:41.735148 7fbe907f86c0 Recovering log #97 2026/04/01-22:27:12.261733 7f3054fed6c0 Recovering log #255
2025/05/09-10:15:41.791781 7fbe907f86c0 Delete type=3 #95 2026/04/01-22:27:12.271772 7f3054fed6c0 Delete type=3 #253
2025/05/09-10:15:41.791840 7fbe907f86c0 Delete type=0 #97 2026/04/01-22:27:12.271827 7f3054fed6c0 Delete type=0 #255
2025/05/09-10:26:09.013435 7fbe8fbff6c0 Level-0 table #102: started 2026/04/01-22:29:12.741245 7f303effd6c0 Level-0 table #260: started
2025/05/09-10:26:09.031920 7fbe8fbff6c0 Level-0 table #102: 3728 bytes OK 2026/04/01-22:29:12.741276 7f303effd6c0 Level-0 table #260: 0 bytes OK
2025/05/09-10:26:09.069119 7fbe8fbff6c0 Delete type=0 #100 2026/04/01-22:29:12.747569 7f303effd6c0 Delete type=0 #258
2025/05/09-10:26:09.069442 7fbe8fbff6c0 Manual compaction at level-0 from '!journal!uNwJgi4kXBCiZmAH' @ 72057594037927935 : 1 .. '!journal.pages!uNwJgi4kXBCiZmAH.onhNU0mXhOpdNZJF' @ 0 : 0; will stop at '!journal.pages!uNwJgi4kXBCiZmAH.onhNU0mXhOpdNZJF' @ 25 : 1 2026/04/01-22:29:12.758955 7f303effd6c0 Manual compaction at level-0 from '!journal!uNwJgi4kXBCiZmAH' @ 72057594037927935 : 1 .. '!journal.pages!uNwJgi4kXBCiZmAH.onhNU0mXhOpdNZJF' @ 0 : 0; will stop at (end)
2025/05/09-10:26:09.069450 7fbe8fbff6c0 Compacting 1@0 + 1@1 files
2025/05/09-10:26:09.093063 7fbe8fbff6c0 Generated table #103@0: 5 keys, 3728 bytes
2025/05/09-10:26:09.093094 7fbe8fbff6c0 Compacted 1@0 + 1@1 files => 3728 bytes
2025/05/09-10:26:09.137637 7fbe8fbff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/05/09-10:26:09.137778 7fbe8fbff6c0 Delete type=2 #72
2025/05/09-10:26:09.137982 7fbe8fbff6c0 Delete type=2 #102
2025/05/09-10:26:09.254090 7fbe8fbff6c0 Manual compaction at level-0 from '!journal.pages!uNwJgi4kXBCiZmAH.onhNU0mXhOpdNZJF' @ 25 : 1 .. '!journal.pages!uNwJgi4kXBCiZmAH.onhNU0mXhOpdNZJF' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/04/20-09:24:48.041836 7fa413fff6c0 Recovering log #93 2026/03/05-21:15:22.235257 7f78e3fff6c0 Recovering log #251
2025/04/20-09:24:48.069770 7fa413fff6c0 Delete type=3 #91 2026/03/05-21:15:22.291402 7f78e3fff6c0 Delete type=3 #249
2025/04/20-09:24:48.069934 7fa413fff6c0 Delete type=0 #93 2026/03/05-21:15:22.291561 7f78e3fff6c0 Delete type=0 #251
2025/04/20-09:25:06.962777 7fa4127fc6c0 Level-0 table #98: started 2026/03/05-21:15:43.404511 7f78e15b46c0 Level-0 table #256: started
2025/04/20-09:25:06.962842 7fa4127fc6c0 Level-0 table #98: 0 bytes OK 2026/03/05-21:15:43.404555 7f78e15b46c0 Level-0 table #256: 0 bytes OK
2025/04/20-09:25:06.970328 7fa4127fc6c0 Delete type=0 #96 2026/03/05-21:15:43.411486 7f78e15b46c0 Delete type=0 #254
2025/04/20-09:25:06.988174 7fa4127fc6c0 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:43.418602 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-000257 Normal file

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000202 MANIFEST-000335

View File

@@ -1,14 +1,7 @@
2025/05/09-10:15:41.297396 7fbe90ff96c0 Recovering log #200 2026/04/01-22:27:12.168731 7f303f7fe6c0 Recovering log #333
2025/05/09-10:15:41.362108 7fbe90ff96c0 Delete type=3 #198 2026/04/01-22:27:12.178191 7f303f7fe6c0 Delete type=3 #331
2025/05/09-10:15:41.362318 7fbe90ff96c0 Delete type=0 #200 2026/04/01-22:27:12.178249 7f303f7fe6c0 Delete type=0 #333
2025/05/09-10:26:08.357066 7fbe8fbff6c0 Level-0 table #205: started 2026/04/01-22:29:12.681047 7f303effd6c0 Level-0 table #338: started
2025/05/09-10:26:08.362656 7fbe8fbff6c0 Level-0 table #205: 30743 bytes OK 2026/04/01-22:29:12.681084 7f303effd6c0 Level-0 table #338: 0 bytes OK
2025/05/09-10:26:08.375920 7fbe8fbff6c0 Delete type=0 #203 2026/04/01-22:29:12.688203 7f303effd6c0 Delete type=0 #336
2025/05/09-10:26:08.439160 7fbe8fbff6c0 Manual compaction at level-0 from '!folders!InCQeTRdT5jXMX82' @ 72057594037927935 : 1 .. '!items!wxIHkrq98eQ3cOvp' @ 0 : 0; will stop at '!items!wxIHkrq98eQ3cOvp' @ 73 : 1 2026/04/01-22:29:12.694822 7f303effd6c0 Manual compaction at level-0 from '!folders!InCQeTRdT5jXMX82' @ 72057594037927935 : 1 .. '!items!wxIHkrq98eQ3cOvp' @ 0 : 0; will stop at (end)
2025/05/09-10:26:08.439169 7fbe8fbff6c0 Compacting 1@0 + 1@1 files
2025/05/09-10:26:08.445806 7fbe8fbff6c0 Generated table #206@0: 38 keys, 31247 bytes
2025/05/09-10:26:08.445836 7fbe8fbff6c0 Compacted 1@0 + 1@1 files => 31247 bytes
2025/05/09-10:26:08.458685 7fbe8fbff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/05/09-10:26:08.458808 7fbe8fbff6c0 Delete type=2 #197
2025/05/09-10:26:08.458974 7fbe8fbff6c0 Delete type=2 #205
2025/05/09-10:26:08.486140 7fbe8fbff6c0 Manual compaction at level-0 from '!items!wxIHkrq98eQ3cOvp' @ 73 : 1 .. '!items!wxIHkrq98eQ3cOvp' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,5 @@
2025/04/20-09:24:47.881295 7fa412ffd6c0 Recovering log #195 2026/03/05-21:15:21.839495 7f7930fff6c0 Delete type=3 #329
2025/04/20-09:24:47.897306 7fa412ffd6c0 Delete type=3 #193 2026/03/05-21:15:43.331720 7f78e15b46c0 Level-0 table #334: started
2025/04/20-09:24:47.897426 7fa412ffd6c0 Delete type=0 #195 2026/03/05-21:15:43.331839 7f78e15b46c0 Level-0 table #334: 0 bytes OK
2025/04/20-09:25:06.915266 7fa4127fc6c0 Level-0 table #201: started 2026/03/05-21:15:43.338988 7f78e15b46c0 Delete type=0 #332
2025/04/20-09:25:06.915317 7fa4127fc6c0 Level-0 table #201: 0 bytes OK 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)
2025/04/20-09:25:06.922256 7fa4127fc6c0 Delete type=0 #199
2025/04/20-09:25:06.929081 7fa4127fc6c0 Manual compaction at level-0 from '!folders!InCQeTRdT5jXMX82' @ 72057594037927935 : 1 .. '!items!wxIHkrq98eQ3cOvp' @ 0 : 0; will stop at (end)

Binary file not shown.

BIN
packs/armes/MANIFEST-000335 Normal file

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000201 MANIFEST-000360

View File

@@ -1,14 +1,7 @@
2025/05/09-10:15:41.365049 7fbe917fa6c0 Recovering log #199 2026/04/01-22:27:12.181232 7f3054fed6c0 Recovering log #358
2025/05/09-10:15:41.456977 7fbe917fa6c0 Delete type=3 #197 2026/04/01-22:27:12.191622 7f3054fed6c0 Delete type=3 #356
2025/05/09-10:15:41.457096 7fbe917fa6c0 Delete type=0 #199 2026/04/01-22:27:12.191685 7f3054fed6c0 Delete type=0 #358
2025/05/09-10:26:08.314346 7fbe8fbff6c0 Level-0 table #204: started 2026/04/01-22:29:12.674602 7f303effd6c0 Level-0 table #363: started
2025/05/09-10:26:08.321855 7fbe8fbff6c0 Level-0 table #204: 11921 bytes OK 2026/04/01-22:29:12.674625 7f303effd6c0 Level-0 table #363: 0 bytes OK
2025/05/09-10:26:08.334418 7fbe8fbff6c0 Delete type=0 #202 2026/04/01-22:29:12.680869 7f303effd6c0 Delete type=0 #361
2025/05/09-10:26:08.402084 7fbe8fbff6c0 Manual compaction at level-0 from '!folders!2wTJBj3dicRKzNOE' @ 72057594037927935 : 1 .. '!items!ufvhWG5V8pX0qrtR' @ 0 : 0; will stop at '!items!ufvhWG5V8pX0qrtR' @ 54 : 1 2026/04/01-22:29:12.694811 7f303effd6c0 Manual compaction at level-0 from '!folders!2wTJBj3dicRKzNOE' @ 72057594037927935 : 1 .. '!items!ufvhWG5V8pX0qrtR' @ 0 : 0; will stop at (end)
2025/05/09-10:26:08.402106 7fbe8fbff6c0 Compacting 1@0 + 1@1 files
2025/05/09-10:26:08.407755 7fbe8fbff6c0 Generated table #205@0: 29 keys, 12111 bytes
2025/05/09-10:26:08.407809 7fbe8fbff6c0 Compacted 1@0 + 1@1 files => 12111 bytes
2025/05/09-10:26:08.419957 7fbe8fbff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/05/09-10:26:08.420095 7fbe8fbff6c0 Delete type=2 #174
2025/05/09-10:26:08.420237 7fbe8fbff6c0 Delete type=2 #204
2025/05/09-10:26:08.486117 7fbe8fbff6c0 Manual compaction at level-0 from '!items!ufvhWG5V8pX0qrtR' @ 54 : 1 .. '!items!ufvhWG5V8pX0qrtR' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/04/20-09:24:47.903860 7fa413fff6c0 Recovering log #195 2026/03/05-21:15:21.843260 7f78e2ffd6c0 Recovering log #354
2025/04/20-09:24:47.919442 7fa413fff6c0 Delete type=3 #193 2026/03/05-21:15:21.929269 7f78e2ffd6c0 Delete type=3 #352
2025/04/20-09:24:47.919592 7fa413fff6c0 Delete type=0 #195 2026/03/05-21:15:21.929400 7f78e2ffd6c0 Delete type=0 #354
2025/04/20-09:25:06.908159 7fa4127fc6c0 Level-0 table #200: started 2026/03/05-21:15:43.345888 7f78e15b46c0 Level-0 table #359: started
2025/04/20-09:25:06.908228 7fa4127fc6c0 Level-0 table #200: 0 bytes OK 2026/03/05-21:15:43.345931 7f78e15b46c0 Level-0 table #359: 0 bytes OK
2025/04/20-09:25:06.915011 7fa4127fc6c0 Delete type=0 #198 2026/03/05-21:15:43.352934 7f78e15b46c0 Delete type=0 #357
2025/04/20-09:25:06.929064 7fa4127fc6c0 Manual compaction at level-0 from '!folders!2wTJBj3dicRKzNOE' @ 72057594037927935 : 1 .. '!items!ufvhWG5V8pX0qrtR' @ 0 : 0; will stop at (end) 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)

Binary file not shown.

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000198 MANIFEST-000358

View File

@@ -1,14 +1,7 @@
2025/05/09-10:15:41.212131 7fbe907f86c0 Recovering log #196 2026/04/01-22:27:12.153529 7f3054fed6c0 Recovering log #356
2025/05/09-10:15:41.294357 7fbe907f86c0 Delete type=3 #194 2026/04/01-22:27:12.165448 7f3054fed6c0 Delete type=3 #354
2025/05/09-10:15:41.294415 7fbe907f86c0 Delete type=0 #196 2026/04/01-22:27:12.165520 7f3054fed6c0 Delete type=0 #356
2025/05/09-10:26:08.334569 7fbe8fbff6c0 Level-0 table #201: started 2026/04/01-22:29:12.688301 7f303effd6c0 Level-0 table #361: started
2025/05/09-10:26:08.342225 7fbe8fbff6c0 Level-0 table #201: 38203 bytes OK 2026/04/01-22:29:12.688354 7f303effd6c0 Level-0 table #361: 0 bytes OK
2025/05/09-10:26:08.356875 7fbe8fbff6c0 Delete type=0 #199 2026/04/01-22:29:12.694630 7f303effd6c0 Delete type=0 #359
2025/05/09-10:26:08.420303 7fbe8fbff6c0 Manual compaction at level-0 from '!folders!4OPhigzcPv46qbWW' @ 72057594037927935 : 1 .. '!items!yx4k7lQHGcom99mk' @ 0 : 0; will stop at '!items!yx4k7lQHGcom99mk' @ 237 : 1 2026/04/01-22:29:12.694832 7f303effd6c0 Manual compaction at level-0 from '!folders!4OPhigzcPv46qbWW' @ 72057594037927935 : 1 .. '!items!yx4k7lQHGcom99mk' @ 0 : 0; will stop at (end)
2025/05/09-10:26:08.420310 7fbe8fbff6c0 Compacting 1@0 + 1@1 files
2025/05/09-10:26:08.426825 7fbe8fbff6c0 Generated table #202@0: 116 keys, 38485 bytes
2025/05/09-10:26:08.426858 7fbe8fbff6c0 Compacted 1@0 + 1@1 files => 38485 bytes
2025/05/09-10:26:08.438808 7fbe8fbff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/05/09-10:26:08.438945 7fbe8fbff6c0 Delete type=2 #171
2025/05/09-10:26:08.439090 7fbe8fbff6c0 Delete type=2 #201
2025/05/09-10:26:08.486130 7fbe8fbff6c0 Manual compaction at level-0 from '!items!yx4k7lQHGcom99mk' @ 237 : 1 .. '!items!yx4k7lQHGcom99mk' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/04/20-09:24:47.857871 7fa4137fe6c0 Recovering log #192 2026/03/05-21:15:21.593711 7f78e3fff6c0 Recovering log #352
2025/04/20-09:24:47.873833 7fa4137fe6c0 Delete type=3 #190 2026/03/05-21:15:21.647801 7f78e3fff6c0 Delete type=3 #350
2025/04/20-09:24:47.873948 7fa4137fe6c0 Delete type=0 #192 2026/03/05-21:15:21.647916 7f78e3fff6c0 Delete type=0 #352
2025/04/20-09:25:06.901277 7fa4127fc6c0 Level-0 table #197: started 2026/03/05-21:15:43.353206 7f78e15b46c0 Level-0 table #357: started
2025/04/20-09:25:06.901384 7fa4127fc6c0 Level-0 table #197: 0 bytes OK 2026/03/05-21:15:43.353251 7f78e15b46c0 Level-0 table #357: 0 bytes OK
2025/04/20-09:25:06.907939 7fa4127fc6c0 Delete type=0 #195 2026/03/05-21:15:43.359989 7f78e15b46c0 Delete type=0 #355
2025/04/20-09:25:06.929038 7fa4127fc6c0 Manual compaction at level-0 from '!folders!4OPhigzcPv46qbWW' @ 72057594037927935 : 1 .. '!items!yx4k7lQHGcom99mk' @ 0 : 0; will stop at (end) 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)

Binary file not shown.

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000210 MANIFEST-000370

View File

@@ -1,14 +1,7 @@
2025/05/09-10:15:41.459323 7fbe907f86c0 Recovering log #208 2026/04/01-22:27:12.194066 7f303ffff6c0 Recovering log #368
2025/05/09-10:15:41.503379 7fbe907f86c0 Delete type=3 #206 2026/04/01-22:27:12.203689 7f303ffff6c0 Delete type=3 #366
2025/05/09-10:15:41.503446 7fbe907f86c0 Delete type=0 #208 2026/04/01-22:27:12.203767 7f303ffff6c0 Delete type=0 #368
2025/05/09-10:26:08.376101 7fbe8fbff6c0 Level-0 table #213: started 2026/04/01-22:29:12.668494 7f303effd6c0 Level-0 table #373: started
2025/05/09-10:26:08.388425 7fbe8fbff6c0 Level-0 table #213: 263867 bytes OK 2026/04/01-22:29:12.668535 7f303effd6c0 Level-0 table #373: 0 bytes OK
2025/05/09-10:26:08.401652 7fbe8fbff6c0 Delete type=0 #211 2026/04/01-22:29:12.674505 7f303effd6c0 Delete type=0 #371
2025/05/09-10:26:08.459044 7fbe8fbff6c0 Manual compaction at level-0 from '!folders!9PQi3Lv54rpcxavo' @ 72057594037927935 : 1 .. '!items!zGlRtP7zSnkjuuue' @ 0 : 0; will stop at '!items!zGlRtP7zSnkjuuue' @ 510 : 1 2026/04/01-22:29:12.694794 7f303effd6c0 Manual compaction at level-0 from '!folders!9PQi3Lv54rpcxavo' @ 72057594037927935 : 1 .. '!items!zGlRtP7zSnkjuuue' @ 0 : 0; will stop at (end)
2025/05/09-10:26:08.459053 7fbe8fbff6c0 Compacting 1@0 + 1@1 files
2025/05/09-10:26:08.472966 7fbe8fbff6c0 Generated table #214@0: 71 keys, 264215 bytes
2025/05/09-10:26:08.472996 7fbe8fbff6c0 Compacted 1@0 + 1@1 files => 264215 bytes
2025/05/09-10:26:08.485587 7fbe8fbff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/05/09-10:26:08.485765 7fbe8fbff6c0 Delete type=2 #205
2025/05/09-10:26:08.485988 7fbe8fbff6c0 Delete type=2 #213
2025/05/09-10:26:08.486148 7fbe8fbff6c0 Manual compaction at level-0 from '!items!zGlRtP7zSnkjuuue' @ 510 : 1 .. '!items!zGlRtP7zSnkjuuue' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/04/20-09:24:47.923568 7fa418ffa6c0 Recovering log #203 2026/03/05-21:15:21.932980 7f78e3fff6c0 Recovering log #364
2025/04/20-09:24:47.940475 7fa418ffa6c0 Delete type=3 #201 2026/03/05-21:15:21.994263 7f78e3fff6c0 Delete type=3 #362
2025/04/20-09:24:47.940632 7fa418ffa6c0 Delete type=0 #203 2026/03/05-21:15:21.994412 7f78e3fff6c0 Delete type=0 #364
2025/04/20-09:25:06.922414 7fa4127fc6c0 Level-0 table #209: started 2026/03/05-21:15:43.360527 7f78e15b46c0 Level-0 table #369: started
2025/04/20-09:25:06.922447 7fa4127fc6c0 Level-0 table #209: 0 bytes OK 2026/03/05-21:15:43.360574 7f78e15b46c0 Level-0 table #369: 0 bytes OK
2025/04/20-09:25:06.928838 7fa4127fc6c0 Delete type=0 #207 2026/03/05-21:15:43.367363 7f78e15b46c0 Delete type=0 #367
2025/04/20-09:25:06.929095 7fa4127fc6c0 Manual compaction at level-0 from '!folders!9PQi3Lv54rpcxavo' @ 72057594037927935 : 1 .. '!items!zGlRtP7zSnkjuuue' @ 0 : 0; will stop at (end) 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)

Binary file not shown.

Binary file not shown.

View File

View File

@@ -1 +1 @@
MANIFEST-000201 MANIFEST-000359

View File

@@ -1,14 +1,7 @@
2025/05/09-10:15:41.507118 7fbe91ffb6c0 Recovering log #199 2026/04/01-22:27:12.208436 7f3054fed6c0 Recovering log #357
2025/05/09-10:15:41.563016 7fbe91ffb6c0 Delete type=3 #197 2026/04/01-22:27:12.218569 7f3054fed6c0 Delete type=3 #355
2025/05/09-10:15:41.563136 7fbe91ffb6c0 Delete type=0 #199 2026/04/01-22:27:12.218625 7f3054fed6c0 Delete type=0 #357
2025/05/09-10:26:08.486247 7fbe8fbff6c0 Level-0 table #204: started 2026/04/01-22:29:12.701378 7f303effd6c0 Level-0 table #362: started
2025/05/09-10:26:08.492712 7fbe8fbff6c0 Level-0 table #204: 20052 bytes OK 2026/04/01-22:29:12.701404 7f303effd6c0 Level-0 table #362: 0 bytes OK
2025/05/09-10:26:08.521869 7fbe8fbff6c0 Delete type=0 #202 2026/04/01-22:29:12.708014 7f303effd6c0 Delete type=0 #360
2025/05/09-10:26:08.657886 7fbe8fbff6c0 Manual compaction at level-0 from '!items!17mjvwS8R3B6LloG' @ 72057594037927935 : 1 .. '!items!zUYIVOuFpRur9aAR' @ 0 : 0; will stop at '!items!zUYIVOuFpRur9aAR' @ 109 : 1 2026/04/01-22:29:12.724717 7f303effd6c0 Manual compaction at level-0 from '!items!17mjvwS8R3B6LloG' @ 72057594037927935 : 1 .. '!items!zUYIVOuFpRur9aAR' @ 0 : 0; will stop at (end)
2025/05/09-10:26:08.657901 7fbe8fbff6c0 Compacting 1@0 + 1@1 files
2025/05/09-10:26:08.675906 7fbe8fbff6c0 Generated table #205@0: 49 keys, 20052 bytes
2025/05/09-10:26:08.675933 7fbe8fbff6c0 Compacted 1@0 + 1@1 files => 20052 bytes
2025/05/09-10:26:08.712163 7fbe8fbff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/05/09-10:26:08.712328 7fbe8fbff6c0 Delete type=2 #196
2025/05/09-10:26:08.712603 7fbe8fbff6c0 Delete type=2 #204
2025/05/09-10:26:08.869429 7fbe8fbff6c0 Manual compaction at level-0 from '!items!zUYIVOuFpRur9aAR' @ 109 : 1 .. '!items!zUYIVOuFpRur9aAR' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/04/20-09:24:47.948681 7fa4137fe6c0 Recovering log #194 2026/03/05-21:15:22.000326 7f78e37fe6c0 Recovering log #353
2025/04/20-09:24:47.965382 7fa4137fe6c0 Delete type=3 #192 2026/03/05-21:15:22.058297 7f78e37fe6c0 Delete type=3 #351
2025/04/20-09:24:47.965487 7fa4137fe6c0 Delete type=0 #194 2026/03/05-21:15:22.058416 7f78e37fe6c0 Delete type=0 #353
2025/04/20-09:25:06.935620 7fa4127fc6c0 Level-0 table #200: started 2026/03/05-21:15:43.339182 7f78e15b46c0 Level-0 table #358: started
2025/04/20-09:25:06.935652 7fa4127fc6c0 Level-0 table #200: 0 bytes OK 2026/03/05-21:15:43.339230 7f78e15b46c0 Level-0 table #358: 0 bytes OK
2025/04/20-09:25:06.942921 7fa4127fc6c0 Delete type=0 #198 2026/03/05-21:15:43.345648 7f78e15b46c0 Delete type=0 #356
2025/04/20-09:25:06.956261 7fa4127fc6c0 Manual compaction at level-0 from '!items!17mjvwS8R3B6LloG' @ 72057594037927935 : 1 .. '!items!zUYIVOuFpRur9aAR' @ 0 : 0; will stop at (end) 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)

Binary file not shown.

Binary file not shown.

View File

View File

View File

@@ -1 +1 @@
MANIFEST-000201 MANIFEST-000359

View File

@@ -1,14 +1,7 @@
2025/05/09-10:15:41.566104 7fbe917fa6c0 Recovering log #199 2026/04/01-22:27:12.221189 7f303ffff6c0 Recovering log #357
2025/05/09-10:15:41.607068 7fbe917fa6c0 Delete type=3 #197 2026/04/01-22:27:12.231482 7f303ffff6c0 Delete type=3 #355
2025/05/09-10:15:41.607127 7fbe917fa6c0 Delete type=0 #199 2026/04/01-22:27:12.231538 7f303ffff6c0 Delete type=0 #357
2025/05/09-10:26:08.522102 7fbe8fbff6c0 Level-0 table #204: started 2026/04/01-22:29:12.717614 7f303effd6c0 Level-0 table #362: started
2025/05/09-10:26:08.533326 7fbe8fbff6c0 Level-0 table #204: 11517 bytes OK 2026/04/01-22:29:12.717636 7f303effd6c0 Level-0 table #362: 0 bytes OK
2025/05/09-10:26:08.560180 7fbe8fbff6c0 Delete type=0 #202 2026/04/01-22:29:12.724605 7f303effd6c0 Delete type=0 #360
2025/05/09-10:26:08.712861 7fbe8fbff6c0 Manual compaction at level-0 from '!items!1icaxIywAwDXQcMz' @ 72057594037927935 : 1 .. '!items!ysGehYm1VkMWrI22' @ 0 : 0; will stop at '!items!ysGehYm1VkMWrI22' @ 71 : 1 2026/04/01-22:29:12.734278 7f303effd6c0 Manual compaction at level-0 from '!items!1icaxIywAwDXQcMz' @ 72057594037927935 : 1 .. '!items!ysGehYm1VkMWrI22' @ 0 : 0; will stop at (end)
2025/05/09-10:26:08.712889 7fbe8fbff6c0 Compacting 1@0 + 1@1 files
2025/05/09-10:26:08.728740 7fbe8fbff6c0 Generated table #205@0: 17 keys, 11517 bytes
2025/05/09-10:26:08.728771 7fbe8fbff6c0 Compacted 1@0 + 1@1 files => 11517 bytes
2025/05/09-10:26:08.762648 7fbe8fbff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/05/09-10:26:08.762817 7fbe8fbff6c0 Delete type=2 #196
2025/05/09-10:26:08.762965 7fbe8fbff6c0 Delete type=2 #204
2025/05/09-10:26:08.869440 7fbe8fbff6c0 Manual compaction at level-0 from '!items!ysGehYm1VkMWrI22' @ 71 : 1 .. '!items!ysGehYm1VkMWrI22' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/04/20-09:24:47.972234 7fa412ffd6c0 Recovering log #194 2026/03/05-21:15:22.062292 7f7930fff6c0 Recovering log #353
2025/04/20-09:24:47.989602 7fa412ffd6c0 Delete type=3 #192 2026/03/05-21:15:22.114101 7f7930fff6c0 Delete type=3 #351
2025/04/20-09:24:47.989720 7fa412ffd6c0 Delete type=0 #194 2026/03/05-21:15:22.114232 7f7930fff6c0 Delete type=0 #353
2025/04/20-09:25:06.929262 7fa4127fc6c0 Level-0 table #200: started 2026/03/05-21:15:43.382516 7f78e15b46c0 Level-0 table #358: started
2025/04/20-09:25:06.929306 7fa4127fc6c0 Level-0 table #200: 0 bytes OK 2026/03/05-21:15:43.382559 7f78e15b46c0 Level-0 table #358: 0 bytes OK
2025/04/20-09:25:06.935454 7fa4127fc6c0 Delete type=0 #198 2026/03/05-21:15:43.389985 7f78e15b46c0 Delete type=0 #356
2025/04/20-09:25:06.956241 7fa4127fc6c0 Manual compaction at level-0 from '!items!1icaxIywAwDXQcMz' @ 72057594037927935 : 1 .. '!items!ysGehYm1VkMWrI22' @ 0 : 0; will stop at (end) 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)

Binary file not shown.

Binary file not shown.

View File

View File

Binary file not shown.

BIN
packs/scenes/000300.ldb Normal file

Binary file not shown.

View File

@@ -1 +1 @@
MANIFEST-000138 MANIFEST-000296

View File

@@ -1,14 +1,14 @@
2025/05/09-10:15:41.675033 7fbe91ffb6c0 Recovering log #136 2026/04/01-22:27:12.247784 7f30557ee6c0 Recovering log #294
2025/05/09-10:15:41.730910 7fbe91ffb6c0 Delete type=3 #134 2026/04/01-22:27:12.257759 7f30557ee6c0 Delete type=3 #292
2025/05/09-10:15:41.730997 7fbe91ffb6c0 Delete type=0 #136 2026/04/01-22:27:12.257889 7f30557ee6c0 Delete type=0 #294
2025/05/09-10:26:08.601281 7fbe8fbff6c0 Level-0 table #141: started 2026/04/01-22:29:12.708136 7f303effd6c0 Level-0 table #299: started
2025/05/09-10:26:08.620048 7fbe8fbff6c0 Level-0 table #141: 1344 bytes OK 2026/04/01-22:29:12.711418 7f303effd6c0 Level-0 table #299: 1499 bytes OK
2025/05/09-10:26:08.657679 7fbe8fbff6c0 Delete type=0 #139 2026/04/01-22:29:12.717517 7f303effd6c0 Delete type=0 #297
2025/05/09-10:26:08.818209 7fbe8fbff6c0 Manual compaction at level-0 from '!scenes!FJXugdbkBpEJEdR6' @ 72057594037927935 : 1 .. '!scenes!FJXugdbkBpEJEdR6' @ 0 : 0; will stop at '!scenes!FJXugdbkBpEJEdR6' @ 5 : 1 2026/04/01-22:29:12.724733 7f303effd6c0 Manual compaction at level-0 from '!scenes!FJXugdbkBpEJEdR6' @ 72057594037927935 : 1 .. '!scenes.levels!FJXugdbkBpEJEdR6.defaultLevel0000' @ 0 : 0; will stop at '!scenes.levels!FJXugdbkBpEJEdR6.defaultLevel0000' @ 6 : 1
2025/05/09-10:26:08.818224 7fbe8fbff6c0 Compacting 1@0 + 1@1 files 2026/04/01-22:29:12.724742 7f303effd6c0 Compacting 1@0 + 1@1 files
2025/05/09-10:26:08.836037 7fbe8fbff6c0 Generated table #142@0: 1 keys, 1344 bytes 2026/04/01-22:29:12.727922 7f303effd6c0 Generated table #300@0: 2 keys, 1499 bytes
2025/05/09-10:26:08.836067 7fbe8fbff6c0 Compacted 1@0 + 1@1 files => 1344 bytes 2026/04/01-22:29:12.727956 7f303effd6c0 Compacted 1@0 + 1@1 files => 1499 bytes
2025/05/09-10:26:08.869141 7fbe8fbff6c0 compacted to: files[ 0 1 0 0 0 0 0 ] 2026/04/01-22:29:12.733918 7f303effd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/05/09-10:26:08.869252 7fbe8fbff6c0 Delete type=2 #111 2026/04/01-22:29:12.734034 7f303effd6c0 Delete type=2 #275
2025/05/09-10:26:08.869366 7fbe8fbff6c0 Delete type=2 #141 2026/04/01-22:29:12.734201 7f303effd6c0 Delete type=2 #299
2025/05/09-10:26:08.869479 7fbe8fbff6c0 Manual compaction at level-0 from '!scenes!FJXugdbkBpEJEdR6' @ 5 : 1 .. '!scenes!FJXugdbkBpEJEdR6' @ 0 : 0; will stop at (end) 2026/04/01-22:29:12.747711 7f303effd6c0 Manual compaction at level-0 from '!scenes.levels!FJXugdbkBpEJEdR6.defaultLevel0000' @ 6 : 1 .. '!scenes.levels!FJXugdbkBpEJEdR6.defaultLevel0000' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/04/20-09:24:48.018535 7fa418ffa6c0 Recovering log #132 2026/03/05-21:15:22.179895 7f78e37fe6c0 Recovering log #290
2025/04/20-09:24:48.034352 7fa418ffa6c0 Delete type=3 #130 2026/03/05-21:15:22.230205 7f78e37fe6c0 Delete type=3 #288
2025/04/20-09:24:48.034446 7fa418ffa6c0 Delete type=0 #132 2026/03/05-21:15:22.230319 7f78e37fe6c0 Delete type=0 #290
2025/04/20-09:25:06.943076 7fa4127fc6c0 Level-0 table #137: started 2026/03/05-21:15:43.367589 7f78e15b46c0 Level-0 table #295: started
2025/04/20-09:25:06.943110 7fa4127fc6c0 Level-0 table #137: 0 bytes OK 2026/03/05-21:15:43.367631 7f78e15b46c0 Level-0 table #295: 0 bytes OK
2025/04/20-09:25:06.949678 7fa4127fc6c0 Delete type=0 #135 2026/03/05-21:15:43.375171 7f78e15b46c0 Delete type=0 #293
2025/04/20-09:25:06.956277 7fa4127fc6c0 Manual compaction at level-0 from '!scenes!FJXugdbkBpEJEdR6' @ 72057594037927935 : 1 .. '!scenes!FJXugdbkBpEJEdR6' @ 0 : 0; will stop at (end) 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)

Binary file not shown.

Binary file not shown.

View File

View File

View File

@@ -1 +1 @@
MANIFEST-000201 MANIFEST-000360

View File

@@ -1,14 +1,7 @@
2025/05/09-10:15:41.609264 7fbe90ff96c0 Recovering log #199 2026/04/01-22:27:12.234129 7f3054fed6c0 Recovering log #358
2025/05/09-10:15:41.672478 7fbe90ff96c0 Delete type=3 #197 2026/04/01-22:27:12.243647 7f3054fed6c0 Delete type=3 #356
2025/05/09-10:15:41.672535 7fbe90ff96c0 Delete type=0 #199 2026/04/01-22:27:12.243734 7f3054fed6c0 Delete type=0 #358
2025/05/09-10:26:08.560441 7fbe8fbff6c0 Level-0 table #204: started 2026/04/01-22:29:12.694943 7f303effd6c0 Level-0 table #363: started
2025/05/09-10:26:08.570942 7fbe8fbff6c0 Level-0 table #204: 21176 bytes OK 2026/04/01-22:29:12.694977 7f303effd6c0 Level-0 table #363: 0 bytes OK
2025/05/09-10:26:08.601073 7fbe8fbff6c0 Delete type=0 #202 2026/04/01-22:29:12.701267 7f303effd6c0 Delete type=0 #361
2025/05/09-10:26:08.763048 7fbe8fbff6c0 Manual compaction at level-0 from '!items!1bAL2MQVpVBd0c5Z' @ 72057594037927935 : 1 .. '!items!zs67k4sxCid6oTK3' @ 0 : 0; will stop at '!items!zs67k4sxCid6oTK3' @ 80 : 1 2026/04/01-22:29:12.724704 7f303effd6c0 Manual compaction at level-0 from '!items!1bAL2MQVpVBd0c5Z' @ 72057594037927935 : 1 .. '!items!zs67k4sxCid6oTK3' @ 0 : 0; will stop at (end)
2025/05/09-10:26:08.763056 7fbe8fbff6c0 Compacting 1@0 + 1@1 files
2025/05/09-10:26:08.781378 7fbe8fbff6c0 Generated table #205@0: 36 keys, 21176 bytes
2025/05/09-10:26:08.781419 7fbe8fbff6c0 Compacted 1@0 + 1@1 files => 21176 bytes
2025/05/09-10:26:08.817648 7fbe8fbff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/05/09-10:26:08.817810 7fbe8fbff6c0 Delete type=2 #196
2025/05/09-10:26:08.818060 7fbe8fbff6c0 Delete type=2 #204
2025/05/09-10:26:08.869448 7fbe8fbff6c0 Manual compaction at level-0 from '!items!zs67k4sxCid6oTK3' @ 80 : 1 .. '!items!zs67k4sxCid6oTK3' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2025/04/20-09:24:47.993946 7fa413fff6c0 Recovering log #194 2026/03/05-21:15:22.117891 7f78e3fff6c0 Recovering log #354
2025/04/20-09:24:48.011487 7fa413fff6c0 Delete type=3 #192 2026/03/05-21:15:22.174616 7f78e3fff6c0 Delete type=3 #352
2025/04/20-09:24:48.011668 7fa413fff6c0 Delete type=0 #194 2026/03/05-21:15:22.174732 7f78e3fff6c0 Delete type=0 #354
2025/04/20-09:25:06.949829 7fa4127fc6c0 Level-0 table #200: started 2026/03/05-21:15:43.375415 7f78e15b46c0 Level-0 table #359: started
2025/04/20-09:25:06.949863 7fa4127fc6c0 Level-0 table #200: 0 bytes OK 2026/03/05-21:15:43.375459 7f78e15b46c0 Level-0 table #359: 0 bytes OK
2025/04/20-09:25:06.956072 7fa4127fc6c0 Delete type=0 #198 2026/03/05-21:15:43.382270 7f78e15b46c0 Delete type=0 #357
2025/04/20-09:25:06.956290 7fa4127fc6c0 Manual compaction at level-0 from '!items!1bAL2MQVpVBd0c5Z' @ 72057594037927935 : 1 .. '!items!zs67k4sxCid6oTK3' @ 0 : 0; will stop at (end) 2026/03/05-21:15:43.390261 7f78e15b46c0 Manual compaction at level-0 from '!items!1bAL2MQVpVBd0c5Z' @ 72057594037927935 : 1 .. '!items!zs67k4sxCid6oTK3' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

File diff suppressed because it is too large Load Diff

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