Compare commits
27 Commits
fvtt-waste
...
13.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| c36f2d0116 | |||
| 690293c1c8 | |||
| 04e35228e4 | |||
| ec2d5385c5 | |||
| 438caf3b1c | |||
| 627ccc707b | |||
| fdf28c4978 | |||
| e0eac58bc9 | |||
| b463323fbe | |||
| bddf772c99 | |||
| a716a3b3d1 | |||
| 5814ef41df | |||
| c0fcbe278f | |||
| 94f7ef8f90 | |||
| 19409dd547 | |||
| 217df7ee10 | |||
| 9990545568 | |||
| d2da332411 | |||
| 028e8bddac | |||
| 5c889a5153 | |||
| 68689add33 | |||
| 4ed2bcd2ee | |||
| 0edf336d28 | |||
| 046cdf4fb2 | |||
| cc0faec25e | |||
| 3419ddf8d6 | |||
| 5aa117b569 |
63
.gitea/workflows/release.yaml
Normal file
63
.gitea/workflows/release.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Release Creation
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner."
|
||||
|
||||
#- uses: actions/checkout@v3
|
||||
- uses: RouxAntoine/checkout@v3.5.4
|
||||
|
||||
# get part of the tag after the `v`
|
||||
- name: Extract tag version number
|
||||
id: get_version
|
||||
uses: battila7/get-version-action@v2
|
||||
|
||||
# Substitute the Manifest and Download URLs in the system.json
|
||||
- name: Substitute Manifest and Download Links For Versioned Ones
|
||||
id: sub_manifest_link_version
|
||||
uses: microsoft/variable-substitution@v1
|
||||
with:
|
||||
files: 'system.json'
|
||||
env:
|
||||
version: ${{steps.get_version.outputs.version-without-v}}
|
||||
url: https://www.uberwald.me/gitea/${{gitea.repository}}
|
||||
manifest: https://www.uberwald.me/gitea/public/fvtt-wasteland/releases/download/latest/system.json
|
||||
download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-wasteland.zip
|
||||
|
||||
# Create a zip file with all files required by the module to add to the release
|
||||
- run: |
|
||||
apt update -y
|
||||
apt install -y zip
|
||||
|
||||
- run: zip -r ./fvtt-wasteland.zip system.json README.md changelog.md assets/ lang/ modules/ packs/ styles/ templates/ template.json
|
||||
|
||||
- name: setup go
|
||||
uses: https://github.com/actions/setup-go@v4
|
||||
with:
|
||||
go-version: '>=1.20.1'
|
||||
|
||||
- name: Use Go Action
|
||||
id: use-go-action
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
files: |-
|
||||
./fvtt-wasteland.zip
|
||||
system.json
|
||||
api_key: '${{secrets.ALLOW_PUSH_RELEASE}}'
|
||||
|
||||
- name: Publish to Foundry server
|
||||
uses: https://github.com/djlechuck/foundryvtt-publish-package-action@v1
|
||||
with:
|
||||
token: ${{ secrets.FOUNDRYVTT_RELEASE_TOKEN }}
|
||||
id: 'fvtt-wasteland'
|
||||
version: ${{github.event.release.tag_name}}
|
||||
manifest: 'https://www.uberwald.me/gitea/public/fvtt-wasteland/releases/download/latest/system.json'
|
||||
notes: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-wasteland.zip'
|
||||
compatibility-minimum: '13'
|
||||
compatibility-verified: '13'
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
.history/
|
||||
node_modules
|
||||
|
||||
12
README.md
12
README.md
@@ -1,11 +1,14 @@
|
||||
# Système Foundry pour Wasteland (French RPG, Titam France/Sombres Projets)
|
||||
|
||||
Système Foundry pour Wasteland (French RPG, Titam France/Sombres Projets)
|
||||
|
||||
## EN
|
||||
|
||||
Unofficial system for Wasteland (French version from Titam France).
|
||||
Unofficial system for Wasteland (French RPG from Titam France).
|
||||
|
||||
Books are mandatory to play and are available at : http://www.titam-france.fr
|
||||
|
||||
``
|
||||
|
||||
## FR
|
||||
|
||||
Système non-officiel pour le JDR Wasteland (Titam France).
|
||||
@@ -16,8 +19,9 @@ Les livres du jeu sont nécessaires pour jouer, et sont disponibles ici : http:/
|
||||
|
||||
# Credits
|
||||
|
||||
Wasteland, le jeu de rôle de Sword & Sorcery, is a property of Titam France/Sombres Projets.
|
||||
Wasteland is a property of Titam France/Sombres Projets.
|
||||
|
||||
# Developmement
|
||||
|
||||
LeRatierBretonnien
|
||||
Code, CSS and automations : LeRatierBretonnien
|
||||
Compendiums : Pretre, LeRatierBretonnien
|
||||
|
||||
BIN
assets/icons/don.webp
Normal file
BIN
assets/icons/don.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
assets/icons/hubris.webp
Normal file
BIN
assets/icons/hubris.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
8
changelog.md
Normal file
8
changelog.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# 12.0.1
|
||||
|
||||
- Fix v12 version
|
||||
|
||||
# 11.0.22
|
||||
|
||||
- Version initiale
|
||||
-
|
||||
35
gulpfile.js
Normal file
35
gulpfile.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const gulp = require('gulp');
|
||||
const less = require('gulp-less');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
|
||||
// Paths
|
||||
const paths = {
|
||||
styles: {
|
||||
src: 'less/**/*.less',
|
||||
dest: 'styles/'
|
||||
}
|
||||
};
|
||||
|
||||
// Compile LESS to CSS
|
||||
function styles() {
|
||||
return gulp.src('less/wasteland.less')
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(less())
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest(paths.styles.dest));
|
||||
}
|
||||
|
||||
// Watch files
|
||||
function watchFiles() {
|
||||
gulp.watch(paths.styles.src, styles);
|
||||
}
|
||||
|
||||
// Define complex tasks
|
||||
const build = gulp.series(styles);
|
||||
const watch = gulp.series(build, watchFiles);
|
||||
|
||||
// Export tasks
|
||||
exports.styles = styles;
|
||||
exports.build = build;
|
||||
exports.watch = watch;
|
||||
exports.default = build;
|
||||
45
lang/fr.json
45
lang/fr.json
@@ -1,24 +1,27 @@
|
||||
{
|
||||
"ACTOR": {
|
||||
"TypePersonnage": "Personnage",
|
||||
"TypePNJ": "PNJ"
|
||||
},
|
||||
|
||||
"ITEM": {
|
||||
"TypeArme": "Arme",
|
||||
"TypeCompetence": "Compétence",
|
||||
"TypeProtection": "Protection",
|
||||
"TypeMonnaie": "Monnaie",
|
||||
"TypeEquipement": "Equipement",
|
||||
"TypeCapacite": "Capacité",
|
||||
"TypeOrigine": "Origine",
|
||||
"TypeHeritage": "Héritage",
|
||||
"TypeMetier": "Métier",
|
||||
"TypeBouclier": "Bouclier",
|
||||
"TypePouvoir": "Pouvoir",
|
||||
"TypeArtifex": "Artifex",
|
||||
"TypeMutation": "Mutation",
|
||||
"TypeCharme": "Charme",
|
||||
"TypePeuple": "Peuple"
|
||||
"TYPES": {
|
||||
"Item": {
|
||||
"arme": "Arme",
|
||||
"competence": "Compétence",
|
||||
"protection": "Protection",
|
||||
"monnaie": "Monnaie",
|
||||
"equipement": "Equipement",
|
||||
"don": "Don",
|
||||
"hubris": "Hubris",
|
||||
"capacite": "Capacité",
|
||||
"origine": "Origine",
|
||||
"heritage": "Héritage",
|
||||
"metier": "Métier",
|
||||
"bouclier": "Bouclier",
|
||||
"pouvoir": "Pouvoir",
|
||||
"artifex": "Artifex",
|
||||
"mutation": "Mutation",
|
||||
"charme": "Charme",
|
||||
"peuple": "Peuple"
|
||||
},
|
||||
"Actor": {
|
||||
"personnage": "Personnage",
|
||||
"creature": "Créature"
|
||||
}
|
||||
}
|
||||
}
|
||||
1201
less/actor-styles.less
Normal file
1201
less/actor-styles.less
Normal file
File diff suppressed because it is too large
Load Diff
859
less/chat-styles.less
Normal file
859
less/chat-styles.less
Normal file
@@ -0,0 +1,859 @@
|
||||
/* ============================================ */
|
||||
/* WASTELAND CHAT MESSAGE STYLES */
|
||||
/* Post-Apocalyptic Theme */
|
||||
/* ============================================ */
|
||||
|
||||
.wasteland-chat-result {
|
||||
background: linear-gradient(135deg, rgba(50, 40, 30, 0.95) 0%, rgba(30, 25, 20, 0.95) 100%);
|
||||
border: 2px solid #8b7355;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
font-family: "Charlemagne", serif;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
position: relative;
|
||||
|
||||
// Effet de texture sale/usée
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
rgba(0, 0, 0, 0.03) 2px,
|
||||
rgba(0, 0, 0, 0.03) 4px
|
||||
);
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.chat-result-header {
|
||||
background: linear-gradient(135deg, #3d2f1f 0%, #2a1f15 100%);
|
||||
border-bottom: 2px solid #8b7355;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
position: relative;
|
||||
box-shadow: inset 0 -2px 4px rgba(0, 0, 0, 0.4);
|
||||
|
||||
// Effet rouille sur le bord
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
#6a0606 20%,
|
||||
#8b7355 40%,
|
||||
#6a0606 60%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.actor-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 2px;
|
||||
border: 2px solid #8b7355;
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
|
||||
filter: contrast(1.1) saturate(0.9);
|
||||
}
|
||||
|
||||
.header-info {
|
||||
flex: 1;
|
||||
|
||||
.actor-name {
|
||||
margin: 0;
|
||||
color: #e8dcc4;
|
||||
font-size: 1.1rem;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.9);
|
||||
font-family: "Charlemagne", serif;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.action-title {
|
||||
color: #c9a86a;
|
||||
font-size: 0.9rem;
|
||||
margin-top: 0.125rem;
|
||||
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.9);
|
||||
font-style: italic;
|
||||
|
||||
i {
|
||||
margin-right: 0.25rem;
|
||||
color: #8b7355;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.result-main {
|
||||
background: linear-gradient(180deg, rgba(230, 220, 200, 0.9) 0%, rgba(210, 200, 180, 0.9) 100%);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-bottom: 1px solid rgba(139, 115, 85, 0.5);
|
||||
position: relative;
|
||||
|
||||
&.damage {
|
||||
background: linear-gradient(180deg, rgba(200, 180, 160, 0.95) 0%, rgba(180, 160, 140, 0.95) 100%);
|
||||
}
|
||||
|
||||
.result-display {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.1875rem;
|
||||
|
||||
.dice-result,
|
||||
.total-result,
|
||||
.difficulty {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
background: linear-gradient(180deg, rgba(50, 40, 30, 0.7) 0%, rgba(40, 30, 20, 0.8) 100%);
|
||||
padding: 0.25rem 0.375rem;
|
||||
border-radius: 2px;
|
||||
border: 2px solid #8b7355;
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
i {
|
||||
color: #c9a86a;
|
||||
font-size: 1rem;
|
||||
display: block;
|
||||
margin-bottom: 0.125rem;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dice-value,
|
||||
.total-value,
|
||||
.difficulty-value {
|
||||
font-size: 1.5rem;
|
||||
color: #e8dcc4;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.9);
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.total-label,
|
||||
.difficulty-label {
|
||||
font-size: 0.75rem;
|
||||
color: #c9a86a;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
line-height: 1.1;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.damage-display {
|
||||
.damage-total {
|
||||
text-align: center;
|
||||
background: linear-gradient(180deg, rgba(60, 50, 40, 0.8) 0%, rgba(50, 40, 30, 0.9) 100%);
|
||||
padding: 0.5rem;
|
||||
border-radius: 2px;
|
||||
border: 2px solid #8b7355;
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.6), 0 2px 4px rgba(0, 0, 0, 0.4);
|
||||
|
||||
i {
|
||||
color: #c9a86a;
|
||||
font-size: 1.2rem;
|
||||
display: block;
|
||||
margin-bottom: 0.25rem;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.damage-label {
|
||||
display: block;
|
||||
font-size: 0.85rem;
|
||||
color: #c9a86a;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.25rem;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.damage-value {
|
||||
display: block;
|
||||
font-size: 2rem;
|
||||
color: #e8dcc4;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.9), 0 0 8px rgba(200, 0, 0, 0.3);
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.result-badge-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 0.25rem;
|
||||
|
||||
.result-badge {
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 2px;
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
border: 1px solid rgba(0, 0, 0, 0.4);
|
||||
|
||||
i {
|
||||
margin-right: 0.375rem;
|
||||
}
|
||||
|
||||
&.heroique {
|
||||
background: linear-gradient(135deg, #c9a86a 0%, #8b7355 100%);
|
||||
color: #1a1a1a;
|
||||
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.5);
|
||||
border-color: #8b7355;
|
||||
}
|
||||
|
||||
&.success {
|
||||
background: linear-gradient(135deg, #6b8e23 0%, #4a6017 100%);
|
||||
color: #e8dcc4;
|
||||
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.8);
|
||||
border-color: #4a6017;
|
||||
}
|
||||
|
||||
&.failure {
|
||||
background: linear-gradient(135deg, #5a4a3a 0%, #3a2a1a 100%);
|
||||
color: #c9a86a;
|
||||
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.9);
|
||||
border-color: #3a2a1a;
|
||||
}
|
||||
|
||||
&.dramatique {
|
||||
background: linear-gradient(135deg, #4a0404 0%, #2a0202 100%);
|
||||
color: #e8dcc4;
|
||||
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.9);
|
||||
border-color: #6a0606;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.result-details {
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(180deg, rgba(70, 60, 50, 0.6) 0%, rgba(60, 50, 40, 0.7) 100%);
|
||||
border-top: 1px solid rgba(139, 115, 85, 0.3);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
|
||||
.details-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.1875rem;
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.1875rem 0.375rem;
|
||||
background: linear-gradient(90deg, rgba(230, 220, 200, 0.6) 0%, rgba(210, 200, 180, 0.5) 100%);
|
||||
border-radius: 1px;
|
||||
font-size: 0.85rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
border-left: 2px solid #8b7355;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
|
||||
&.bonus {
|
||||
background: linear-gradient(90deg, rgba(200, 220, 180, 0.7) 0%, rgba(180, 200, 160, 0.6) 100%);
|
||||
border-left-color: #6b8e23;
|
||||
}
|
||||
|
||||
&.malus {
|
||||
background: linear-gradient(90deg, rgba(220, 180, 160, 0.7) 0%, rgba(200, 160, 140, 0.6) 100%);
|
||||
border-left-color: #8b4513;
|
||||
}
|
||||
|
||||
&.rune {
|
||||
background: linear-gradient(90deg, rgba(190, 180, 210, 0.7) 0%, rgba(170, 160, 190, 0.6) 100%);
|
||||
border-left-color: #6a5acd;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
color: #2a1f15;
|
||||
font-weight: bold;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
color: #2a1f15;
|
||||
font-weight: 500;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.result-effects {
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(180deg, rgba(80, 70, 60, 0.5) 0%, rgba(70, 60, 50, 0.6) 100%);
|
||||
border-top: 1px solid rgba(139, 115, 85, 0.4);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
|
||||
.effect-success,
|
||||
.effect-warning,
|
||||
.effect-failure,
|
||||
.effect-damage,
|
||||
.effect-heroic {
|
||||
padding: 0.3125rem 0.5rem;
|
||||
margin-bottom: 0.3125rem;
|
||||
background: linear-gradient(90deg, rgba(230, 220, 200, 0.8) 0%, rgba(210, 200, 180, 0.7) 100%);
|
||||
border-radius: 1px;
|
||||
border-left: 3px solid;
|
||||
font-size: 0.85rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
|
||||
i {
|
||||
margin-right: 0.375rem;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.effect-success {
|
||||
border-left-color: #6b8e23;
|
||||
color: #2a1f15;
|
||||
}
|
||||
|
||||
.effect-heroic {
|
||||
border-left-color: #c9a86a;
|
||||
color: #2a1f15;
|
||||
font-weight: bold;
|
||||
background: linear-gradient(90deg, rgba(201, 168, 106, 0.3) 0%, rgba(230, 220, 200, 0.8) 100%);
|
||||
}
|
||||
|
||||
.effect-warning {
|
||||
border-left-color: #d97706;
|
||||
color: #2a1f15;
|
||||
}
|
||||
|
||||
.effect-failure {
|
||||
border-left-color: #8b4513;
|
||||
color: #2a1f15;
|
||||
}
|
||||
|
||||
.effect-damage {
|
||||
border-left-color: #6a0606;
|
||||
color: #2a1f15;
|
||||
background: linear-gradient(90deg, rgba(200, 100, 100, 0.3) 0%, rgba(210, 200, 180, 0.7) 100%);
|
||||
}
|
||||
}
|
||||
|
||||
.damage-button-section {
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(180deg, rgba(100, 70, 60, 0.5) 0%, rgba(80, 60, 50, 0.6) 100%);
|
||||
border-top: 1px solid rgba(139, 115, 85, 0.5);
|
||||
|
||||
.chat-card-button {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(to bottom, #6a0606 0%, #4a0404 100%);
|
||||
border: 2px solid #8b7355;
|
||||
border-radius: 2px;
|
||||
color: #e8dcc4;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
font-family: "Charlemagne", serif;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to bottom, #8a0808 0%, #5a0505 100%);
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
border-color: #c9a86a;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.predilection-reroll-section {
|
||||
padding: 0.75rem;
|
||||
background: linear-gradient(135deg, rgba(139, 115, 85, 0.3) 0%, rgba(100, 80, 60, 0.4) 100%);
|
||||
border: 2px solid #8b7355;
|
||||
border-radius: 2px;
|
||||
margin: 0.5rem;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
// Cacher toute icône de dé qui pourrait apparaître par erreur
|
||||
.fa-dice {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.predilection-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid rgba(139, 115, 85, 0.5);
|
||||
|
||||
i {
|
||||
color: #c9a86a;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.predilection-title {
|
||||
color: #e8dcc4;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
font-family: "Charlemagne", serif;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.predilection-results {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
|
||||
.predilection-roll {
|
||||
flex: 1;
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(90deg, rgba(230, 220, 200, 0.4) 0%, rgba(210, 200, 180, 0.3) 100%);
|
||||
border-radius: 2px;
|
||||
border-left: 2px solid #8b7355;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
position: relative;
|
||||
|
||||
&.kept {
|
||||
background: linear-gradient(90deg, rgba(200, 220, 180, 0.6) 0%, rgba(180, 200, 160, 0.5) 100%);
|
||||
border-left-color: #6b8e23;
|
||||
box-shadow: 0 0 8px rgba(107, 142, 35, 0.3);
|
||||
}
|
||||
|
||||
.roll-label {
|
||||
color: #2a1f15;
|
||||
font-weight: bold;
|
||||
font-size: 0.75rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
.roll-value {
|
||||
color: #2a1f15;
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
i.fa-check-circle {
|
||||
color: #6b8e23;
|
||||
font-size: 1.1rem;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.predilection-kept {
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(90deg, rgba(200, 220, 180, 0.7) 0%, rgba(180, 200, 160, 0.6) 100%);
|
||||
border: 1px solid #6b8e23;
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
color: #2a1f15;
|
||||
font-size: 0.95rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
i {
|
||||
color: #6b8e23;
|
||||
}
|
||||
|
||||
.kept-label {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.predilection-section {
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(180deg, rgba(100, 90, 70, 0.5) 0%, rgba(80, 70, 60, 0.6) 100%);
|
||||
border-top: 1px solid rgba(139, 115, 85, 0.5);
|
||||
|
||||
.chat-card-button {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(to bottom, #5a4a3a 0%, #3a2a1a 100%);
|
||||
border: 2px solid #8b7355;
|
||||
border-radius: 2px;
|
||||
color: #e8dcc4;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
font-family: "Charlemagne", serif;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to bottom, #6a5a4a 0%, #4a3a2a 100%);
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
border-color: #c9a86a;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================ */
|
||||
/* WASTELAND ITEM CHAT POSTS */
|
||||
/* ============================================ */
|
||||
|
||||
.wasteland-chat-item {
|
||||
background: linear-gradient(135deg, rgba(50, 40, 30, 0.95) 0%, rgba(30, 25, 20, 0.95) 100%);
|
||||
border: 2px solid #8b7355;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
font-family: "Charlemagne", serif;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
position: relative;
|
||||
|
||||
// Effet de texture sale/usée
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
rgba(0, 0, 0, 0.03) 2px,
|
||||
rgba(0, 0, 0, 0.03) 4px
|
||||
);
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.chat-item-header {
|
||||
background: linear-gradient(135deg, #3d2f1f 0%, #2a1f15 100%);
|
||||
border-bottom: 2px solid #8b7355;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
position: relative;
|
||||
box-shadow: inset 0 -2px 4px rgba(0, 0, 0, 0.4);
|
||||
|
||||
// Effet rouille sur le bord
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
#6a0606 20%,
|
||||
#8b7355 40%,
|
||||
#6a0606 60%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 2px;
|
||||
border: 2px solid #8b7355;
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
|
||||
filter: contrast(1.1) saturate(0.9);
|
||||
}
|
||||
|
||||
.header-info {
|
||||
flex: 1;
|
||||
|
||||
.item-name {
|
||||
margin: 0;
|
||||
color: #e8dcc4;
|
||||
font-size: 1.1rem;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.9);
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.item-type {
|
||||
margin-top: 0.25rem;
|
||||
color: #c9a86a;
|
||||
font-size: 0.85rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
text-transform: capitalize;
|
||||
|
||||
i {
|
||||
color: #8b7355;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-item-body {
|
||||
padding: 0.75rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
.item-description {
|
||||
color: #e8dcc4;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 0.75rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
||||
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-properties {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid rgba(139, 115, 85, 0.3);
|
||||
|
||||
.property {
|
||||
background: linear-gradient(90deg, rgba(230, 220, 200, 0.6) 0%, rgba(210, 200, 180, 0.5) 100%);
|
||||
padding: 0.375rem 0.5rem;
|
||||
border-radius: 1px;
|
||||
border-left: 2px solid #8b7355;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.property-label {
|
||||
color: #2a1f15;
|
||||
font-weight: bold;
|
||||
font-size: 0.85rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
.property-value {
|
||||
color: #2a1f15;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================================ */
|
||||
/* WASTELAND WELCOME MESSAGE */
|
||||
/* ============================================ */
|
||||
|
||||
.wasteland-welcome-message {
|
||||
background: linear-gradient(135deg, rgba(61, 47, 31, 0.15) 0%, rgba(42, 31, 21, 0.2) 100%);
|
||||
border: 2px solid #6a0606;
|
||||
border-radius: 8px;
|
||||
padding: 0;
|
||||
margin: 8px 0;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4),
|
||||
inset 0 1px 0 rgba(201, 168, 106, 0.1);
|
||||
font-family: "Charlemagne", serif;
|
||||
|
||||
.welcome-header {
|
||||
background: linear-gradient(135deg, #6a0606 0%, #4a0404 100%);
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
border-bottom: 2px solid #c9a86a;
|
||||
position: relative;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
.welcome-icon {
|
||||
font-size: 1.8rem;
|
||||
color: #c9a86a;
|
||||
margin-bottom: 4px;
|
||||
text-shadow: 0 0 10px rgba(201, 168, 106, 0.5);
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.welcome-title {
|
||||
margin: 4px 0 2px 0;
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: #e8dcc4;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
|
||||
font-family: "Charlemagne", serif;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.welcome-subtitle {
|
||||
font-size: 0.9rem;
|
||||
color: #c9a86a;
|
||||
font-style: italic;
|
||||
margin-top: 2px;
|
||||
line-height: 1.2;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-content {
|
||||
padding: 12px;
|
||||
background: linear-gradient(180deg, rgba(230, 220, 200, 0.9) 0%, rgba(210, 200, 180, 0.85) 100%);
|
||||
|
||||
.welcome-section {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 4px;
|
||||
border: 1px solid #c9a86a;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
flex-shrink: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #6a0606 0%, #4a0404 100%);
|
||||
color: #c9a86a;
|
||||
border-radius: 50%;
|
||||
font-size: 1rem;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
i {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.section-text {
|
||||
flex: 1;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
|
||||
strong {
|
||||
display: block;
|
||||
color: #3d2f1f;
|
||||
margin-bottom: 4px;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
color: #2a1f15;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.welcome-link {
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
color: #6a0606;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s ease;
|
||||
font-size: 0.9rem;
|
||||
|
||||
i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #8b0606;
|
||||
text-shadow: 0 0 4px rgba(106, 6, 6, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-footer {
|
||||
background: linear-gradient(135deg, #4a0404 0%, #6a0606 100%);
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
color: #c9a86a;
|
||||
font-style: italic;
|
||||
font-size: 0.95rem;
|
||||
border-top: 1px solid #8b7355;
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
font-family: "Charlemagne", serif;
|
||||
|
||||
i {
|
||||
margin: 0 8px;
|
||||
opacity: 0.7;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
}
|
||||
569
less/item-styles.less
Normal file
569
less/item-styles.less
Normal file
@@ -0,0 +1,569 @@
|
||||
/* ==================== Item Sheet Styles ==================== */
|
||||
|
||||
/* Item header with image and name */
|
||||
.fvtt-wasteland.item {
|
||||
/* Background pour toute la fiche d'item */
|
||||
background: url("../assets/ui/pc_sheet_bg.webp") repeat;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
/* AppV2 - Remove window content padding */
|
||||
.window-content {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* AppV2 - Main section structure */
|
||||
section {
|
||||
background: url("../assets/ui/pc_sheet_bg.webp") repeat-y;
|
||||
color: black;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* AppV2 Item Sheets - Disabled inputs readability */
|
||||
input:disabled,
|
||||
select:disabled {
|
||||
color: #000000;
|
||||
opacity: 0.8;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* Inputs and selects styling */
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
select {
|
||||
color: #000000;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid #999999;
|
||||
margin: 0;
|
||||
padding: 2px 4px;
|
||||
font-family: "Charlemagne", serif;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
textarea {
|
||||
margin: 0;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin: 0 4px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex: 0 0 auto;
|
||||
border-bottom: 1px solid #999;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sheet-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.5rem;
|
||||
background: url("../assets/ui/pc_sheet_bg.webp") repeat;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.item-sheet-img {
|
||||
flex: 0 0 100px;
|
||||
height: 100px;
|
||||
border: 2px solid #999;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.item-sheet-title {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 1.5rem;
|
||||
border-bottom: none;
|
||||
|
||||
input {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
font-family: "Charlemagne", serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.item-subtitle {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
/* Navigation tabs - Modern style */
|
||||
nav.tabs {
|
||||
display: flex;
|
||||
border-bottom: 2px solid #403f3e;
|
||||
margin: 0;
|
||||
padding: 4px 8px;
|
||||
background: linear-gradient(to bottom, #2a2520 0%, #1a1510 100%);
|
||||
flex: 0 0 auto;
|
||||
gap: 4px;
|
||||
|
||||
a.item {
|
||||
padding: 8px 16px;
|
||||
color: rgba(218, 218, 218, 0.85);
|
||||
text-decoration: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 6px 6px 0 0;
|
||||
font-family: "Charlemagne", serif;
|
||||
font-size: 0.9rem;
|
||||
font-weight: normal;
|
||||
transition: all 0.3s ease;
|
||||
background: rgba(64, 63, 62, 0.3);
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
|
||||
i {
|
||||
display: none; // Hide icons for cleaner look
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(74, 4, 4, 0.4);
|
||||
color: #f5f5f5;
|
||||
border-color: rgba(218, 218, 218, 0.2);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: linear-gradient(to bottom, #4a0404 0%, #3a0303 100%);
|
||||
border: 1px solid transparent;
|
||||
color: #f5f5f5;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Tab content */
|
||||
.tab {
|
||||
display: none;
|
||||
padding: 8px 12px;
|
||||
overflow-y: auto;
|
||||
flex: 1 1 auto;
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sheet body - scrollable content */
|
||||
.sheet-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 1rem;
|
||||
|
||||
&[data-tab] {
|
||||
display: none;
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dans l'onglet details, les form-group sont horizontaux par défaut */
|
||||
&[data-tab="details"] {
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
color: #464331;
|
||||
flex: 0 0 auto;
|
||||
min-width: 160px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
select,
|
||||
textarea {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
flex: 0 0 auto;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
/* Checkbox avec label après (pas avant) */
|
||||
&:has(input[type="checkbox"]:first-child) {
|
||||
label {
|
||||
min-width: auto;
|
||||
flex: 1;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Exception: quand le label contient lui-même le checkbox */
|
||||
label:has(input[type="checkbox"]) {
|
||||
min-width: auto;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pour les sections avec grilles, garder le comportement vertical */
|
||||
.grid .form-group {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.25rem;
|
||||
|
||||
label {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Form groups - comportement par défaut pour autres onglets */
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
color: #464331;
|
||||
}
|
||||
|
||||
&.horizontal {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
label {
|
||||
flex: 0 0 auto;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
input, select, textarea {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Grid layouts */
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
margin: 0.5rem 0;
|
||||
|
||||
&.grid-2col {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
&.grid-3col {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
&.grid-4col {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Editor content */
|
||||
.editor-content {
|
||||
min-height: 200px;
|
||||
border: 1px solid #999;
|
||||
padding: 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Actions buttons */
|
||||
.item-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid #999;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
padding: 0.5rem 1rem;
|
||||
background: rgba(74, 4, 4, 0.8);
|
||||
color: white;
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-family: "Charlemagne", serif;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(74, 4, 4, 1);
|
||||
box-shadow: 0 0 8px rgba(74, 4, 4, 0.6);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Rollable elements */
|
||||
.rollable {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: #ff6600;
|
||||
text-shadow: 0 0 8px rgba(255, 102, 0, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
/* Item-specific sections */
|
||||
.item-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
.detail-section {
|
||||
padding: 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border: 1px solid #999;
|
||||
border-radius: 4px;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
padding-bottom: 0.25rem;
|
||||
border-bottom: 1px solid #999;
|
||||
font-size: 1.1rem;
|
||||
color: #2a1510;
|
||||
font-family: "Charlemagne", serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Specific item type styles */
|
||||
.fvtt-wasteland.item {
|
||||
&.arme-content,
|
||||
&.bouclier-content {
|
||||
.weapon-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.5rem;
|
||||
|
||||
.stat-box {
|
||||
padding: 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border: 1px solid #999;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.8rem;
|
||||
color: #666;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.competence-content {
|
||||
.predilections-container {
|
||||
margin-top: 0.5rem;
|
||||
|
||||
.no-predilections {
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
color: #999;
|
||||
padding: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.predilections-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0 0 0.75rem 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
|
||||
.predilection-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.5rem;
|
||||
background: rgba(42, 37, 32, 0.3);
|
||||
border: 1px solid rgba(106, 6, 6, 0.3);
|
||||
border-radius: 4px;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(42, 37, 32, 0.5);
|
||||
border-color: rgba(106, 6, 6, 0.5);
|
||||
}
|
||||
|
||||
.predilection-main {
|
||||
flex: 1;
|
||||
|
||||
.predilection-name {
|
||||
width: 100%;
|
||||
padding: 0.375rem 0.5rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
border-radius: 3px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
font-family: inherit;
|
||||
|
||||
&::placeholder {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: #6a0606;
|
||||
box-shadow: 0 0 0 2px rgba(106, 6, 6, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.predilection-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex-shrink: 0;
|
||||
|
||||
.predilection-used {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
|
||||
input[type="checkbox"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.9rem;
|
||||
color: rgba(218, 218, 218, 0.85);
|
||||
}
|
||||
|
||||
&:hover span {
|
||||
color: rgba(218, 218, 218, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.predilection-delete {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
background: rgba(106, 6, 6, 0.6);
|
||||
border: 1px solid rgba(106, 6, 6, 0.8);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
i {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(106, 6, 6, 0.9);
|
||||
border-color: #6a0606;
|
||||
transform: scale(1.05);
|
||||
|
||||
i {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-predilection-btn {
|
||||
width: 100%;
|
||||
padding: 0.5rem 1rem;
|
||||
background: linear-gradient(to bottom, #4a0404 0%, #3a0303 100%);
|
||||
border: 1px solid #6a0606;
|
||||
border-radius: 4px;
|
||||
color: rgba(218, 218, 218, 0.95);
|
||||
font-family: "Charlemagne", serif;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
i {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to bottom, #5a0505 0%, #4a0404 100%);
|
||||
border-color: #8a0808;
|
||||
color: #fff;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
311
less/roll-dialog-styles.less
Normal file
311
less/roll-dialog-styles.less
Normal file
@@ -0,0 +1,311 @@
|
||||
/* ============================================ */
|
||||
/* WASTELAND ROLL DIALOG STYLES */
|
||||
/* ============================================ */
|
||||
|
||||
.wasteland-roll-dialog {
|
||||
background: url("../assets/ui/pc_sheet_bg.webp") repeat;
|
||||
|
||||
.window-content {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(to bottom, #4a0404 0%, #3a0303 100%);
|
||||
border-bottom: 2px solid #6a0606;
|
||||
margin: -0.5rem -0.5rem 0.5rem -0.5rem;
|
||||
|
||||
.actor-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 2px solid #6a0606;
|
||||
border-radius: 4px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
flex: 1;
|
||||
color: #f5f5f5;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 0.15rem 0;
|
||||
font-size: 1rem;
|
||||
font-family: "Charlemagne", serif;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.competence-name {
|
||||
font-size: 0.85rem;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-style: italic;
|
||||
|
||||
.attribut-info {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
padding: 0 0.5rem 0.5rem 0.5rem;
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 0.4rem;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 0.2rem;
|
||||
font-weight: bold;
|
||||
font-size: 0.85rem;
|
||||
color: #2a1510;
|
||||
font-family: "Charlemagne", serif;
|
||||
}
|
||||
|
||||
select,
|
||||
input[type="number"] {
|
||||
width: 100%;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border: 1px solid #6a0606;
|
||||
border-radius: 3px;
|
||||
padding: 0.3rem 0.4rem;
|
||||
font-size: 0.85rem;
|
||||
color: #1a1510;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border-color: #aa0a0a;
|
||||
box-shadow: 0 0 4px rgba(170, 10, 10, 0.3);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modifiers-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.4rem;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.attributes-section {
|
||||
background: rgba(106, 6, 6, 0.1);
|
||||
border: 1px solid #6a0606;
|
||||
border-radius: 4px;
|
||||
padding: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
label {
|
||||
color: #6a0606;
|
||||
}
|
||||
}
|
||||
|
||||
.special-option {
|
||||
margin-top: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background: rgba(106, 6, 6, 0.05);
|
||||
border: 2px solid #6a0606;
|
||||
border-radius: 4px;
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&.highlight {
|
||||
span {
|
||||
font-weight: bold;
|
||||
color: #6a0606;
|
||||
font-family: "Charlemagne", serif;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
accent-color: #6a0606;
|
||||
}
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
font-size: 0.85rem;
|
||||
color: #2a1510;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Weapon Section */
|
||||
.weapon-section {
|
||||
background: rgba(139, 101, 8, 0.1);
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(139, 101, 8, 0.4);
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.weapon-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.4rem 0.5rem;
|
||||
background: rgba(139, 101, 8, 0.15);
|
||||
border-radius: 3px;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.weapon-label {
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
color: #2a1510;
|
||||
font-family: "Charlemagne", serif;
|
||||
}
|
||||
|
||||
.weapon-bonus {
|
||||
font-size: 0.85rem;
|
||||
color: #8b0000;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.defense-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.4rem 0.5rem;
|
||||
background: rgba(0, 100, 0, 0.1);
|
||||
border-radius: 3px;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.defense-label {
|
||||
font-size: 0.85rem;
|
||||
color: #2a1510;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.defense-value {
|
||||
font-size: 1rem;
|
||||
color: #006400;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Combat Modifiers */
|
||||
.combat-modifiers,
|
||||
.ranged-combat-section {
|
||||
background: rgba(139, 69, 19, 0.1);
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(139, 69, 19, 0.4);
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
h4 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: #2a1510;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
font-family: "Charlemagne", serif;
|
||||
text-transform: uppercase;
|
||||
text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.3rem 0.4rem;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 0.3rem;
|
||||
transition: background 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
width: auto;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
accent-color: #8b4513;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #2a1510;
|
||||
font-size: 0.85rem;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.info-message {
|
||||
padding: 0.4rem 0.6rem;
|
||||
background: rgba(201, 168, 106, 0.2);
|
||||
border-left: 3px solid rgba(201, 168, 106, 0.8);
|
||||
border-radius: 3px;
|
||||
font-size: 0.85rem;
|
||||
color: #2a1510;
|
||||
margin-bottom: 0.5rem;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.modifiers-columns {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.3rem 0.6rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Styles pour les boutons du dialog
|
||||
.dialog-buttons {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
padding: 0.5rem;
|
||||
border-top: 1px solid #6a0606;
|
||||
margin: 0.5rem -0.5rem -0.5rem -0.5rem;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: linear-gradient(to bottom, #4a0404 0%, #3a0303 100%);
|
||||
border: 1px solid #6a0606;
|
||||
border-radius: 3px;
|
||||
color: #f5f5f5;
|
||||
font-size: 0.85rem;
|
||||
font-weight: bold;
|
||||
font-family: "Charlemagne", serif;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to bottom, #5a0505 0%, #4a0404 100%);
|
||||
box-shadow: 0 0 6px rgba(106, 6, 6, 0.6);
|
||||
}
|
||||
|
||||
&.default {
|
||||
border-width: 2px;
|
||||
border-color: #aa0a0a;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1365
less/simple-converted.less
Normal file
1365
less/simple-converted.less
Normal file
File diff suppressed because it is too large
Load Diff
8
less/wasteland.less
Normal file
8
less/wasteland.less
Normal file
@@ -0,0 +1,8 @@
|
||||
// Main LESS file for Wasteland system
|
||||
// Importing base styles and component-specific styles
|
||||
|
||||
@import "simple-converted";
|
||||
@import "item-styles";
|
||||
@import "actor-styles";
|
||||
@import "roll-dialog-styles";
|
||||
@import "chat-styles";
|
||||
27
modules/applications/sheets/_module.mjs
Normal file
27
modules/applications/sheets/_module.mjs
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Index des applications AppV2 pour Wasteland
|
||||
* Ce fichier centralise tous les exports des applications
|
||||
*/
|
||||
|
||||
// Applications de feuilles d'acteurs
|
||||
export { default as WastelandPersonnageSheet } from './wasteland-personnage-sheet.mjs';
|
||||
export { default as WastelandCreatureSheet } from './wasteland-creature-sheet.mjs';
|
||||
|
||||
// Applications de feuilles d'items
|
||||
export { default as WastelandArmeSheet } from './wasteland-arme-sheet.mjs';
|
||||
export { default as WastelandArtifexSheet } from './wasteland-artifex-sheet.mjs';
|
||||
export { default as WastelandBouclierSheet } from './wasteland-bouclier-sheet.mjs';
|
||||
export { default as WastelandCapaciteSheet } from './wasteland-capacite-sheet.mjs';
|
||||
export { default as WastelandCharmeSheet } from './wasteland-charme-sheet.mjs';
|
||||
export { default as WastelandCompetenceSheet } from './wasteland-competence-sheet.mjs';
|
||||
export { default as WastelandDonSheet } from './wasteland-don-sheet.mjs';
|
||||
export { default as WastelandEquipementSheet } from './wasteland-equipement-sheet.mjs';
|
||||
export { default as WastelandHeritageSheet } from './wasteland-heritage-sheet.mjs';
|
||||
export { default as WastelandHubrisSheet } from './wasteland-hubris-sheet.mjs';
|
||||
export { default as WastelandMetierSheet } from './wasteland-metier-sheet.mjs';
|
||||
export { default as WastelandMonnaieSheet } from './wasteland-monnaie-sheet.mjs';
|
||||
export { default as WastelandMutationSheet } from './wasteland-mutation-sheet.mjs';
|
||||
export { default as WastelandOrigineSheet } from './wasteland-origine-sheet.mjs';
|
||||
export { default as WastelandPeupleSheet } from './wasteland-peuple-sheet.mjs';
|
||||
export { default as WastelandPouvoirSheet } from './wasteland-pouvoir-sheet.mjs';
|
||||
export { default as WastelandProtectionSheet } from './wasteland-protection-sheet.mjs';
|
||||
382
modules/applications/sheets/base-actor-sheet.mjs
Normal file
382
modules/applications/sheets/base-actor-sheet.mjs
Normal file
@@ -0,0 +1,382 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||
|
||||
import { WastelandUtility } from "../../wasteland-utility.js"
|
||||
|
||||
export default class WastelandActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
||||
/**
|
||||
* Different sheet modes.
|
||||
* @enum {number}
|
||||
*/
|
||||
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
this.#dragDrop = this.#createDragDropHandlers()
|
||||
this._sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||
}
|
||||
|
||||
#dragDrop
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["fvtt-wasteland", "sheet", "actor"],
|
||||
position: {
|
||||
width: 650,
|
||||
height: 720,
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
tabs: [
|
||||
{
|
||||
navSelector: 'nav[data-group="primary"]',
|
||||
contentSelector: "section.sheet-body",
|
||||
initial: "stats",
|
||||
},
|
||||
],
|
||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
||||
actions: {
|
||||
editImage: WastelandActorSheet.#onEditImage,
|
||||
toggleSheet: WastelandActorSheet.#onToggleSheet,
|
||||
editItem: WastelandActorSheet.#onEditItem,
|
||||
deleteItem: WastelandActorSheet.#onDeleteItem,
|
||||
createItem: WastelandActorSheet.#onCreateItem,
|
||||
equipItem: WastelandActorSheet.#onEquipItem,
|
||||
modifyQuantity: WastelandActorSheet.#onModifyQuantity,
|
||||
incDecSante: WastelandActorSheet.#onIncDecSante,
|
||||
rollAttribut: WastelandActorSheet.#onRollAttribut,
|
||||
rollCompetence: WastelandActorSheet.#onRollCompetence,
|
||||
rollCharme: WastelandActorSheet.#onRollCharme,
|
||||
rollPouvoir: WastelandActorSheet.#onRollPouvoir,
|
||||
rollArmeOffensif: WastelandActorSheet.#onRollArmeOffensif,
|
||||
rollArmeDegats: WastelandActorSheet.#onRollArmeDegats,
|
||||
resetPredilections: WastelandActorSheet.#onResetPredilections,
|
||||
rollAssommer: WastelandActorSheet.#onRollAssommer,
|
||||
rollFuir: WastelandActorSheet.#onRollFuir,
|
||||
rollImmobiliser: WastelandActorSheet.#onRollImmobiliser,
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the sheet currently in 'Play' mode?
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isPlayMode() {
|
||||
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the sheet currently in 'Edit' mode?
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isEditMode() {
|
||||
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
|
||||
}
|
||||
|
||||
/**
|
||||
* Tab groups state
|
||||
* @type {object}
|
||||
*/
|
||||
tabGroups = { primary: "stats" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const actor = this.document
|
||||
|
||||
const context = {
|
||||
actor: actor,
|
||||
system: actor.system,
|
||||
source: actor.toObject(),
|
||||
fields: actor.schema.fields,
|
||||
systemFields: actor.system.schema.fields,
|
||||
isEditable: this.isEditable,
|
||||
isEditMode: this.isEditMode,
|
||||
isPlayMode: this.isPlayMode,
|
||||
isGM: game.user.isGM,
|
||||
config: game.system.wasteland.config,
|
||||
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true }),
|
||||
enrichedComportement: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.comportement || "", { async: true }),
|
||||
enrichedHabitat: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.habitat || "", { async: true }),
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options)
|
||||
this.#dragDrop.forEach((d) => d.bind(this.element))
|
||||
|
||||
// Handle edit-item-data changes
|
||||
this.element.querySelectorAll('.edit-item-data').forEach(element => {
|
||||
element.addEventListener('change', async (event) => {
|
||||
const target = event.currentTarget
|
||||
const itemElement = target.closest('[data-item-id]')
|
||||
if (!itemElement) return
|
||||
|
||||
const itemId = itemElement.dataset.itemId
|
||||
const itemType = itemElement.dataset.itemType
|
||||
const itemField = target.dataset.itemField
|
||||
const dataType = target.dataset.dtype
|
||||
const value = target.value
|
||||
|
||||
await this.document.editItemField(itemId, itemType, itemField, dataType, value)
|
||||
})
|
||||
})
|
||||
|
||||
// Activate tab navigation manually
|
||||
const nav = this.element.querySelector('nav.tabs[data-group]')
|
||||
if (nav) {
|
||||
const group = nav.dataset.group
|
||||
// Activate the current tab
|
||||
const activeTab = this.tabGroups[group] || "stats"
|
||||
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()
|
||||
})
|
||||
})
|
||||
|
||||
// Show/hide tab content
|
||||
this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
|
||||
content.classList.toggle('active', content.dataset.tab === activeTab)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// #region Drag-and-Drop Workflow
|
||||
/**
|
||||
* Create drag-and-drop workflow handlers for this Application
|
||||
*/
|
||||
#createDragDropHandlers() {
|
||||
return []
|
||||
}
|
||||
|
||||
// #region Actions
|
||||
|
||||
/**
|
||||
* Handle editing the actor image
|
||||
* @param {Event} event - The triggering event
|
||||
*/
|
||||
static async #onEditImage(event) {
|
||||
event.preventDefault()
|
||||
const sheet = this
|
||||
const filePicker = new FilePicker({
|
||||
type: "image",
|
||||
current: sheet.document.img,
|
||||
callback: (path) => {
|
||||
sheet.document.update({ img: path })
|
||||
},
|
||||
})
|
||||
filePicker.browse()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle toggling the sheet mode
|
||||
* @param {Event} event - The triggering event
|
||||
*/
|
||||
static async #onToggleSheet(event) {
|
||||
event.preventDefault()
|
||||
const sheet = this
|
||||
sheet._sheetMode = sheet._sheetMode === sheet.constructor.SHEET_MODES.PLAY ? sheet.constructor.SHEET_MODES.EDIT : sheet.constructor.SHEET_MODES.PLAY
|
||||
sheet.render()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle editing an item
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onEditItem(event, target) {
|
||||
const li = target.closest(".item")
|
||||
const itemId = li?.dataset.itemId
|
||||
if (!itemId) return
|
||||
const item = this.actor.items.get(itemId)
|
||||
if (item) item.sheet.render(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle deleting an item
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onDeleteItem(event, target) {
|
||||
const li = target.closest(".item")
|
||||
await WastelandUtility.confirmDelete(this, li)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle creating an item
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onCreateItem(event, target) {
|
||||
const itemType = target.dataset.type
|
||||
await this.actor.createEmbeddedDocuments("Item", [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle equipping an item
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onEquipItem(event, target) {
|
||||
const li = target.closest(".item")
|
||||
const itemId = li?.dataset.itemId
|
||||
if (!itemId) return
|
||||
await this.actor.equipItem(itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle modifying item quantity
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onModifyQuantity(event, target) {
|
||||
const li = target.closest(".item")
|
||||
const itemId = li?.dataset.itemId
|
||||
if (!itemId) return
|
||||
|
||||
const item = this.actor.items.get(itemId)
|
||||
if (!item) return
|
||||
|
||||
const qty = parseInt(target.dataset.qty) || 0
|
||||
const currentQty = item.system.quantite || 0
|
||||
const newQty = Math.max(0, currentQty + qty)
|
||||
|
||||
await item.update({ 'system.quantite': newQty })
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle modifying santé/psyché
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onIncDecSante(event, target) {
|
||||
const field = target.dataset.field
|
||||
const value = parseInt(target.dataset.value) || 0
|
||||
|
||||
if (field === 'psyche') {
|
||||
await this.actor.update({ 'system.psyche.value': this.actor.system.psyche.value + value })
|
||||
} else if (field === 'nonletaux') {
|
||||
await this.actor.update({ 'system.sante.nonletaux': this.actor.system.sante.nonletaux + value })
|
||||
} else if (field === 'letaux') {
|
||||
await this.actor.update({ 'system.sante.letaux': this.actor.system.sante.letaux + value })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle rolling an attribute
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onRollAttribut(event, target) {
|
||||
const attrKey = target.dataset.attrKey
|
||||
await this.actor.rollAttribut(attrKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle rolling a competence
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onRollCompetence(event, target) {
|
||||
const li = target.closest(".item")
|
||||
const itemId = li?.dataset.itemId
|
||||
const attrKey = target.dataset.attrKey
|
||||
if (!itemId) return
|
||||
await this.actor.rollCompetence(attrKey, itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle rolling a charme
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onRollCharme(event, target) {
|
||||
const li = target.closest(".item")
|
||||
const itemId = li?.dataset.itemId
|
||||
if (!itemId) return
|
||||
await this.actor.rollCharme(itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle rolling a pouvoir
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onRollPouvoir(event, target) {
|
||||
const li = target.closest(".item")
|
||||
const itemId = li?.dataset.itemId
|
||||
if (!itemId) return
|
||||
await this.actor.rollPouvoir(itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle rolling weapon attack
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onRollArmeOffensif(event, target) {
|
||||
const li = target.closest(".item")
|
||||
const itemId = li?.dataset.itemId
|
||||
if (!itemId) return
|
||||
await this.actor.rollArmeOffensif(itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle rolling weapon damage
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onRollArmeDegats(event, target) {
|
||||
const li = target.closest(".item")
|
||||
const itemId = li?.dataset.itemId
|
||||
if (!itemId) return
|
||||
await this.actor.rollArmeDegats(itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle resetting all predilections
|
||||
* @param {Event} event - The originating click event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onResetPredilections(event, target) {
|
||||
await this.actor.resetAllPredilections()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Assommer roll
|
||||
* @param {Event} event - The originating click event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onRollAssommer(event, target) {
|
||||
await this.actor.rollAssommer()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Fuir roll
|
||||
* @param {Event} event - The originating click event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onRollFuir(event, target) {
|
||||
await this.actor.rollFuir()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Immobiliser roll
|
||||
* @param {Event} event - The originating click event
|
||||
* @param {HTMLElement} target - The target element
|
||||
*/
|
||||
static async #onRollImmobiliser(event, target) {
|
||||
await this.actor.rollImmobiliser()
|
||||
}
|
||||
}
|
||||
158
modules/applications/sheets/base-item-sheet.mjs
Normal file
158
modules/applications/sheets/base-item-sheet.mjs
Normal file
@@ -0,0 +1,158 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||
|
||||
export default class WastelandItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
this.#dragDrop = this.#createDragDropHandlers()
|
||||
}
|
||||
|
||||
#dragDrop
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["fvtt-wasteland", "item"],
|
||||
position: {
|
||||
width: 620,
|
||||
height: 600,
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
tabs: [
|
||||
{
|
||||
navSelector: 'nav[data-group="primary"]',
|
||||
contentSelector: "section.sheet-body",
|
||||
initial: "description",
|
||||
},
|
||||
],
|
||||
dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }],
|
||||
actions: {
|
||||
editImage: WastelandItemSheet.#onEditImage,
|
||||
postItem: WastelandItemSheet.#onPostItem,
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Tab groups state
|
||||
* @type {object}
|
||||
*/
|
||||
tabGroups = { primary: "description" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = {
|
||||
fields: this.document.schema.fields,
|
||||
systemFields: this.document.system.schema.fields,
|
||||
item: this.document,
|
||||
system: this.document.system,
|
||||
source: this.document.toObject(),
|
||||
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true }),
|
||||
isEditMode: true,
|
||||
isEditable: this.isEditable,
|
||||
isGM: game.user.isGM,
|
||||
config: game.system.wasteland.config,
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options)
|
||||
this.#dragDrop.forEach((d) => d.bind(this.element))
|
||||
|
||||
// Activate tab navigation manually
|
||||
const nav = this.element.querySelector('nav.tabs[data-group]')
|
||||
if (nav) {
|
||||
const group = nav.dataset.group
|
||||
// Activate the current tab
|
||||
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()
|
||||
})
|
||||
})
|
||||
|
||||
// Show/hide tab content
|
||||
this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
|
||||
content.classList.toggle('active', content.dataset.tab === activeTab)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// #region Drag-and-Drop Workflow
|
||||
/**
|
||||
* Create drag-and-drop workflow handlers for this Application
|
||||
*/
|
||||
#createDragDropHandlers() {
|
||||
return []
|
||||
}
|
||||
|
||||
// #region Actions
|
||||
|
||||
/**
|
||||
* Handle editing the item image
|
||||
* @param {Event} event - The triggering event
|
||||
*/
|
||||
static async #onEditImage(event) {
|
||||
event.preventDefault()
|
||||
const filePicker = new FilePicker({
|
||||
type: "image",
|
||||
current: this.document.img,
|
||||
callback: (path) => {
|
||||
this.document.update({ img: path })
|
||||
},
|
||||
})
|
||||
filePicker.browse()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle posting the item to chat
|
||||
* @param {Event} event - The triggering event
|
||||
*/
|
||||
static async #onPostItem(event) {
|
||||
event.preventDefault()
|
||||
|
||||
// Prepare chat data
|
||||
const chatData = {
|
||||
name: this.document.name,
|
||||
img: this.document.img,
|
||||
type: this.document.type,
|
||||
system: this.document.system,
|
||||
}
|
||||
|
||||
// Add actor reference if item is owned
|
||||
if (this.document.actor) {
|
||||
chatData.actor = { id: this.document.actor.id }
|
||||
}
|
||||
|
||||
// Don't post any image for the item if the default image is used
|
||||
if (chatData.img && chatData.img.includes("/blank.png")) {
|
||||
chatData.img = null
|
||||
}
|
||||
|
||||
// JSON object for easy creation
|
||||
chatData.jsondata = JSON.stringify({
|
||||
compendium: "postedItem",
|
||||
payload: chatData,
|
||||
})
|
||||
|
||||
// Render the chat card template
|
||||
const html = await foundry.applications.handlebars.renderTemplate('systems/fvtt-wasteland/templates/post-item.hbs', chatData)
|
||||
|
||||
// Create the chat message
|
||||
const chatOptions = {
|
||||
user: game.user.id,
|
||||
content: html,
|
||||
speaker: ChatMessage.getSpeaker({ actor: this.document.actor })
|
||||
}
|
||||
|
||||
ChatMessage.create(chatOptions)
|
||||
}
|
||||
}
|
||||
49
modules/applications/sheets/wasteland-arme-sheet.mjs
Normal file
49
modules/applications/sheets/wasteland-arme-sheet.mjs
Normal file
@@ -0,0 +1,49 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandArmeSheet extends WastelandItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["arme"],
|
||||
position: {
|
||||
width: 620,
|
||||
},
|
||||
window: {
|
||||
contentClasses: ["arme-content"],
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-wasteland/templates/item-arme-sheet.hbs",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = {
|
||||
primary: "details",
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an array of form header tabs.
|
||||
* @returns {Record<string, Partial<ApplicationTab>>}
|
||||
*/
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "Détails" },
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
33
modules/applications/sheets/wasteland-artifex-sheet.mjs
Normal file
33
modules/applications/sheets/wasteland-artifex-sheet.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandArtifexSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["artifex"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["artifex-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-artifex-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "details" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "Détails" },
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
33
modules/applications/sheets/wasteland-bouclier-sheet.mjs
Normal file
33
modules/applications/sheets/wasteland-bouclier-sheet.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandBouclierSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["bouclier"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["bouclier-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-bouclier-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "details" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "Détails" },
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
32
modules/applications/sheets/wasteland-capacite-sheet.mjs
Normal file
32
modules/applications/sheets/wasteland-capacite-sheet.mjs
Normal file
@@ -0,0 +1,32 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandCapaciteSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["capacite"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["capacite-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-capacite-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "description" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
33
modules/applications/sheets/wasteland-charme-sheet.mjs
Normal file
33
modules/applications/sheets/wasteland-charme-sheet.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandCharmeSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["charme"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["charme-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-charme-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "details" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "Détails" },
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
72
modules/applications/sheets/wasteland-competence-sheet.mjs
Normal file
72
modules/applications/sheets/wasteland-competence-sheet.mjs
Normal file
@@ -0,0 +1,72 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandCompetenceSheet extends WastelandItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["competence"],
|
||||
position: {
|
||||
width: 620,
|
||||
},
|
||||
window: {
|
||||
contentClasses: ["competence-content"],
|
||||
},
|
||||
actions: {
|
||||
"add-predilection": this.#onAddPredilection,
|
||||
"delete-predilection": this.#onDeletePredilection
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-wasteland/templates/item-competence-sheet.hbs",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = {
|
||||
primary: "details",
|
||||
}
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "Détails" },
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle adding a new predilection
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static async #onAddPredilection(event, target) {
|
||||
const predilections = foundry.utils.duplicate(this.document.system.predilections)
|
||||
predilections.push({ name: "Nouvelle prédilection", used: false })
|
||||
await this.document.update({ "system.predilections": predilections })
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle deleting a predilection
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The delete button element
|
||||
*/
|
||||
static async #onDeletePredilection(event, target) {
|
||||
const index = parseInt(target.dataset.index)
|
||||
const predilections = foundry.utils.duplicate(this.document.system.predilections)
|
||||
predilections.splice(index, 1)
|
||||
await this.document.update({ "system.predilections": predilections })
|
||||
}
|
||||
}
|
||||
40
modules/applications/sheets/wasteland-creature-sheet.mjs
Normal file
40
modules/applications/sheets/wasteland-creature-sheet.mjs
Normal file
@@ -0,0 +1,40 @@
|
||||
import WastelandActorSheet from "./base-actor-sheet.mjs"
|
||||
|
||||
export default class WastelandCreatureSheet extends WastelandActorSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "creature"],
|
||||
window: {
|
||||
...super.DEFAULT_OPTIONS.window,
|
||||
title: "Feuille de Créature",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
sheet: {
|
||||
template: "systems/fvtt-wasteland/templates/actor-creature-sheet.hbs",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = {
|
||||
primary: "stats",
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
const actor = this.document
|
||||
|
||||
// Add creature-specific data
|
||||
context.skills = actor.getSkills()
|
||||
context.armes = foundry.utils.duplicate(actor.getWeapons())
|
||||
context.protections = foundry.utils.duplicate(actor.getArmors())
|
||||
context.capacites = foundry.utils.duplicate(actor.getCapacites())
|
||||
context.combat = actor.getCombatValues()
|
||||
|
||||
return context
|
||||
}
|
||||
}
|
||||
32
modules/applications/sheets/wasteland-don-sheet.mjs
Normal file
32
modules/applications/sheets/wasteland-don-sheet.mjs
Normal file
@@ -0,0 +1,32 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandDonSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["don"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["don-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-don-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "description" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
33
modules/applications/sheets/wasteland-equipement-sheet.mjs
Normal file
33
modules/applications/sheets/wasteland-equipement-sheet.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandEquipementSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["equipement"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["equipement-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-equipement-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "details" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "Détails" },
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
32
modules/applications/sheets/wasteland-heritage-sheet.mjs
Normal file
32
modules/applications/sheets/wasteland-heritage-sheet.mjs
Normal file
@@ -0,0 +1,32 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandHeritageSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["heritage"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["heritage-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-heritage-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "description" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
33
modules/applications/sheets/wasteland-hubris-sheet.mjs
Normal file
33
modules/applications/sheets/wasteland-hubris-sheet.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandHubrisSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["hubris"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["hubris-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-hubris-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "details" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "Détails" },
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
32
modules/applications/sheets/wasteland-metier-sheet.mjs
Normal file
32
modules/applications/sheets/wasteland-metier-sheet.mjs
Normal file
@@ -0,0 +1,32 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandMetierSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["metier"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["metier-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-metier-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "description" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
33
modules/applications/sheets/wasteland-monnaie-sheet.mjs
Normal file
33
modules/applications/sheets/wasteland-monnaie-sheet.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandMonnaieSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["monnaie"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["monnaie-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-monnaie-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "details" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "Détails" },
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
32
modules/applications/sheets/wasteland-mutation-sheet.mjs
Normal file
32
modules/applications/sheets/wasteland-mutation-sheet.mjs
Normal file
@@ -0,0 +1,32 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandMutationSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["mutation"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["mutation-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-mutation-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "description" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
32
modules/applications/sheets/wasteland-origine-sheet.mjs
Normal file
32
modules/applications/sheets/wasteland-origine-sheet.mjs
Normal file
@@ -0,0 +1,32 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandOrigineSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["origine"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["origine-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-origine-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "description" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
59
modules/applications/sheets/wasteland-personnage-sheet.mjs
Normal file
59
modules/applications/sheets/wasteland-personnage-sheet.mjs
Normal file
@@ -0,0 +1,59 @@
|
||||
import WastelandActorSheet from "./base-actor-sheet.mjs"
|
||||
|
||||
export default class WastelandPersonnageSheet extends WastelandActorSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "personnage"],
|
||||
window: {
|
||||
...super.DEFAULT_OPTIONS.window,
|
||||
title: "Feuille de Personnage",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
sheet: {
|
||||
template: "systems/fvtt-wasteland/templates/actor-personnage-sheet.hbs",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = {
|
||||
primary: "stats",
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
const actor = this.document
|
||||
|
||||
// Add personnage-specific data
|
||||
context.skills = actor.getSkills()
|
||||
context.armes = foundry.utils.duplicate(actor.getWeapons())
|
||||
context.protections = foundry.utils.duplicate(actor.getArmors())
|
||||
context.pouvoirs = foundry.utils.duplicate(actor.getPouvoirs())
|
||||
context.dons = foundry.utils.duplicate(actor.getDons())
|
||||
context.hubrises = foundry.utils.duplicate(actor.getHubris())
|
||||
context.tours = foundry.utils.duplicate(actor.getTours())
|
||||
context.artifex = foundry.utils.duplicate(actor.getArtifex())
|
||||
context.charmes = foundry.utils.duplicate(actor.getCharmes())
|
||||
context.peuple = foundry.utils.duplicate(actor.getPeuple() || {})
|
||||
context.origine = foundry.utils.duplicate(actor.getOrigine() || {})
|
||||
context.heritage = foundry.utils.duplicate(actor.getHeritage() || {})
|
||||
context.metier = foundry.utils.duplicate(actor.getMetier() || {})
|
||||
context.combat = actor.getCombatValues()
|
||||
context.capacites = foundry.utils.duplicate(actor.getCapacites())
|
||||
context.equipements = foundry.utils.duplicate(actor.getEquipments())
|
||||
context.monnaies = foundry.utils.duplicate(actor.getMonnaies())
|
||||
context.mutations = foundry.utils.duplicate(actor.getMutations())
|
||||
|
||||
// Enrich HTML fields for biodata
|
||||
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.notes || "", { async: true })
|
||||
context.enrichedGMNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.gmnotes || "", { async: true })
|
||||
context.enrichedSequelles = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.sequelles || "", { async: true })
|
||||
context.enrichedTraumatismes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.traumatismes || "", { async: true })
|
||||
|
||||
return context
|
||||
}
|
||||
}
|
||||
32
modules/applications/sheets/wasteland-peuple-sheet.mjs
Normal file
32
modules/applications/sheets/wasteland-peuple-sheet.mjs
Normal file
@@ -0,0 +1,32 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandPeupleSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["peuple"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["peuple-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-peuple-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "description" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
33
modules/applications/sheets/wasteland-pouvoir-sheet.mjs
Normal file
33
modules/applications/sheets/wasteland-pouvoir-sheet.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandPouvoirSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["pouvoir"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["pouvoir-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-pouvoir-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "details" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "Détails" },
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
33
modules/applications/sheets/wasteland-protection-sheet.mjs
Normal file
33
modules/applications/sheets/wasteland-protection-sheet.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import WastelandItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class WastelandProtectionSheet extends WastelandItemSheet {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["protection"],
|
||||
position: { width: 620 },
|
||||
window: { contentClasses: ["protection-content"] },
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-wasteland/templates/item-protection-sheet.hbs" },
|
||||
}
|
||||
|
||||
tabGroups = { primary: "details" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "Détails" },
|
||||
description: { id: "description", group: "primary", label: "Description" }
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
return context
|
||||
}
|
||||
}
|
||||
156
modules/applications/wasteland-roll-dialog.mjs
Normal file
156
modules/applications/wasteland-roll-dialog.mjs
Normal file
@@ -0,0 +1,156 @@
|
||||
import { WastelandUtility } from "../wasteland-utility.js"
|
||||
|
||||
/**
|
||||
* Dialogue de jet de dé pour Wasteland - Version DialogV2
|
||||
*/
|
||||
export class WastelandRollDialog {
|
||||
|
||||
/**
|
||||
* Create and display the roll dialog
|
||||
* @param {WastelandActor} actor - The actor making the roll
|
||||
* @param {Object} rollData - Data for the roll
|
||||
* @returns {Promise<WastelandRollDialog>}
|
||||
*/
|
||||
static async create(actor, rollData) {
|
||||
// Préparer le contexte pour le template
|
||||
const context = {
|
||||
...rollData,
|
||||
difficulte: String(rollData.difficulte || 0),
|
||||
img: actor.img,
|
||||
name: actor.name,
|
||||
config: game.system.wasteland.config,
|
||||
}
|
||||
|
||||
// Rendre le template en HTML
|
||||
const content = await foundry.applications.handlebars.renderTemplate(
|
||||
"systems/fvtt-wasteland/templates/roll-dialog-v2.hbs",
|
||||
context
|
||||
)
|
||||
|
||||
// Utiliser DialogV2.wait avec le HTML rendu
|
||||
return foundry.applications.api.DialogV2.wait({
|
||||
window: { title: "Test de Capacité", icon: "fa-solid fa-dice-d20" },
|
||||
classes: ["wasteland-roll-dialog"],
|
||||
position: { width: 500 },
|
||||
modal: false,
|
||||
content,
|
||||
buttons: rollData.charme ? [
|
||||
{
|
||||
action: "roll",
|
||||
label: "Lancer",
|
||||
icon: "fa-solid fa-dice-d20",
|
||||
default: true,
|
||||
callback: (event, button, dialog) => {
|
||||
this._updateRollDataFromForm(rollData, button.form.elements, actor)
|
||||
WastelandUtility.rollWasteland(rollData)
|
||||
}
|
||||
}
|
||||
] : [
|
||||
{
|
||||
action: "rolld10",
|
||||
label: "Lancer 1d10",
|
||||
icon: "fa-solid fa-dice-d10",
|
||||
default: true,
|
||||
callback: (event, button, dialog) => {
|
||||
this._updateRollDataFromForm(rollData, button.form.elements, actor)
|
||||
rollData.mainDice = "1d10"
|
||||
WastelandUtility.rollWasteland(rollData)
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "rolld20",
|
||||
label: "Lancer 1d20",
|
||||
icon: "fa-solid fa-dice-d20",
|
||||
callback: (event, button, dialog) => {
|
||||
this._updateRollDataFromForm(rollData, button.form.elements, actor)
|
||||
rollData.mainDice = "1d20"
|
||||
WastelandUtility.rollWasteland(rollData)
|
||||
}
|
||||
},
|
||||
],
|
||||
rejectClose: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Mettre à jour rollData avec les valeurs du formulaire
|
||||
* @param {Object} rollData - L'objet rollData à mettre à jour
|
||||
* @param {HTMLFormControlsCollection} formElements - Les éléments du formulaire
|
||||
* @param {WastelandActor} actor - L'acteur pour récupérer les attributs
|
||||
* @private
|
||||
*/
|
||||
static _updateRollDataFromForm(rollData, formElements, actor) {
|
||||
// Attributs
|
||||
if (formElements.attrKey) {
|
||||
rollData.attrKey = formElements.attrKey.value
|
||||
if (rollData.attrKey !== "tochoose" && actor) {
|
||||
rollData.attr = foundry.utils.duplicate(actor.system.attributs[rollData.attrKey])
|
||||
}
|
||||
}
|
||||
|
||||
// Modificateurs de base
|
||||
if (formElements.difficulte) {
|
||||
rollData.difficulte = Number(formElements.difficulte.value)
|
||||
}
|
||||
if (formElements.modificateur) {
|
||||
rollData.modificateur = Number(formElements.modificateur.value)
|
||||
}
|
||||
|
||||
// Charme
|
||||
if (formElements.charmeDice) {
|
||||
rollData.charmeDice = String(formElements.charmeDice.value)
|
||||
}
|
||||
|
||||
// Combat mêlée
|
||||
if (formElements.typeAttaque) {
|
||||
rollData.typeAttaque = String(formElements.typeAttaque.value)
|
||||
rollData.typeAttaqueLabel = rollData.config.attaques[rollData.typeAttaque]
|
||||
}
|
||||
if (formElements.isMonte !== undefined) {
|
||||
rollData.isMonte = formElements.isMonte.checked
|
||||
}
|
||||
|
||||
// Combat distance
|
||||
if (formElements.visee !== undefined) {
|
||||
rollData.visee = formElements.visee.checked
|
||||
}
|
||||
if (formElements.cibleconsciente !== undefined) {
|
||||
rollData.cibleconsciente = formElements.cibleconsciente.checked
|
||||
}
|
||||
if (formElements.ciblecourt !== undefined) {
|
||||
rollData.ciblecourt = formElements.ciblecourt.checked
|
||||
}
|
||||
if (formElements.typeCouvert) {
|
||||
rollData.typeCouvert = String(formElements.typeCouvert.value)
|
||||
const couvert = rollData.config.couverts[rollData.typeCouvert]
|
||||
if (rollData.typeCouvert !== "aucun" && couvert) {
|
||||
rollData.typeCouvertLabel = couvert.name
|
||||
rollData.typeCouvertValue = couvert.value
|
||||
}
|
||||
}
|
||||
|
||||
// Désavantages (avantages tactiques)
|
||||
if (!rollData.desavantages) rollData.desavantages = {}
|
||||
|
||||
if (formElements.cibleausol !== undefined) {
|
||||
rollData.desavantages.cibleausol = formElements.cibleausol.checked
|
||||
}
|
||||
if (formElements.cibledesarmee !== undefined) {
|
||||
rollData.desavantages.cibledesarmee = formElements.cibledesarmee.checked
|
||||
}
|
||||
if (formElements.ciblerestreint !== undefined) {
|
||||
rollData.desavantages.ciblerestreint = formElements.ciblerestreint.checked
|
||||
}
|
||||
if (formElements.cibleimmobilisée !== undefined) {
|
||||
rollData.desavantages.cibleimmobilisée = formElements.cibleimmobilisée.checked
|
||||
}
|
||||
if (formElements.ciblesurplomb !== undefined) {
|
||||
rollData.desavantages.ciblesurplomb = formElements.ciblesurplomb.checked
|
||||
}
|
||||
|
||||
// Double D20
|
||||
if (formElements.doubleD20 !== undefined) {
|
||||
rollData.doubleD20 = formElements.doubleD20.checked
|
||||
}
|
||||
}
|
||||
}
|
||||
26
modules/models/arme.mjs
Normal file
26
modules/models/arme.mjs
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Data model pour les armes
|
||||
*/
|
||||
export default class ArmeDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
typearme: new fields.StringField({ initial: "" }),
|
||||
isdefense: new fields.BooleanField({ initial: false }),
|
||||
bonusmaniementoff: new fields.NumberField({ initial: 0, integer: true }),
|
||||
bonusmaniementdef: new fields.NumberField({ initial: 0, integer: true }),
|
||||
nobonusdegats: new fields.BooleanField({ initial: false }),
|
||||
degats: new fields.StringField({ initial: "" }),
|
||||
nonletaux: new fields.BooleanField({ initial: false }),
|
||||
deuxmains: new fields.BooleanField({ initial: false }),
|
||||
courte: new fields.NumberField({ initial: 0, integer: true }),
|
||||
moyenne: new fields.NumberField({ initial: 0, integer: true }),
|
||||
longue: new fields.NumberField({ initial: 0, integer: true }),
|
||||
tr: new fields.NumberField({ initial: 0, integer: true }),
|
||||
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||
prix: new fields.NumberField({ initial: 0, integer: true }),
|
||||
equipped: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
}
|
||||
}
|
||||
18
modules/models/artifex.mjs
Normal file
18
modules/models/artifex.mjs
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Data model pour les artifex
|
||||
*/
|
||||
export default class ArtifexDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
artifextype: new fields.StringField({ initial: "vapeur" }),
|
||||
competence: new fields.StringField({ initial: "" }),
|
||||
complexite: new fields.NumberField({ initial: 0, integer: true }),
|
||||
dureerealisation: new fields.StringField({ initial: "" }),
|
||||
tempsmiseenroute: new fields.StringField({ initial: "" }),
|
||||
defautcourant: new fields.StringField({ initial: "" }),
|
||||
prix: new fields.NumberField({ initial: 0, integer: true })
|
||||
};
|
||||
}
|
||||
}
|
||||
17
modules/models/bouclier.mjs
Normal file
17
modules/models/bouclier.mjs
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Data model pour les boucliers
|
||||
*/
|
||||
export default class BouclierDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
bonusdefense: new fields.NumberField({ initial: 0, integer: true }),
|
||||
degats: new fields.StringField({ initial: "" }),
|
||||
nonletaux: new fields.BooleanField({ initial: false }),
|
||||
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||
prix: new fields.NumberField({ initial: 0, integer: true }),
|
||||
equipped: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
}
|
||||
}
|
||||
11
modules/models/capacite.mjs
Normal file
11
modules/models/capacite.mjs
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Data model pour les capacités
|
||||
*/
|
||||
export default class CapaciteDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
25
modules/models/charme.mjs
Normal file
25
modules/models/charme.mjs
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Data model pour les charmes
|
||||
*/
|
||||
export default class CharmeDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
charmetype: new fields.StringField({ initial: "tour" }),
|
||||
resultats: new fields.ArrayField(new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: -1, integer: true }),
|
||||
description: new fields.StringField({ initial: "" })
|
||||
}), {
|
||||
initial: [
|
||||
{ value: -1, description: "" },
|
||||
{ value: -1, description: "" },
|
||||
{ value: -1, description: "" },
|
||||
{ value: -1, description: "" },
|
||||
{ value: -1, description: "" },
|
||||
{ value: -1, description: "" }
|
||||
]
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
21
modules/models/competence.mjs
Normal file
21
modules/models/competence.mjs
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Data model pour les compétences
|
||||
*/
|
||||
export default class CompetenceDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
niveau: new fields.NumberField({ initial: 0, integer: true }),
|
||||
attribut1: new fields.StringField({ initial: "" }),
|
||||
attribut2: new fields.StringField({ initial: "" }),
|
||||
attribut3: new fields.StringField({ initial: "" }),
|
||||
doublebonus: new fields.BooleanField({ initial: false }),
|
||||
predilections: new fields.ArrayField(new fields.SchemaField({
|
||||
name: new fields.StringField({ initial: "" }),
|
||||
description: new fields.StringField({ initial: "" }),
|
||||
used: new fields.BooleanField({ initial: false })
|
||||
}), { initial: [] })
|
||||
};
|
||||
}
|
||||
}
|
||||
99
modules/models/creature.mjs
Normal file
99
modules/models/creature.mjs
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Data model pour les créatures
|
||||
*/
|
||||
export default class CreatureDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
// Template biodata
|
||||
biodata: new fields.SchemaField({
|
||||
name: new fields.StringField({ initial: "" }),
|
||||
age: new fields.NumberField({ initial: 0, integer: true }),
|
||||
alignement: new fields.StringField({ initial: "" }),
|
||||
poids: new fields.StringField({ initial: "" }),
|
||||
taille: new fields.StringField({ initial: "" }),
|
||||
cheveux: new fields.StringField({ initial: "" }),
|
||||
sexe: new fields.StringField({ initial: "" }),
|
||||
yeux: new fields.StringField({ initial: "" }),
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
habitat: new fields.StringField({ initial: "" }),
|
||||
comportement: new fields.StringField({ initial: "" }),
|
||||
psychemultiplier: new fields.NumberField({ initial: 1, integer: true }),
|
||||
notes: new fields.HTMLField({ initial: "" }),
|
||||
gmnotes: new fields.HTMLField({ initial: "" })
|
||||
}),
|
||||
// Template core
|
||||
terreur: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: -1, integer: true })
|
||||
}),
|
||||
protection: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
ressource: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
subactors: new fields.ArrayField(new fields.StringField(), { initial: [] }),
|
||||
attributs: new fields.SchemaField({
|
||||
adr: new fields.SchemaField({
|
||||
label: new fields.StringField({ initial: "Adresse" }),
|
||||
labelnorm: new fields.StringField({ initial: "adresse" }),
|
||||
abbrev: new fields.StringField({ initial: "adr" }),
|
||||
value: new fields.NumberField({ initial: 1, integer: true })
|
||||
}),
|
||||
pui: new fields.SchemaField({
|
||||
label: new fields.StringField({ initial: "Puissance" }),
|
||||
labelnorm: new fields.StringField({ initial: "puissance" }),
|
||||
abbrev: new fields.StringField({ initial: "pui" }),
|
||||
value: new fields.NumberField({ initial: 1, integer: true })
|
||||
}),
|
||||
cla: new fields.SchemaField({
|
||||
label: new fields.StringField({ initial: "Clairvoyance" }),
|
||||
labelnorm: new fields.StringField({ initial: "clairvoyance" }),
|
||||
abbrev: new fields.StringField({ initial: "cla" }),
|
||||
value: new fields.NumberField({ initial: 1, integer: true })
|
||||
}),
|
||||
pre: new fields.SchemaField({
|
||||
label: new fields.StringField({ initial: "Présence" }),
|
||||
labelnorm: new fields.StringField({ initial: "presence" }),
|
||||
abbrev: new fields.StringField({ initial: "pre" }),
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
tre: new fields.SchemaField({
|
||||
label: new fields.StringField({ initial: "Trempe" }),
|
||||
labelnorm: new fields.StringField({ initial: "trempe" }),
|
||||
abbrev: new fields.StringField({ initial: "tre" }),
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
})
|
||||
}),
|
||||
bonneaventure: new fields.SchemaField({
|
||||
base: new fields.NumberField({ initial: 0, integer: true }),
|
||||
actuelle: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
experience: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
eclat: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
sante: new fields.SchemaField({
|
||||
base: new fields.NumberField({ initial: 0, integer: true }),
|
||||
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||
nonletaux: new fields.NumberField({ initial: 0, integer: true }),
|
||||
letaux: new fields.NumberField({ initial: 0, integer: true }),
|
||||
sequelles: new fields.StringField({ initial: "" })
|
||||
}),
|
||||
psyche: new fields.SchemaField({
|
||||
fullmax: new fields.NumberField({ initial: 0, integer: true }),
|
||||
currentmax: new fields.NumberField({ initial: 0, integer: true }),
|
||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||
traumatismes: new fields.StringField({ initial: "" })
|
||||
}),
|
||||
combat: new fields.SchemaField({
|
||||
initbonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||
vitessebonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||
bonusdegats: new fields.NumberField({ initial: 0, integer: true }),
|
||||
defensebonus: new fields.NumberField({ initial: 0, integer: true })
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
12
modules/models/don.mjs
Normal file
12
modules/models/don.mjs
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Data model pour les dons
|
||||
*/
|
||||
export default class DonDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
coutpsyche: new fields.NumberField({ initial: 0, integer: true })
|
||||
};
|
||||
}
|
||||
}
|
||||
14
modules/models/equipement.mjs
Normal file
14
modules/models/equipement.mjs
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Data model pour les équipements
|
||||
*/
|
||||
export default class EquipementDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
quantite: new fields.NumberField({ initial: 1, integer: true, min: 0 }),
|
||||
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||
prix: new fields.NumberField({ initial: 0, integer: true })
|
||||
};
|
||||
}
|
||||
}
|
||||
11
modules/models/heritage.mjs
Normal file
11
modules/models/heritage.mjs
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Data model pour les héritages
|
||||
*/
|
||||
export default class HeritageDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
12
modules/models/hubris.mjs
Normal file
12
modules/models/hubris.mjs
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Data model pour les hubris
|
||||
*/
|
||||
export default class HubrisDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
hubristype: new fields.StringField({ initial: "mental" })
|
||||
};
|
||||
}
|
||||
}
|
||||
27
modules/models/index.mjs
Normal file
27
modules/models/index.mjs
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Index des DataModels pour Wasteland
|
||||
* Ce fichier centralise tous les exports des modèles de données
|
||||
*/
|
||||
|
||||
// Modèles d'items
|
||||
export { default as ArmeDataModel } from './arme.mjs';
|
||||
export { default as BouclierDataModel } from './bouclier.mjs';
|
||||
export { default as CapaciteDataModel } from './capacite.mjs';
|
||||
export { default as CompetenceDataModel } from './competence.mjs';
|
||||
export { default as DonDataModel } from './don.mjs';
|
||||
export { default as EquipementDataModel } from './equipement.mjs';
|
||||
export { default as HeritageDataModel } from './heritage.mjs';
|
||||
export { default as MetierDataModel } from './metier.mjs';
|
||||
export { default as MonnaieDataModel } from './monnaie.mjs';
|
||||
export { default as OrigineDataModel } from './origine.mjs';
|
||||
export { default as ProtectionDataModel } from './protection.mjs';
|
||||
export { default as MutationDataModel } from './mutation.mjs';
|
||||
export { default as PouvoirDataModel } from './pouvoir.mjs';
|
||||
export { default as CharmeDataModel } from './charme.mjs';
|
||||
export { default as ArtifexDataModel } from './artifex.mjs';
|
||||
export { default as PeupleDataModel } from './peuple.mjs';
|
||||
export { default as HubrisDataModel } from './hubris.mjs';
|
||||
|
||||
// Modèles d'acteurs
|
||||
export { default as PersonnageDataModel } from './personnage.mjs';
|
||||
export { default as CreatureDataModel } from './creature.mjs';
|
||||
11
modules/models/metier.mjs
Normal file
11
modules/models/metier.mjs
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Data model pour les métiers
|
||||
*/
|
||||
export default class MetierDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
13
modules/models/monnaie.mjs
Normal file
13
modules/models/monnaie.mjs
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Data model pour les monnaies
|
||||
*/
|
||||
export default class MonnaieDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
quantite: new fields.NumberField({ initial: 0, integer: true }),
|
||||
unite: new fields.StringField({ initial: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
11
modules/models/mutation.mjs
Normal file
11
modules/models/mutation.mjs
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Data model pour les mutations
|
||||
*/
|
||||
export default class MutationDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
11
modules/models/origine.mjs
Normal file
11
modules/models/origine.mjs
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Data model pour les origines
|
||||
*/
|
||||
export default class OrigineDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
100
modules/models/personnage.mjs
Normal file
100
modules/models/personnage.mjs
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Data model pour les personnages
|
||||
*/
|
||||
export default class PersonnageDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
// Template biodata
|
||||
biodata: new fields.SchemaField({
|
||||
name: new fields.StringField({ initial: "" }),
|
||||
age: new fields.NumberField({ initial: 0, integer: true }),
|
||||
alignement: new fields.StringField({ initial: "" }),
|
||||
poids: new fields.StringField({ initial: "" }),
|
||||
taille: new fields.StringField({ initial: "" }),
|
||||
cheveux: new fields.StringField({ initial: "" }),
|
||||
sexe: new fields.StringField({ initial: "" }),
|
||||
yeux: new fields.StringField({ initial: "" }),
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
habitat: new fields.StringField({ initial: "" }),
|
||||
comportement: new fields.StringField({ initial: "" }),
|
||||
psychemultiplier: new fields.NumberField({ initial: 1, integer: true }),
|
||||
notes: new fields.HTMLField({ initial: "" }),
|
||||
gmnotes: new fields.HTMLField({ initial: "" })
|
||||
}),
|
||||
// Template core
|
||||
terreur: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: -1, integer: true })
|
||||
}),
|
||||
protection: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
ressource: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
subactors: new fields.ArrayField(new fields.StringField(), { initial: [] }),
|
||||
attributs: new fields.SchemaField({
|
||||
adr: new fields.SchemaField({
|
||||
label: new fields.StringField({ initial: "Adresse" }),
|
||||
labelnorm: new fields.StringField({ initial: "adresse" }),
|
||||
abbrev: new fields.StringField({ initial: "adr" }),
|
||||
value: new fields.NumberField({ initial: 1, integer: true })
|
||||
}),
|
||||
pui: new fields.SchemaField({
|
||||
label: new fields.StringField({ initial: "Puissance" }),
|
||||
labelnorm: new fields.StringField({ initial: "puissance" }),
|
||||
abbrev: new fields.StringField({ initial: "pui" }),
|
||||
value: new fields.NumberField({ initial: 1, integer: true })
|
||||
}),
|
||||
cla: new fields.SchemaField({
|
||||
label: new fields.StringField({ initial: "Clairvoyance" }),
|
||||
labelnorm: new fields.StringField({ initial: "clairvoyance" }),
|
||||
abbrev: new fields.StringField({ initial: "cla" }),
|
||||
value: new fields.NumberField({ initial: 1, integer: true })
|
||||
}),
|
||||
pre: new fields.SchemaField({
|
||||
label: new fields.StringField({ initial: "Présence" }),
|
||||
labelnorm: new fields.StringField({ initial: "presence" }),
|
||||
abbrev: new fields.StringField({ initial: "pre" }),
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
tre: new fields.SchemaField({
|
||||
label: new fields.StringField({ initial: "Trempe" }),
|
||||
labelnorm: new fields.StringField({ initial: "trempe" }),
|
||||
abbrev: new fields.StringField({ initial: "tre" }),
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
})
|
||||
}),
|
||||
bonneaventure: new fields.SchemaField({
|
||||
base: new fields.NumberField({ initial: 0, integer: true }),
|
||||
actuelle: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
experience: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
eclat: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
sante: new fields.SchemaField({
|
||||
base: new fields.NumberField({ initial: 0, integer: true }),
|
||||
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||
nonletaux: new fields.NumberField({ initial: 0, integer: true }),
|
||||
letaux: new fields.NumberField({ initial: 0, integer: true }),
|
||||
sequelles: new fields.StringField({ initial: "" })
|
||||
}),
|
||||
psyche: new fields.SchemaField({
|
||||
fullmax: new fields.NumberField({ initial: 0, integer: true }),
|
||||
currentmax: new fields.NumberField({ initial: 0, integer: true }),
|
||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||
traumatismes: new fields.StringField({ initial: "" })
|
||||
}),
|
||||
combat: new fields.SchemaField({
|
||||
initbonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||
vitessebonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||
bonusdegats: new fields.NumberField({ initial: 0, integer: true }),
|
||||
defensebonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||
monte: new fields.BooleanField({ initial: false })
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
11
modules/models/peuple.mjs
Normal file
11
modules/models/peuple.mjs
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Data model pour les peuples
|
||||
*/
|
||||
export default class PeupleDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
20
modules/models/pouvoir.mjs
Normal file
20
modules/models/pouvoir.mjs
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Data model pour les pouvoirs
|
||||
*/
|
||||
export default class PouvoirDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
chemin: new fields.StringField({ initial: "force" }),
|
||||
attribut1: new fields.StringField({ initial: "cla" }),
|
||||
competence: new fields.StringField({ initial: "" }),
|
||||
seuil: new fields.NumberField({ initial: 0, integer: true }),
|
||||
coutpsyche: new fields.NumberField({ initial: 0, integer: true }),
|
||||
cible: new fields.StringField({ initial: "" }),
|
||||
duree: new fields.StringField({ initial: "" }),
|
||||
formulesimple: new fields.StringField({ initial: "" }),
|
||||
formuleetendue: new fields.StringField({ initial: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
15
modules/models/protection.mjs
Normal file
15
modules/models/protection.mjs
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Data model pour les protections
|
||||
*/
|
||||
export default class ProtectionDataModel extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
description: new fields.HTMLField({ initial: "" }),
|
||||
protection: new fields.NumberField({ initial: 0, integer: true }),
|
||||
equipped: new fields.BooleanField({ initial: false }),
|
||||
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||
prix: new fields.NumberField({ initial: 0, integer: true })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/**
|
||||
* Extend the basic ActorSheet with some very simple modifications
|
||||
* @extends {ActorSheet}
|
||||
*/
|
||||
|
||||
import { WastelandUtility } from "./wasteland-utility.js";
|
||||
import { WastelandRollDialog } from "./wasteland-roll-dialog.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class WastelandActorSheet extends ActorSheet {
|
||||
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
|
||||
return mergeObject(super.defaultOptions, {
|
||||
classes: ["fvtt-wasteland", "sheet", "actor"],
|
||||
template: "systems/fvtt-wasteland/templates/actor-sheet.html",
|
||||
width: 640,
|
||||
height: 720,
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }],
|
||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
||||
editScore: false
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async getData() {
|
||||
const objectData = duplicate(this.object)
|
||||
let actorData = objectData
|
||||
|
||||
let formData = {
|
||||
title: this.title,
|
||||
id: objectData.id,
|
||||
type: objectData.type,
|
||||
img: objectData.img,
|
||||
name: objectData.name,
|
||||
editable: this.isEditable,
|
||||
cssClass: this.isEditable ? "editable" : "locked",
|
||||
data: actorData.system,
|
||||
effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)),
|
||||
limited: this.object.limited,
|
||||
skills: this.actor.getSkills(),
|
||||
armes: duplicate(this.actor.getWeapons()),
|
||||
protections: duplicate(this.actor.getArmors()),
|
||||
pouvoirs:duplicate(this.actor.getPouvoirs()),
|
||||
tours:duplicate(this.actor.getTours()),
|
||||
charmes:duplicate(this.actor.getCharmes()),
|
||||
origine: duplicate(this.actor.getOrigine() || {}),
|
||||
heritage: duplicate(this.actor.getHeritage() || {}),
|
||||
metier: duplicate(this.actor.getMetier() || {}),
|
||||
combat: this.actor.getCombatValues(),
|
||||
config: duplicate(game.system.wasteland.config),
|
||||
equipements: duplicate(this.actor.getEquipments()),
|
||||
monnaies: duplicate(this.actor.getMonnaies()),
|
||||
description: await TextEditor.enrichHTML(this.object.system.biodata.description, {async: true}),
|
||||
options: this.options,
|
||||
owner: this.document.isOwner,
|
||||
editScore: this.options.editScore,
|
||||
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;
|
||||
|
||||
// Update Inventory Item
|
||||
html.find('.item-edit').click(ev => {
|
||||
const li = $(ev.currentTarget).parents(".item")
|
||||
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");
|
||||
WastelandUtility.confirmDelete(this, li);
|
||||
})
|
||||
html.find('.edit-item-data').change(ev => {
|
||||
const li = $(ev.currentTarget).parents(".item")
|
||||
let itemId = li.data("item-id")
|
||||
let itemType = li.data("item-type")
|
||||
let itemField = $(ev.currentTarget).data("item-field")
|
||||
let dataType = $(ev.currentTarget).data("dtype")
|
||||
let value = ev.currentTarget.value
|
||||
this.actor.editItemField(itemId, itemType, itemField, dataType, value)
|
||||
})
|
||||
|
||||
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-attribut').click((event) => {
|
||||
const li = $(event.currentTarget).parents(".item")
|
||||
let attrKey = li.data("attr-key")
|
||||
this.actor.rollAttribut(attrKey)
|
||||
})
|
||||
html.find('.roll-competence').click((event) => {
|
||||
const li = $(event.currentTarget).parents(".item")
|
||||
let attrKey = $(event.currentTarget).data("attr-key")
|
||||
let compId = li.data("item-id")
|
||||
this.actor.rollCompetence(attrKey, compId)
|
||||
})
|
||||
html.find('.roll-charme').click((event) => {
|
||||
const li = $(event.currentTarget).parents(".item")
|
||||
let charmeId = li.data("item-id")
|
||||
this.actor.rollCharme(charmeId)
|
||||
})
|
||||
|
||||
html.find('.roll-pouvoir').click((event) => {
|
||||
const li = $(event.currentTarget).parents(".item")
|
||||
let pouvoirId = li.data("item-id")
|
||||
this.actor.rollPouvoir(pouvoirId)
|
||||
})
|
||||
html.find('.roll-arme-offensif').click((event) => {
|
||||
const li = $(event.currentTarget).parents(".item")
|
||||
let armeId = li.data("item-id")
|
||||
this.actor.rollArmeOffensif(armeId)
|
||||
})
|
||||
html.find('.roll-arme-degats').click((event) => {
|
||||
const li = $(event.currentTarget).parents(".item")
|
||||
let armeId = li.data("item-id")
|
||||
this.actor.rollArmeDegats(armeId)
|
||||
})
|
||||
html.find('.quantity-modify').click(event => {
|
||||
const li = $(event.currentTarget).parents(".item")
|
||||
const value = Number($(event.currentTarget).data("quantite-value"))
|
||||
this.actor.incDecQuantity( li.data("item-id"), value );
|
||||
})
|
||||
html.find('.item-add').click((event) => {
|
||||
const itemType = $(event.currentTarget).data("type")
|
||||
this.actor.createEmbeddedDocuments('Item', [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true })
|
||||
})
|
||||
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** @override */
|
||||
setPosition(options = {}) {
|
||||
const position = super.setPosition(options);
|
||||
const sheetBody = this.element.find(".sheet-body");
|
||||
const bodyHeight = position.height - 192;
|
||||
sheetBody.css("height", bodyHeight);
|
||||
return position;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/*async _onDropItem(event, dragData) {
|
||||
let item = await WastelandUtility.searchItem( dragData)
|
||||
this.actor.preprocessItem( event, item, true )
|
||||
super._onDropItem(event, dragData)
|
||||
}*/
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** @override */
|
||||
_updateObject(event, formData) {
|
||||
// Update the Actor
|
||||
return this.object.update(formData);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* -------------------------------------------- */
|
||||
import { WastelandUtility } from "./wasteland-utility.js";
|
||||
import { WastelandRollDialog } from "./wasteland-roll-dialog.js";
|
||||
import { WastelandRollDialog } from "./applications/wasteland-roll-dialog.mjs";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
const __degatsBonus = [-2, -2, -1, -1, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 8, 8, 9, 9, 10, 10]
|
||||
@@ -37,11 +37,12 @@ export class WastelandActor extends Actor {
|
||||
return actor;
|
||||
}
|
||||
|
||||
const skills = await WastelandUtility.loadCompendium("fvtt-wasteland.skills")
|
||||
if (data.type == 'personnage') {
|
||||
const skills = await WastelandUtility.loadCompendium("fvtt-wasteland.skills")
|
||||
data.items = skills.map(i => i.toObject())
|
||||
}
|
||||
if (data.type == 'pnj') {
|
||||
if (data.type == 'creature') {
|
||||
data.items = skills.filter(i=>i.name.toLowerCase().includes("mêlée")).map(i => i.toObject())
|
||||
}
|
||||
|
||||
return super.create(data, options);
|
||||
@@ -49,10 +50,11 @@ export class WastelandActor extends Actor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
prepareArme(arme) {
|
||||
arme = duplicate(arme)
|
||||
arme = foundry.utils.duplicate(arme)
|
||||
let combat = this.getCombatValues()
|
||||
if (arme.system.typearme == "contact" || arme.system.typearme == "contactjet") {
|
||||
arme.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée"))
|
||||
arme.system.isMelee = true
|
||||
arme.system.competence = foundry.utils.duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée"))
|
||||
arme.system.attrKey = "pui"
|
||||
arme.system.totalDegats = arme.system.degats + "+" + combat.bonusDegatsTotal
|
||||
arme.system.totalOffensif = this.system.attributs.pui.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff
|
||||
@@ -61,7 +63,8 @@ export class WastelandActor extends Actor {
|
||||
}
|
||||
}
|
||||
if (arme.system.typearme == "jet" || arme.system.typearme == "tir") {
|
||||
arme.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "armes à distance"))
|
||||
arme.system.isDistance = true
|
||||
arme.system.competence = foundry.utils.duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "armes à distance"))
|
||||
arme.system.attrKey = "adr"
|
||||
arme.system.totalOffensif = this.system.attributs.adr.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff
|
||||
arme.system.totalDegats = arme.system.degats
|
||||
@@ -73,9 +76,9 @@ export class WastelandActor extends Actor {
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
prepareBouclier(bouclier) {
|
||||
bouclier = duplicate(bouclier)
|
||||
bouclier = foundry.utils.duplicate(bouclier)
|
||||
let combat = this.getCombatValues()
|
||||
bouclier.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée"))
|
||||
bouclier.system.competence = foundry.utils.duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée"))
|
||||
bouclier.system.attrKey = "pui"
|
||||
bouclier.system.totalDegats = bouclier.system.degats + "+" + combat.bonusDegatsTotal
|
||||
bouclier.system.totalOffensif = this.system.attributs.pui.value + bouclier.system.competence.system.niveau
|
||||
@@ -116,18 +119,36 @@ export class WastelandActor extends Actor {
|
||||
WastelandUtility.sortArrayObjectsByName(items)
|
||||
return items
|
||||
}
|
||||
getArtifex() {
|
||||
return this.getItemSorted(["artifex"])
|
||||
}
|
||||
getCapacites() {
|
||||
return this.getItemSorted(["capacite"])
|
||||
}
|
||||
getPouvoirs() {
|
||||
return this.getItemSorted(["pouvoir"])
|
||||
}
|
||||
getDons() {
|
||||
return this.getItemSorted(["don"])
|
||||
}
|
||||
getHubris() {
|
||||
return this.getItemSorted(["hubris"])
|
||||
}
|
||||
getEquipments() {
|
||||
return this.getItemSorted(["equipement"])
|
||||
}
|
||||
getMonnaies() {
|
||||
return this.getItemSorted(["monnaie"])
|
||||
}
|
||||
getMutations() {
|
||||
return this.getItemSorted(["mutation"])
|
||||
}
|
||||
getArmors() {
|
||||
return this.getItemSorted(["protection"])
|
||||
}
|
||||
getPeuple() {
|
||||
return this.items.find(item => item.type == "peuple")
|
||||
}
|
||||
getOrigine() {
|
||||
return this.items.find(item => item.type == "origine")
|
||||
}
|
||||
@@ -141,7 +162,7 @@ export class WastelandActor extends Actor {
|
||||
getSkills() {
|
||||
let comp = []
|
||||
for (let item of this.items) {
|
||||
item = duplicate(item)
|
||||
item = foundry.utils.duplicate(item)
|
||||
if (item.type == "competence") {
|
||||
item.system.attribut1total = item.system.niveau + (this.system.attributs[item.system.attribut1]?.value || 0)
|
||||
item.system.attribut2total = item.system.niveau + (this.system.attributs[item.system.attribut2]?.value || 0)
|
||||
@@ -203,7 +224,7 @@ export class WastelandActor extends Actor {
|
||||
if (this.system.sante.base != newSante) {
|
||||
this.update({ 'system.sante.base': newSante })
|
||||
}
|
||||
let newPsyche = (this.system.attributs.cla.value + this.system.attributs.tre.value) * this.system.biodata.psychemultiplier + 5
|
||||
let newPsyche = ((this.system.attributs.cla.value + this.system.attributs.tre.value) * 2) + 5
|
||||
if (this.system.psyche.fullmax != newPsyche) {
|
||||
this.update({ 'system.psyche.fullmax': newPsyche })
|
||||
}
|
||||
@@ -219,7 +240,7 @@ export class WastelandActor extends Actor {
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
incDecSante(value) {
|
||||
let sante = duplicate(this.system.sante)
|
||||
let sante = foundry.utils.duplicate(this.system.sante)
|
||||
sante.letaux += value
|
||||
this.update({ 'system.sante': sante })
|
||||
}
|
||||
@@ -227,7 +248,7 @@ export class WastelandActor extends Actor {
|
||||
getItemById(id) {
|
||||
let item = this.items.find(item => item.id == id);
|
||||
if (item) {
|
||||
item = duplicate(item)
|
||||
item = foundry.utils.duplicate(item)
|
||||
}
|
||||
return item;
|
||||
}
|
||||
@@ -277,12 +298,16 @@ export class WastelandActor extends Actor {
|
||||
changeEclat(value) {
|
||||
let newE = this.system.eclat.value
|
||||
newE += value
|
||||
// Empêcher que l'éclat devienne négatif
|
||||
if (newE < 0) {
|
||||
newE = 0
|
||||
}
|
||||
this.update({ 'system.eclat.value': newE })
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
subPointsPsyche(value) {
|
||||
let psyche = duplicate(this.system.psyche)
|
||||
let psyche = foundry.utils.duplicate(this.system.psyche)
|
||||
psyche.currentmax -= value
|
||||
this.update( {'system.psyche': psyche})
|
||||
}
|
||||
@@ -321,13 +346,13 @@ export class WastelandActor extends Actor {
|
||||
getSubActors() {
|
||||
let subActors = [];
|
||||
for (let id of this.system.subactors) {
|
||||
subActors.push(duplicate(game.actors.get(id)));
|
||||
subActors.push(foundry.utils.duplicate(game.actors.get(id)));
|
||||
}
|
||||
return subActors;
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
async addSubActor(subActorId) {
|
||||
let subActors = duplicate(this.system.subactors);
|
||||
let subActors = foundry.utils.duplicate(this.system.subactors);
|
||||
subActors.push(subActorId);
|
||||
await this.update({ 'system.subactors': subActors });
|
||||
}
|
||||
@@ -358,11 +383,37 @@ export class WastelandActor extends Actor {
|
||||
/* -------------------------------------------- */
|
||||
async setPredilectionUsed(compId, predIdx) {
|
||||
let comp = this.items.get(compId)
|
||||
let pred = duplicate(comp.system.predilections)
|
||||
let pred = foundry.utils.duplicate(comp.system.predilections)
|
||||
pred[predIdx].used = true
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: compId, 'system.predilections': pred }])
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async resetAllPredilections() {
|
||||
let updates = []
|
||||
for (let item of this.items) {
|
||||
if (item.type === "competence" && item.system.predilections && item.system.predilections.length > 0) {
|
||||
let pred = foundry.utils.duplicate(item.system.predilections)
|
||||
let hasUsed = false
|
||||
for (let p of pred) {
|
||||
if (p.used) {
|
||||
p.used = false
|
||||
hasUsed = true
|
||||
}
|
||||
}
|
||||
if (hasUsed) {
|
||||
updates.push({ _id: item.id, 'system.predilections': pred })
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updates.length > 0) {
|
||||
await this.updateEmbeddedDocuments('Item', updates)
|
||||
ui.notifications.info(`${updates.length} prédilection(s) réinitialisée(s) pour ${this.name}`)
|
||||
} else {
|
||||
ui.notifications.info(`Aucune prédilection à réinitialiser pour ${this.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getInitiativeScore( ) {
|
||||
return Number(this.system.attributs.adr.value) + Number(this.system.combat.initbonus)
|
||||
@@ -381,7 +432,7 @@ export class WastelandActor extends Actor {
|
||||
}
|
||||
if ( arme.system.totalDefensif > maxDef) {
|
||||
maxDef = arme.system.totalDefensif
|
||||
bestArme = duplicate(arme)
|
||||
bestArme = foundry.utils.duplicate(arme)
|
||||
}
|
||||
}
|
||||
return bestArme
|
||||
@@ -395,71 +446,70 @@ export class WastelandActor extends Actor {
|
||||
rollData.actorId = this.id
|
||||
rollData.tokenId = this.token?.id
|
||||
rollData.img = this.img
|
||||
rollData.canEclatDoubleD20 = true // Always true in Wastelan
|
||||
rollData.canEclatDoubleD20 = this.getEclat() >= 1 // Vérifier que l'acteur a au moins 1 point d'éclat
|
||||
rollData.doubleD20 = false
|
||||
rollData.attributs = WastelandUtility.getAttributs()
|
||||
rollData.config = duplicate(game.system.wasteland.config)
|
||||
rollData.config = foundry.utils.duplicate(game.system.wasteland.config)
|
||||
rollData.desavantages = {}
|
||||
rollData.isMonte = this.system.combat.monte
|
||||
|
||||
if (attrKey) {
|
||||
rollData.attrKey = attrKey
|
||||
if (attrKey != "tochoose") {
|
||||
rollData.actionImg = "systems/fvtt-wasteland/assets/icons/" + this.system.attributs[attrKey].labelnorm + ".webp"
|
||||
rollData.attr = duplicate(this.system.attributs[attrKey])
|
||||
rollData.attr = foundry.utils.duplicate(this.system.attributs[attrKey])
|
||||
}
|
||||
}
|
||||
if (compId) {
|
||||
rollData.competence = duplicate(this.items.get(compId) || {})
|
||||
rollData.competence = foundry.utils.duplicate(this.items.get(compId) || {})
|
||||
rollData.actionImg = rollData.competence?.img
|
||||
}
|
||||
if (compName) {
|
||||
rollData.competence = duplicate(this.items.find( item => item.name.toLowerCase() == compName.toLowerCase()) || {})
|
||||
rollData.competence = foundry.utils.duplicate(this.items.find( item => item.name.toLowerCase() == compName.toLowerCase()) || {})
|
||||
rollData.actionImg = rollData.competence?.img
|
||||
}
|
||||
return rollData
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollAttribut(attrKey) {
|
||||
async launchRoll(rollData) {
|
||||
console.log("RollData", rollData)
|
||||
await WastelandRollDialog.create(this, rollData)
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
rollAttribut(attrKey) {
|
||||
let rollData = this.getCommonRollData(attrKey)
|
||||
let rollDialog = await WastelandRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
this.launchRoll(rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollCompetence(attrKey, compId) {
|
||||
rollCompetence(attrKey, compId) {
|
||||
let rollData = this.getCommonRollData(attrKey, compId)
|
||||
console.log("RollDatra", rollData)
|
||||
let rollDialog = await WastelandRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
this.launchRoll(rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollCharme(charmeId) {
|
||||
let rollData = this.getCommonRollData("cla")
|
||||
rollData.charme = duplicate(this.items.get(charmeId) || {})
|
||||
rollData.charme = foundry.utils.duplicate(this.items.get(charmeId) || {})
|
||||
rollData.charmeDice = "1d4"
|
||||
console.log("RollDatra", rollData)
|
||||
let rollDialog = await WastelandRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
this.launchRoll(rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollPouvoir(pouvoirId) {
|
||||
let comp = this.items.find(comp => comp.type == "competence" && comp.name.toLowerCase() == "savoir : runes")
|
||||
if ( !comp) {
|
||||
ui.notifications.warn("La compétence Savoirs : Runes n'a pas été trouvée, abandon.")
|
||||
return
|
||||
async rollPouvoir(pouvoirId) {
|
||||
let pouvoir = foundry.utils.duplicate(this.items.get(pouvoirId) || {})
|
||||
if (pouvoir?.system) {
|
||||
let rollData = this.getCommonRollData(pouvoir.system.attribut1, undefined, pouvoir.system.competence)
|
||||
if (!rollData.competence) {
|
||||
ui.notifications.error("Le pouvoir " + pouvoir.name + " n'a pas de compétence associée. Editez le pouvoir avec la compétence associée.")
|
||||
return
|
||||
}
|
||||
rollData.pouvoir = pouvoir
|
||||
rollData.difficulte = pouvoir.system.seuil
|
||||
this.launchRoll(rollData)
|
||||
}
|
||||
let rollData = this.getCommonRollData("cla", undefined, "Savoir : Runes")
|
||||
rollData.rune = duplicate(this.items.get(runeId) || {})
|
||||
rollData.difficulte = rollData.rune?.system?.seuil || 0
|
||||
rollData.runemode = "prononcer"
|
||||
rollData.runeame = 1
|
||||
console.log("runeData", rollData)
|
||||
let rollDialog = await WastelandRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
}
|
||||
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
async rollArmeOffensif(armeId) {
|
||||
let arme = this.items.get(armeId)
|
||||
@@ -471,9 +521,13 @@ export class WastelandActor extends Actor {
|
||||
}
|
||||
let rollData = this.getCommonRollData(arme.system.attrKey, arme.system.competence._id)
|
||||
rollData.arme = arme
|
||||
console.log("ARME!", rollData)
|
||||
let rollDialog = await WastelandRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
rollData.typeAttaque = "assaut"
|
||||
rollData.typeCouvert = "aucun"
|
||||
rollData.hasDesavantageBonus = true
|
||||
rollData.visee = false
|
||||
rollData.ciblecourt = false
|
||||
rollData.cibleconsciente = false
|
||||
this.launchRoll(rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -485,7 +539,7 @@ export class WastelandActor extends Actor {
|
||||
if (arme.type == "bouclier") {
|
||||
arme = this.prepareBouclier(arme)
|
||||
}
|
||||
let roll = new Roll(arme.system.totalDegats).roll({ async: false })
|
||||
let roll = await new Roll(arme.system.totalDegats).roll()
|
||||
await WastelandUtility.showDiceSoNice(roll, game.settings.get("core", "rollMode"));
|
||||
let rollData = {
|
||||
arme: arme,
|
||||
@@ -496,8 +550,52 @@ export class WastelandActor extends Actor {
|
||||
actionImg: arme.img,
|
||||
}
|
||||
WastelandUtility.createChatWithRollMode(rollData.alias, {
|
||||
content: await renderTemplate(`systems/fvtt-wasteland/templates/chat-degats-result.html`, rollData)
|
||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-degats-result-v2.hbs`, rollData)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollAssommer() {
|
||||
let rollData = this.getCommonRollData("adr", undefined, "Mouvements")
|
||||
rollData.specialAction = "Assommer"
|
||||
rollData.typeAttaque = "assommer"
|
||||
rollData.typeCouvert = "aucun"
|
||||
rollData.hasDesavantageBonus = true
|
||||
if (rollData.defender) {
|
||||
rollData.selectDifficulte = false
|
||||
rollData.difficulte = rollData.defender.system.attributs.tre.value * 2
|
||||
}
|
||||
this.launchRoll(rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollFuir() {
|
||||
let rollData = this.getCommonRollData("adr", undefined, "Mouvements")
|
||||
rollData.specialAction = "Fuir"
|
||||
rollData.typeAttaque = "fuir"
|
||||
rollData.typeCouvert = "aucun"
|
||||
rollData.hasDesavantageBonus = true
|
||||
if (rollData.defender) {
|
||||
rollData.selectDifficulte = false
|
||||
let comp = rollData.defender.items.find(it => it.type == "competence" && it.name.toLowerCase() == "mouvements")
|
||||
rollData.difficulte = rollData.defender.system.attributs.adr.value + ((comp) ? comp.system.niveau : rollData.defender.system.attributs.adr.value)
|
||||
}
|
||||
this.launchRoll(rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollImmobiliser() {
|
||||
let rollData = this.getCommonRollData("pui", undefined, "Mêlée")
|
||||
rollData.specialAction = "Immobiliser"
|
||||
rollData.typeAttaque = "immobiliser"
|
||||
rollData.typeCouvert = "aucun"
|
||||
rollData.hasDesavantageBonus = true
|
||||
if (rollData.defender) {
|
||||
rollData.selectDifficulte = false
|
||||
rollData.difficulte = rollData.armeDefense ? rollData.armeDefense.system.totalDefensif : 10
|
||||
}
|
||||
this.launchRoll(rollData)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export class WastelandCombat extends Combat {
|
||||
const c = this.combatants.get(ids[cId]);
|
||||
let id = c._id || c.id;
|
||||
let initBonus = c.actor ? c.actor.getInitiativeScore() : 0
|
||||
let roll = new Roll("1d10 + "+initBonus).roll({ async: false})
|
||||
let roll = await new Roll("1d10 + "+initBonus).roll()
|
||||
await WastelandUtility.showDiceSoNice(roll, game.settings.get("core", "rollMode"))
|
||||
//console.log("Init bonus", initBonus, roll.total)
|
||||
await this.updateEmbeddedDocuments("Combatant", [ { _id: id, initiative: roll.total } ]);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* -------------------------------------------- */
|
||||
|
||||
import { WastelandUtility } from "./wasteland-utility.js";
|
||||
import { WastelandRollDialog } from "./wasteland-roll-dialog.js";
|
||||
import { WastelandRollDialog } from "./applications/wasteland-roll-dialog.mjs";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class WastelandCommands {
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
export const WASTELAND_CONFIG = {
|
||||
|
||||
attributs: {
|
||||
"adr": "Adresse",
|
||||
"pui": "Puissance",
|
||||
"cla": "Clairvoyance",
|
||||
"pre": "Présence",
|
||||
"tre": "Trempe"
|
||||
},
|
||||
cheminpouvoir : {
|
||||
"force": "Chemin des Forces",
|
||||
"forge": "Chemin des Forges",
|
||||
"echo": "Chemin des Échos",
|
||||
"reflet": "Chemin des Reflets",
|
||||
"ame": "Chemin des Âmes",
|
||||
"mort": "Chemin des Morts"
|
||||
"mort": "Chemin des Morts",
|
||||
"vie": "Chemin de Vie",
|
||||
"guerre": "Chemin des guerres",
|
||||
"horizon": "Chemin vers l'horizon",
|
||||
"voleurinvisible": "Chemin du Voleur invisible",
|
||||
"nuit": "Chemin des Nuits",
|
||||
"oiseaux": "Chemin des Oiseaux"
|
||||
},
|
||||
hubrisType: {
|
||||
"mental": "Mental",
|
||||
"physique": "Physique",
|
||||
},
|
||||
charmetype: {
|
||||
tour: "Tour",
|
||||
@@ -20,4 +37,69 @@ export const WASTELAND_CONFIG = {
|
||||
"1d12": "1d12",
|
||||
"1d20": "1d20",
|
||||
},
|
||||
artifexType: {
|
||||
"vapeur": "Vapeur",
|
||||
"mecanique": "Mécanique",
|
||||
"chimie": "Chimie",
|
||||
"electricite": "Électricité",
|
||||
"chimerie": "Chimérie",
|
||||
},
|
||||
typeArmeOptions: {
|
||||
contact: "Arme de contact",
|
||||
contactjet: "Arme de contact et de Jet",
|
||||
jet: "Arme de Jet",
|
||||
tir: "Arme de Tir",
|
||||
special: "Spécial (capacité/don)"
|
||||
},
|
||||
attaques: {
|
||||
assaut: "Assaut",
|
||||
precise: "Attaque Précise",
|
||||
feinte: "Feinte",
|
||||
coupbas: "Coup Bas",
|
||||
charger: "Charger",
|
||||
contenir: "Contenir",
|
||||
desarmer: "Désarmer"
|
||||
},
|
||||
couverts: {
|
||||
aucun: { name: "Aucun", value: 0 },
|
||||
rondache: { name: "Couvert Léger", value: -2 },
|
||||
pavois: { name: "Couvert Moyen", value: -5 },
|
||||
complet: { name: "Couvert Complet", value: -10 },
|
||||
},
|
||||
listePortees: {
|
||||
"10": "Moins que Courte",
|
||||
"15": "Courte ou plus",
|
||||
"20": "Moyenne ou plus",
|
||||
"25": "Longue ou plus"
|
||||
},
|
||||
difficulteOptions: {
|
||||
"0": "Aucune/Inconnue",
|
||||
"5": "Facile (5)",
|
||||
"6": "(6)",
|
||||
"7": "(7)",
|
||||
"8": "(8)",
|
||||
"9": "(9)",
|
||||
"10": "Moyenne (10)",
|
||||
"11": "(11)",
|
||||
"12": "(12)",
|
||||
"13": "(13)",
|
||||
"14": "(14)",
|
||||
"15": "Ardue (15)",
|
||||
"16": "(16)",
|
||||
"17": "(17)",
|
||||
"18": "(18)",
|
||||
"19": "(19)",
|
||||
"20": "Hasardeuse (20)",
|
||||
"21": "(21)",
|
||||
"22": "(22)",
|
||||
"23": "(23)",
|
||||
"24": "(24)",
|
||||
"25": "Insensée (25)",
|
||||
"26": "(26)",
|
||||
"27": "(27)",
|
||||
"28": "(28)",
|
||||
"29": "(29)",
|
||||
"30": "Pure Folie (30)"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
import { WastelandUtility } from "./wasteland-utility.js";
|
||||
|
||||
/**
|
||||
* Extend the basic ItemSheet with some very simple modifications
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class WastelandItemSheet extends ItemSheet {
|
||||
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
|
||||
return mergeObject(super.defaultOptions, {
|
||||
classes: ["fvtt-wasteland", "sheet", "item"],
|
||||
template: "systems/fvtt-wasteland/templates/item-sheet.html",
|
||||
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
||||
width: 620,
|
||||
height: 550
|
||||
//tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description"}]
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
_getHeaderButtons() {
|
||||
let buttons = super._getHeaderButtons();
|
||||
// Add "Post to chat" button
|
||||
// We previously restricted this to GM and editable items only. If you ever find this comment because it broke something: eh, sorry!
|
||||
buttons.unshift(
|
||||
{
|
||||
class: "post",
|
||||
icon: "fas fa-comment",
|
||||
onclick: ev => { }
|
||||
})
|
||||
return buttons
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** @override */
|
||||
setPosition(options = {}) {
|
||||
const position = super.setPosition(options);
|
||||
const sheetBody = this.element.find(".sheet-body");
|
||||
const bodyHeight = position.height - 192;
|
||||
sheetBody.css("height", bodyHeight);
|
||||
if (this.item.type.includes('weapon')) {
|
||||
position.width = 640;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async getData() {
|
||||
const objectData = duplicate(this.object)
|
||||
let itemData = objectData
|
||||
let formData = {
|
||||
title: this.title,
|
||||
id: this.id,
|
||||
type: objectData.type,
|
||||
img: objectData.img,
|
||||
name: objectData.name,
|
||||
editable: this.isEditable,
|
||||
cssClass: this.isEditable ? "editable" : "locked",
|
||||
attributs: WastelandUtility.getAttributs(),
|
||||
config: duplicate(game.system.wasteland.config),
|
||||
data: itemData.system,
|
||||
system: itemData.system,
|
||||
limited: this.object.limited,
|
||||
options: this.options,
|
||||
owner: this.document.isOwner,
|
||||
description: await TextEditor.enrichHTML(this.object.system.description, {async: true}),
|
||||
isGM: game.user.isGM
|
||||
}
|
||||
|
||||
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() {
|
||||
let chatData = duplicate(WastelandUtility.data(this.item));
|
||||
if (this.actor) {
|
||||
chatData.actor = { id: this.actor.id };
|
||||
}
|
||||
// Don't post any image for the item (which would leave a large gap) if the default image is used
|
||||
if (chatData.img.includes("/blank.png")) {
|
||||
chatData.img = null;
|
||||
}
|
||||
// JSON object for easy creation
|
||||
chatData.jsondata = JSON.stringify(
|
||||
{
|
||||
compendium: "postedItem",
|
||||
payload: chatData,
|
||||
});
|
||||
|
||||
renderTemplate('systems/fvtt-Wasteland-rpg/templates/post-item.html', chatData).then(html => {
|
||||
let chatOptions = WastelandUtility.chatDataSetup(html);
|
||||
ChatMessage.create(chatOptions)
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** @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);
|
||||
})
|
||||
html.find('.edit-prediction').change(ev => {
|
||||
const li = $(ev.currentTarget).parents(".prediction-item")
|
||||
let index = li.data("prediction-index")
|
||||
let pred = duplicate(this.object.system.predilections)
|
||||
pred[index].name = ev.currentTarget.value
|
||||
this.object.update( { 'data.predilections': pred })
|
||||
})
|
||||
html.find('.delete-prediction').click(ev => {
|
||||
const li = $(ev.currentTarget).parents(".prediction-item")
|
||||
let index = li.data("prediction-index")
|
||||
let pred = duplicate(this.object.system.predilections)
|
||||
pred.splice(index,1)
|
||||
this.object.update( { 'data.predilections': pred })
|
||||
})
|
||||
html.find('.use-prediction').change(ev => {
|
||||
const li = $(ev.currentTarget).parents(".prediction-item")
|
||||
let index = li.data("prediction-index")
|
||||
let pred = duplicate(this.object.system.predilections)
|
||||
pred[index].used = ev.currentTarget.checked
|
||||
this.object.update( { 'data.predilections': pred })
|
||||
})
|
||||
html.find('#add-predilection').click(ev => {
|
||||
let pred = duplicate(this.object.system.predilections)
|
||||
pred.push( { name: "Nouvelle prédilection", used: false })
|
||||
this.object.update( { 'data.predilections': pred })
|
||||
})
|
||||
// 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-wasteland/templates/item-${type}-sheet.html`;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** @override */
|
||||
_updateObject(event, formData) {
|
||||
return this.object.update(formData);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,9 @@ export const defaultItemImg = {
|
||||
artifex: "systems/fvtt-wasteland/assets/icons/artifact.webp",
|
||||
heritage: "systems/fvtt-wasteland/assets/icons/legacy.webp",
|
||||
charme: "systems/fvtt-wasteland/assets/icons/charm.webp",
|
||||
peuple: "systems/fvtt-wasteland/assets/icons/people.webp",
|
||||
don: "systems/fvtt-wasteland/assets/icons/don.webp",
|
||||
hubris: "systems/fvtt-wasteland/assets/icons/hubris.webp",
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,14 +9,17 @@
|
||||
/* -------------------------------------------- */
|
||||
// Import Modules
|
||||
import { WastelandActor } from "./wasteland-actor.js";
|
||||
import { WastelandItemSheet } from "./wasteland-item-sheet.js";
|
||||
import { WastelandActorSheet } from "./wasteland-actor-sheet.js";
|
||||
//import { WastelandNPCSheet } from "./wasteland-npc-sheet.js";
|
||||
import { WastelandUtility } from "./wasteland-utility.js";
|
||||
import { WastelandCombat } from "./wasteland-combat.js";
|
||||
import { WastelandItem } from "./wasteland-item.js";
|
||||
import { WASTELAND_CONFIG } from "./wasteland-config.js";
|
||||
|
||||
// Import DataModels
|
||||
import * as models from "./models/index.mjs";
|
||||
|
||||
// Import AppV2 Sheets
|
||||
import * as sheets from "./applications/sheets/_module.mjs";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Foundry VTT Initialization */
|
||||
/* -------------------------------------------- */
|
||||
@@ -28,9 +31,9 @@ Hooks.once("init", async function () {
|
||||
/* -------------------------------------------- */
|
||||
// preload handlebars templates
|
||||
WastelandUtility.preloadHandlebarsTemplates();
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
// Set an initiative formula for the system
|
||||
// Set an initiative formula for the system
|
||||
CONFIG.Combat.initiative = {
|
||||
formula: "1d6",
|
||||
decimals: 1
|
||||
@@ -45,60 +48,123 @@ Hooks.once("init", async function () {
|
||||
// Define custom Entity classes
|
||||
CONFIG.Combat.documentClass = WastelandCombat
|
||||
CONFIG.Actor.documentClass = WastelandActor
|
||||
CONFIG.Item.documentClass = WastelandItem
|
||||
game.system.wasteland = {
|
||||
config: WASTELAND_CONFIG
|
||||
CONFIG.Actor.dataModels = {
|
||||
personnage: models.PersonnageDataModel,
|
||||
creature: models.CreatureDataModel
|
||||
}
|
||||
|
||||
CONFIG.Item.documentClass = WastelandItem
|
||||
CONFIG.Item.dataModels = {
|
||||
arme: models.ArmeDataModel,
|
||||
artifex: models.ArtifexDataModel,
|
||||
bouclier: models.BouclierDataModel,
|
||||
capacite: models.CapaciteDataModel,
|
||||
charme: models.CharmeDataModel,
|
||||
competence: models.CompetenceDataModel,
|
||||
don: models.DonDataModel,
|
||||
equipement: models.EquipementDataModel,
|
||||
heritage: models.HeritageDataModel,
|
||||
hubris: models.HubrisDataModel,
|
||||
metier: models.MetierDataModel,
|
||||
monnaie: models.MonnaieDataModel,
|
||||
mutation: models.MutationDataModel,
|
||||
origine: models.OrigineDataModel,
|
||||
peuple: models.PeupleDataModel,
|
||||
pouvoir: models.PouvoirDataModel,
|
||||
protection: models.ProtectionDataModel
|
||||
}
|
||||
|
||||
game.system.wasteland = {
|
||||
config: WASTELAND_CONFIG,
|
||||
models,
|
||||
sheets
|
||||
}
|
||||
|
||||
// Initialize dynamic config options that need to be available immediately
|
||||
// Create option lists inline to ensure they're available
|
||||
const listeNiveauSkill = {}
|
||||
for (let i = 0; i <= 10; i++) {
|
||||
listeNiveauSkill[`${i}`] = `${i}`
|
||||
}
|
||||
game.system.wasteland.config.listeNiveauSkill = listeNiveauSkill
|
||||
|
||||
const listeNiveauAttribut = {}
|
||||
for (let i = 0; i <= 10; i++) {
|
||||
listeNiveauAttribut[`${i}`] = `${i}`
|
||||
}
|
||||
game.system.wasteland.config.listeNiveauAttribut = listeNiveauAttribut
|
||||
|
||||
const listeNiveauCreature = {}
|
||||
for (let i = 0; i <= 35; i++) {
|
||||
listeNiveauCreature[`${i}`] = `${i}`
|
||||
}
|
||||
game.system.wasteland.config.listeNiveauCreature = listeNiveauCreature
|
||||
|
||||
const modificateurOptions = []
|
||||
for (let i = -15; i <= 15; i++) {
|
||||
modificateurOptions.push({ key: i, label: `${i >= 0 ? '+' : ''}${i}` })
|
||||
}
|
||||
game.system.wasteland.config.modificateurOptions = modificateurOptions
|
||||
|
||||
const pointsAmeOptions = {}
|
||||
for (let i = 0; i <= 20; i++) {
|
||||
pointsAmeOptions[`${i}`] = `${i}`
|
||||
}
|
||||
game.system.wasteland.config.pointsAmeOptions = pointsAmeOptions
|
||||
|
||||
/* -------------------------------------------- */
|
||||
// Register sheet application classes
|
||||
Actors.unregisterSheet("core", ActorSheet);
|
||||
Actors.registerSheet("fvtt-wasteland", WastelandActorSheet, { types: ["personnage"], makeDefault: true })
|
||||
//Actors.registerSheet("fvtt-wasteland", WastelandNPCSheet, { types: ["npc"], makeDefault: false });
|
||||
|
||||
// Register AppV2 Actor Sheets
|
||||
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
|
||||
foundry.documents.collections.Actors.registerSheet("fvtt-wasteland", sheets.WastelandPersonnageSheet, { types: ["personnage"], makeDefault: true });
|
||||
foundry.documents.collections.Actors.registerSheet("fvtt-wasteland", sheets.WastelandCreatureSheet, { types: ["creature"], makeDefault: true });
|
||||
|
||||
Items.unregisterSheet("core", ItemSheet);
|
||||
Items.registerSheet("fvtt-wasteland", WastelandItemSheet, { makeDefault: true })
|
||||
// Register AppV2 Item Sheets
|
||||
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandArmeSheet, { types: ["arme"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandArtifexSheet, { types: ["artifex"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandBouclierSheet, { types: ["bouclier"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandCapaciteSheet, { types: ["capacite"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandCharmeSheet, { types: ["charme"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandCompetenceSheet, { types: ["competence"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandDonSheet, { types: ["don"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandEquipementSheet, { types: ["equipement"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandHeritageSheet, { types: ["heritage"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandHubrisSheet, { types: ["hubris"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandMetierSheet, { types: ["metier"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandMonnaieSheet, { types: ["monnaie"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandMutationSheet, { types: ["mutation"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandOrigineSheet, { types: ["origine"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandPeupleSheet, { types: ["peuple"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandPouvoirSheet, { types: ["pouvoir"], makeDefault: true });
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-wasteland", sheets.WastelandProtectionSheet, { types: ["protection"], makeDefault: true });
|
||||
|
||||
WastelandUtility.init();
|
||||
|
||||
|
||||
});
|
||||
|
||||
/* -------------------------------------------- */
|
||||
function welcomeMessage() {
|
||||
async function welcomeMessage() {
|
||||
const templateData = {};
|
||||
const html = await foundry.applications.handlebars.renderTemplate("systems/fvtt-wasteland/templates/chat-welcome-message.hbs", templateData);
|
||||
|
||||
ChatMessage.create({
|
||||
user: game.user.id,
|
||||
whisper: [game.user.id],
|
||||
content: `<div id="welcome-message-Wasteland"><span class="rdd-roll-part">
|
||||
<strong>Bienvenue dans les Jeunes Royaumes de Wasteland !</strong>
|
||||
<p>Les livres de Wasteland sont nécessaires pour jouer : https://www.titam-france.fr</p>
|
||||
<p>Wasteland est jeu de rôle publié par Titam France/Sombres projets, tout les droits leur appartiennent.</p>
|
||||
<p>Système développé par LeRatierBretonnien, support sur le <a href="https://discord.gg/pPSDNJk">Discord FR de Foundry</a>.</p>
|
||||
` });
|
||||
content: html
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
// Register world usage statistics
|
||||
function registerUsageCount( registerKey ) {
|
||||
if ( game.user.isGM ) {
|
||||
game.settings.register(registerKey, "world-key", {
|
||||
name: "Unique world key",
|
||||
scope: "world",
|
||||
config: false,
|
||||
default: "",
|
||||
type: String
|
||||
});
|
||||
|
||||
let worldKey = game.settings.get(registerKey, "world-key")
|
||||
if ( worldKey == undefined || worldKey == "" ) {
|
||||
worldKey = randomID(32)
|
||||
game.settings.set(registerKey, "world-key", worldKey )
|
||||
}
|
||||
// Simple API counter
|
||||
let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.version}"`
|
||||
//$.ajaxSetup({
|
||||
//headers: { 'Access-Control-Allow-Origin': '*' }
|
||||
//})
|
||||
$.ajax(regURL)
|
||||
/* -------------------------------------------- */
|
||||
async function importDefaultScene() {
|
||||
let exists = game.scenes.find(j => j.name == "Accueil");
|
||||
if (!exists) {
|
||||
const scenes = await WastelandUtility.loadCompendium("fvtt-wasteland.scenes")
|
||||
let newDocuments = scenes.filter(i => i.name == "Accueil");
|
||||
await game.scenes.documentClass.create(newDocuments);
|
||||
game.scenes.find(i => i.name == "Accueil").activate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +174,7 @@ function registerUsageCount( registerKey ) {
|
||||
Hooks.once("ready", function () {
|
||||
|
||||
WastelandUtility.ready();
|
||||
|
||||
// User warning
|
||||
if (!game.user.isGM && game.user.character == undefined) {
|
||||
ui.notifications.info("Attention ! Aucun personnage n'est relié au joueur !");
|
||||
@@ -116,9 +183,24 @@ Hooks.once("ready", function () {
|
||||
user: game.user._id
|
||||
});
|
||||
}
|
||||
|
||||
registerUsageCount('fvtt-wasteland')
|
||||
if (!game.user.isGM && game.user.character && !game.user.character.prototypeToken.actorLink) {
|
||||
ui.notifications.info("Le token de du joueur n'est pas connecté à l'acteur !");
|
||||
ChatMessage.create({
|
||||
content: "<b>ATTENTION</b> Le token du joueur " + game.user.name + " n'est pas connecté à l'acteur !",
|
||||
user: game.user._id
|
||||
});
|
||||
}
|
||||
|
||||
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter=>{
|
||||
console.log("ClassCounter loaded", moduleCounter)
|
||||
moduleCounter.ClassCounter.registerUsageCount()
|
||||
}).catch(err=>
|
||||
console.log("No stats available, giving up.")
|
||||
)
|
||||
welcomeMessage();
|
||||
|
||||
importDefaultScene();
|
||||
|
||||
});
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -134,4 +216,3 @@ Hooks.on("chatMessage", (html, content, msg) => {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
import { WastelandUtility } from "./wasteland-utility.js";
|
||||
|
||||
export class WastelandRollDialog extends Dialog {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async create(actor, rollData) {
|
||||
|
||||
let options = { classes: ["WastelandDialog"], width: 340, height: 'fit-content', 'z-index': 99999 };
|
||||
let html = await renderTemplate('systems/fvtt-wasteland/templates/roll-dialog-generic.html', rollData);
|
||||
|
||||
return new WastelandRollDialog(actor, rollData, html, options);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
constructor(actor, rollData, html, options, close = undefined) {
|
||||
let buttons = {
|
||||
rolld10: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "Lancer 1d10",
|
||||
callback: () => { this.roll("1d10") }
|
||||
},
|
||||
rolld20: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "Lancer 1d20",
|
||||
callback: () => { this.roll("1d20") }
|
||||
},
|
||||
cancel: {
|
||||
icon: '<i class="fas fa-times"></i>',
|
||||
label: "Annuler",
|
||||
callback: () => { this.close() }
|
||||
}
|
||||
}
|
||||
if (rollData.charme) {
|
||||
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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
let conf = {
|
||||
title: "Test de Capacité",
|
||||
content: html,
|
||||
buttons: buttons,
|
||||
close: close
|
||||
}
|
||||
|
||||
super(conf, options);
|
||||
|
||||
this.actor = actor
|
||||
this.rollData = rollData
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
roll(dice) {
|
||||
this.rollData.mainDice = dice
|
||||
WastelandUtility.rollWasteland(this.rollData)
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
var dialog = this;
|
||||
function onLoad() {
|
||||
}
|
||||
$(function () { onLoad(); });
|
||||
|
||||
html.find('#modificateur').change(async (event) => {
|
||||
this.rollData.modificateur = Number(event.currentTarget.value)
|
||||
})
|
||||
html.find('#difficulte').change(async (event) => {
|
||||
this.rollData.difficulte = Number(event.currentTarget.value)
|
||||
})
|
||||
html.find('#attrKey').change(async (event) => {
|
||||
this.rollData.attrKey = String(event.currentTarget.value)
|
||||
})
|
||||
html.find('#runemode').change(async (event) => {
|
||||
this.rollData.runemode = String(event.currentTarget.value)
|
||||
})
|
||||
html.find('#runeame').change(async (event) => {
|
||||
this.rollData.runeame = Number(event.currentTarget.value)
|
||||
})
|
||||
html.find('#doubleD20').change(async (event) => {
|
||||
this.rollData.doubleD20 = event.currentTarget.checked
|
||||
})
|
||||
html.find('#charmeDice').change(async (event) => {
|
||||
this.rollData.charmeDice = String(event.currentTarget.value)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,17 @@ import { WastelandCommands } from "./wasteland-commands.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
const __contrecouptCharme = {
|
||||
1 : {name: "Effet chromatique", description: "" },
|
||||
3 : {name: "Enivrement Kobold", description: "" },
|
||||
5 : {name: "Mutisme superstitieux", description: "" },
|
||||
7 : {name: "Agité!", description: "" },
|
||||
9 : {name: "Somnolence", description: "" },
|
||||
11 : {name: "Manie incontrôlable", description: "" },
|
||||
13 : {name: "Malédiction des Ternes", description: "" },
|
||||
15 : {name: "La petite Mort", description: "" },
|
||||
17 : {name: "Angoisse cauchemardesque", description: "" },
|
||||
19 : {name: "Anémie Kobold", description: "" }
|
||||
}
|
||||
1 : {name: "Effet chromatique", description: "le corps du kobold prend des teintes aussi étranges que voyantes. L'effet s’estompe progressivement et 24 heures plus tard, le kobold retrouve ses couleurs d’origine." },
|
||||
3 : {name: "Enivrement Kobold", description: "très excité par son premier tour, le kobold doit immédiatement faire un autre tour, pour lequel il emploiera un dé plus gros." },
|
||||
5 : {name: "Mutisme superstitieux", description: "le kobold ne doit plus parler» pendant les prochaines 24 heures. S'il le fait malgré tout, les effets de son tour s’arrêtent." },
|
||||
7 : {name: "Agité!", description: "le kobold ne tient plus en place. Il ne peut se reposer pendant les prochaines 12 heures. Dès. que 12 heures se sont écoulées, il s'effondre comme une masse et dort 12 heures d'affilée d’un sommeil enchanté dont rien ne pourra le réveiller." },
|
||||
9 : {name: "Somnolence", description: "le kobold devient somnolent. Il n’arrive pas à se concentrer même sur une tâche simple, bäille sans arrêt, traîne les pieds et n’agit plus que de mauvaise grâce. Cela dure jusqu’à ce qu'il ait dormi au moins 12 heures." },
|
||||
11 : {name: "Manie incontrôlable", description: "le kobold est pris d’une manie incontrôlable. Dès qu'il voit un chapeau rouge, il doit suivre son porteur. Il ne mangera que si son voisin de gauche mange aussi, etc. Cela dure pendant une jour- née puis l’effet s’inverse pendant une heure : il ne suivra jamais un chapeau rouge, ne mangera jamais si son voi- sin de gauche mange, etc. Le contrecoup prend alors fin." },
|
||||
13 : {name: "Malédiction des Ternes", description: "le kobold perd cette qualité mystérieuse qui fait que les kobolds sont des kobolds et devient tout. Terne. Il perd 1d20 point(s) de Bonne Aventure (s’il doit en perdre plus qu'il n’en a, il tombe simplement à 0). Ces points perdus pourront cependant être regagnés normalement." },
|
||||
15 : {name: "La petite Mort", description: "le kobold s'endort pour 1420 heures. Rien ni personne ne pourra le tirer de ce sommeil enchanté avant que ce contrecoup ne prenne fin." },
|
||||
17 : {name: "Angoisse cauchemardesque", description: "le kobold a une brève vision de pure horreur. Il perd 1420 points de Psyché {s'il doit en perdre plus qu'il n’en a, il tombe à 0)." },
|
||||
19 : {name: "Anémie Kobold", description: "le kobold se met à saigner du nez, des oreilles et même d’autres endroits. Il perd 1420 point(s) de Santé." }
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class WastelandUtility {
|
||||
@@ -23,14 +23,12 @@ export class WastelandUtility {
|
||||
/* -------------------------------------------- */
|
||||
static async init() {
|
||||
Hooks.on('renderChatLog', (log, html, data) => WastelandUtility.chatListeners(html))
|
||||
Hooks.on("getChatLogEntryContext", (html, options) => WastelandUtility.chatRollMenu(html, options))
|
||||
Hooks.on('renderChatMessageHTML', (message, html, data) => WastelandUtility.chatListeners(html))
|
||||
Hooks.on("getChatMessageContextOptions", (html, options) => WastelandUtility.chatRollMenu(html, options))
|
||||
|
||||
Hooks.on("getCombatTrackerEntryContext", (html, options) => {
|
||||
WastelandUtility.pushInitiativeOptions(html, options);
|
||||
})
|
||||
Hooks.on("dropCanvasData", (canvas, data) => {
|
||||
WastelandUtility.dropItemOnToken(canvas, data)
|
||||
});
|
||||
|
||||
this.rollDataStore = {}
|
||||
this.defenderStore = {}
|
||||
@@ -114,6 +112,25 @@ export class WastelandUtility {
|
||||
static async ready() {
|
||||
const skills = await WastelandUtility.loadCompendium("fvtt-wasteland.skills")
|
||||
this.skills = skills.map(i => i.toObject())
|
||||
|
||||
// Note: listeNiveauSkill, listeNiveauCreature, etc. are now created in init hook
|
||||
// to be available immediately when sheets are opened
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static createDirectOptionList(min, max) {
|
||||
let options = {};
|
||||
for (let i = min; i <= max; i++) {
|
||||
options[`${i}`] = `${i}`;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
static createArrayOptionList(min, max) {
|
||||
let options = [];
|
||||
for (let i = min; i <= max; i++) {
|
||||
options.push({key:`${i}`, label:`${i}`});
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -135,15 +152,41 @@ export class WastelandUtility {
|
||||
/* -------------------------------------------- */
|
||||
static async chatListeners(html) {
|
||||
|
||||
html.on("click", '.predilection-reroll', async event => {
|
||||
$(html).on("click", '.predilection-reroll', async event => {
|
||||
let predIdx = $(event.currentTarget).data("predilection-index")
|
||||
let messageId = WastelandUtility.findChatMessageId(event.currentTarget)
|
||||
let message = game.messages.get(messageId)
|
||||
let rollData = message.getFlag("world", "wasteland-roll")
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
|
||||
// Sauvegarder le résultat précédent
|
||||
let previousResult = {
|
||||
diceResult: rollData.diceResult,
|
||||
finalResult: rollData.finalResult,
|
||||
diceFormula: rollData.diceFormula
|
||||
}
|
||||
|
||||
// Récupérer la prédilection utilisée
|
||||
let predilectionUsed = foundry.utils.duplicate(rollData.competence.system.predilections[predIdx])
|
||||
|
||||
// Marquer la prédilection comme utilisée
|
||||
await actor.setPredilectionUsed(rollData.competence._id, predIdx)
|
||||
rollData.competence = duplicate(actor.getCompetence(rollData.competence._id))
|
||||
await WastelandUtility.rollWasteland(rollData)
|
||||
|
||||
// Mettre à jour la compétence dans rollData
|
||||
rollData.competence = foundry.utils.duplicate(actor.getCompetence(rollData.competence._id))
|
||||
|
||||
// Lancer le reroll avec prédilection
|
||||
await WastelandUtility.rollWastelandPredilection(rollData, predilectionUsed, previousResult)
|
||||
})
|
||||
|
||||
$(html).on("click", '.arme-roll-degats', async event => {
|
||||
let messageId = WastelandUtility.findChatMessageId(event.currentTarget)
|
||||
let message = game.messages.get(messageId)
|
||||
let rollData = message.getFlag("world", "wasteland-roll")
|
||||
if (rollData && rollData.arme) {
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
await actor.rollArmeDegats(rollData.arme._id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -151,11 +194,12 @@ export class WastelandUtility {
|
||||
static async preloadHandlebarsTemplates() {
|
||||
|
||||
const templatePaths = [
|
||||
'systems/fvtt-wasteland/templates/editor-notes-gm.html',
|
||||
'systems/fvtt-wasteland/templates/partial-item-description.html',
|
||||
'systems/fvtt-wasteland/templates/partial-list-niveau.html'
|
||||
'systems/fvtt-wasteland/templates/editor-notes-gm.hbs',
|
||||
'systems/fvtt-wasteland/templates/partial-item-description.hbs',
|
||||
'systems/fvtt-wasteland/templates/partial-item-header.hbs',
|
||||
'systems/fvtt-wasteland/templates/partial-item-nav.hbs'
|
||||
]
|
||||
return loadTemplates(templatePaths);
|
||||
return foundry.applications.handlebars.loadTemplates(templatePaths);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -187,15 +231,6 @@ export class WastelandUtility {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static createDirectOptionList(min, max) {
|
||||
let options = {};
|
||||
for (let i = min; i <= max; i++) {
|
||||
options[`${i}`] = `${i}`;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static buildListOptions(min, max) {
|
||||
let options = ""
|
||||
@@ -220,14 +255,14 @@ export class WastelandUtility {
|
||||
|
||||
let id = rollData.rollId;
|
||||
let oldRollData = this.rollDataStore[id] || {};
|
||||
let newRollData = mergeObject(oldRollData, rollData);
|
||||
let newRollData = foundry.utils.mergeObject(oldRollData, rollData);
|
||||
this.rollDataStore[id] = newRollData;
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
static saveRollData(rollData) {
|
||||
game.socket.emit("system.fvtt-wasteland", {
|
||||
name: "msg_update_roll", data: rollData
|
||||
}); // Notify all other clients of the roll
|
||||
}); // Notify all other clients of the roll
|
||||
this.updateRollData(rollData);
|
||||
}
|
||||
|
||||
@@ -292,10 +327,10 @@ export class WastelandUtility {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static computeResult(rollData, actor) {
|
||||
static async computeResult(rollData, actor) {
|
||||
if (rollData.charme) {
|
||||
let resultIndex = false
|
||||
let resTab = duplicate(rollData.charme.system.resultats)
|
||||
let resTab = foundry.utils.duplicate(rollData.charme.system.resultats)
|
||||
for(let id in resTab) {
|
||||
let res = resTab[id]
|
||||
if (!resultIndex && rollData.finalResult >= res.value) {
|
||||
@@ -305,7 +340,7 @@ export class WastelandUtility {
|
||||
if (resultIndex) {
|
||||
rollData.charmeDuree = rollData.charme.system.resultats[resultIndex].description
|
||||
}
|
||||
let effectRoll = new Roll(rollData.charmeDice).roll({ async: false })
|
||||
let effectRoll = await new Roll(rollData.charmeDice).roll()
|
||||
if (rollData.charme.system.charmetype == "tour") {
|
||||
rollData.contrecoupResult = effectRoll.total
|
||||
if (rollData.contrecoupResult % 2 == 1) {
|
||||
@@ -313,8 +348,8 @@ export class WastelandUtility {
|
||||
}
|
||||
}
|
||||
if (rollData.charme.system.charmetype == "charme") {
|
||||
rollData.charmeSante = effectRoll.total
|
||||
actor.incDecSante(rollData.charmeSante)
|
||||
rollData.charmeSante = effectRoll.total
|
||||
actor.incDecSante(rollData.charmeSante)
|
||||
}
|
||||
} else {
|
||||
if (rollData.mainDice == "1d20") {
|
||||
@@ -330,13 +365,13 @@ export class WastelandUtility {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//console.log("Result : ", rollData)
|
||||
if (rollData.difficulte > 0 && !rollData.isDramatique) {
|
||||
rollData.isSuccess = (rollData.finalResult >= rollData.difficulte)
|
||||
rollData.isHeroique = ((rollData.finalResult - rollData.difficulte) >= 10)
|
||||
rollData.isDramatique = ((rollData.finalResult - rollData.difficulte) <= -10)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,46 +382,90 @@ export class WastelandUtility {
|
||||
if (rollData.attrKey == "tochoose") { // No attr selected, force address
|
||||
rollData.attrKey = "adr"
|
||||
}
|
||||
if (!rollData.attr) {
|
||||
rollData.actionImg = "systems/fvtt-wasteland/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp"
|
||||
rollData.attr = duplicate(actor.system.attributs[rollData.attrKey])
|
||||
if (!rollData.attr && actor && actor.system.attributs && actor.system.attributs[rollData.attrKey]) {
|
||||
rollData.attr = foundry.utils.duplicate(actor.system.attributs[rollData.attrKey])
|
||||
if (rollData.attr.labelnorm) {
|
||||
rollData.actionImg = "systems/fvtt-wasteland/assets/icons/" + rollData.attr.labelnorm + ".webp"
|
||||
}
|
||||
}
|
||||
|
||||
if (rollData.charme) {
|
||||
rollData.diceFormula = rollData.charmeDice
|
||||
} else {
|
||||
rollData.diceFormula = rollData.mainDice
|
||||
if (rollData.doubleD20) { // Multiply result !
|
||||
// Double D20 ne s'applique que si on lance un D20, pas un D10
|
||||
if (rollData.doubleD20 && rollData.mainDice === "1d20") {
|
||||
rollData.diceFormula += "*2"
|
||||
if (!rollData.isReroll) {
|
||||
actor.changeEclat(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("BEFORE COMP", rollData)
|
||||
if (rollData.competence) {
|
||||
rollData.predilections = duplicate(rollData.competence.system.predilections.filter(pred => !pred.used) || [])
|
||||
rollData.predilections = foundry.utils.duplicate(rollData.competence.system.predilections.filter(pred => !pred.used) || [])
|
||||
let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0
|
||||
rollData.diceFormula += `+${rollData.attr.value}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}`
|
||||
let attrValue = rollData.attr?.value || 0
|
||||
rollData.diceFormula += `+${attrValue}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}`
|
||||
} else {
|
||||
rollData.diceFormula += `+${rollData.attr.value}*2+${rollData.modificateur}`
|
||||
let attrValue = rollData.attr?.value || 0
|
||||
rollData.diceFormula += `+${attrValue}*2+${rollData.modificateur}`
|
||||
}
|
||||
if (rollData.arme && rollData.arme.type == "arme") {
|
||||
rollData.diceFormula += `+${rollData.arme.system.bonusmaniementoff}`
|
||||
}
|
||||
|
||||
let myRoll = new Roll(rollData.diceFormula).roll({ async: false })
|
||||
// Apply desavantages (tactical advantages)
|
||||
let desavantagesBonus = 0
|
||||
if (rollData.desavantages) {
|
||||
for (let desavantage in rollData.desavantages) {
|
||||
if (rollData.desavantages[desavantage]) {
|
||||
desavantagesBonus += 5
|
||||
}
|
||||
}
|
||||
desavantagesBonus = Math.min(15, desavantagesBonus)
|
||||
if (desavantagesBonus > 0) {
|
||||
rollData.diceFormula += `+${desavantagesBonus}`
|
||||
}
|
||||
}
|
||||
|
||||
// Monté ?
|
||||
if (rollData.isMonte) {
|
||||
rollData.diceFormula += "+5"
|
||||
}
|
||||
|
||||
// Specific modifiers for ranged combat
|
||||
if (rollData.arme?.system?.isDistance) {
|
||||
if (rollData.visee) {
|
||||
rollData.diceFormula += "+5"
|
||||
}
|
||||
if (rollData.cibleconsciente && rollData.defender) {
|
||||
rollData.diceFormula += `-${rollData.defender.system.attributs.adr.value}`
|
||||
}
|
||||
if (rollData.ciblecourt) {
|
||||
if (rollData.difficulte <= 15) { // Portée courte ou moins
|
||||
rollData.diceFormula += `-5`
|
||||
} else {
|
||||
rollData.diceFormula += `-10`
|
||||
}
|
||||
}
|
||||
if (rollData.typeCouvert && rollData.typeCouvert != "aucun" && rollData.config.couverts[rollData.typeCouvert]) {
|
||||
rollData.diceFormula += `+${rollData.config.couverts[rollData.typeCouvert].value}`
|
||||
}
|
||||
}
|
||||
|
||||
let myRoll = await new Roll(rollData.diceFormula).roll()
|
||||
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
|
||||
rollData.roll = duplicate(myRoll)
|
||||
rollData.roll = foundry.utils.duplicate(myRoll)
|
||||
rollData.diceResult = myRoll.terms[0].results[0].result
|
||||
console.log(">>>> ", myRoll)
|
||||
|
||||
rollData.finalResult = myRoll.total
|
||||
this.computeResult(rollData, actor)
|
||||
await this.computeResult(rollData, actor)
|
||||
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result.html`, rollData)
|
||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result-v2.hbs`, rollData)
|
||||
}, rollData)
|
||||
|
||||
}
|
||||
@@ -395,20 +474,146 @@ export class WastelandUtility {
|
||||
static async bonusRollWasteland(rollData) {
|
||||
rollData.bonusFormula = rollData.addedBonus
|
||||
|
||||
let bonusRoll = new Roll(rollData.bonusFormula).roll({ async: false })
|
||||
let bonusRoll = await new Roll(rollData.bonusFormula).roll()
|
||||
await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode"));
|
||||
rollData.bonusRoll = duplicate(bonusRoll)
|
||||
rollData.bonusRoll = foundry.utils.duplicate(bonusRoll)
|
||||
|
||||
rollData.finalResult += rollData.bonusRoll.total
|
||||
|
||||
this.computeResult(rollData)
|
||||
await this.computeResult(rollData)
|
||||
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result.html`, rollData)
|
||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result-v2.hbs`, rollData)
|
||||
}, rollData)
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async rollWastelandPredilection(rollData, predilectionUsed, previousResult) {
|
||||
// Sauvegarder le premier résultat
|
||||
rollData.firstRoll = {
|
||||
diceResult: previousResult.diceResult,
|
||||
finalResult: previousResult.finalResult,
|
||||
diceFormula: previousResult.diceFormula
|
||||
}
|
||||
rollData.predilectionUsed = predilectionUsed
|
||||
rollData.isPredilectionReroll = true
|
||||
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
if (rollData.attrKey == "tochoose") {
|
||||
rollData.attrKey = "adr"
|
||||
}
|
||||
if (!rollData.attr && actor && actor.system.attributs && actor.system.attributs[rollData.attrKey]) {
|
||||
rollData.attr = foundry.utils.duplicate(actor.system.attributs[rollData.attrKey])
|
||||
if (rollData.attr.labelnorm) {
|
||||
rollData.actionImg = "systems/fvtt-wasteland/assets/icons/" + rollData.attr.labelnorm + ".webp"
|
||||
}
|
||||
}
|
||||
|
||||
if (rollData.charme) {
|
||||
rollData.diceFormula = rollData.charmeDice
|
||||
} else {
|
||||
rollData.diceFormula = rollData.mainDice
|
||||
// Double D20 ne s'applique que si on lance un D20, pas un D10
|
||||
if (rollData.doubleD20 && rollData.mainDice === "1d20") {
|
||||
rollData.diceFormula += "*2"
|
||||
}
|
||||
}
|
||||
|
||||
if (rollData.competence) {
|
||||
rollData.predilections = foundry.utils.duplicate(rollData.competence.system.predilections.filter(pred => !pred.used) || [])
|
||||
let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0
|
||||
let attrValue = rollData.attr?.value || 0
|
||||
rollData.diceFormula += `+${attrValue}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}`
|
||||
} else {
|
||||
let attrValue = rollData.attr?.value || 0
|
||||
rollData.diceFormula += `+${attrValue}*2+${rollData.modificateur}`
|
||||
}
|
||||
if (rollData.arme && rollData.arme.type == "arme") {
|
||||
rollData.diceFormula += `+${rollData.arme.system.bonusmaniementoff}`
|
||||
}
|
||||
|
||||
// Apply desavantages (tactical advantages)
|
||||
if (rollData.desavantages) {
|
||||
let totalDesavantage = 0
|
||||
for (let key in rollData.desavantages) {
|
||||
if (rollData.desavantages[key]) {
|
||||
totalDesavantage += 5
|
||||
}
|
||||
}
|
||||
totalDesavantage = Math.min(totalDesavantage, 15)
|
||||
if (totalDesavantage > 0) {
|
||||
rollData.diceFormula += `+${totalDesavantage}`
|
||||
}
|
||||
}
|
||||
|
||||
// Apply mounted bonus
|
||||
if (rollData.isMonte) {
|
||||
rollData.diceFormula += `+5`
|
||||
}
|
||||
|
||||
// Apply ranged combat modifiers
|
||||
if (rollData.arme && rollData.arme.system && rollData.arme.system.isDistance) {
|
||||
if (rollData.visee) {
|
||||
rollData.diceFormula += `+5`
|
||||
}
|
||||
if (rollData.cibleconsciente && rollData.defender) {
|
||||
let adrMalus = -(rollData.defender.system?.attributs?.adr?.value || 0)
|
||||
rollData.diceFormula += `${adrMalus}`
|
||||
}
|
||||
if (rollData.ciblecourt) {
|
||||
if (rollData.portee === "portee-courte" || rollData.portee === "portee-moyenne") {
|
||||
rollData.diceFormula += `-5`
|
||||
} else {
|
||||
rollData.diceFormula += `-10`
|
||||
}
|
||||
}
|
||||
if (rollData.couvert && rollData.typeCouvertValue) {
|
||||
rollData.diceFormula += `${rollData.typeCouvertValue}`
|
||||
}
|
||||
}
|
||||
|
||||
// Nouveau jet de dé
|
||||
let myRoll = await new Roll(rollData.diceFormula).roll()
|
||||
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
|
||||
rollData.roll = foundry.utils.duplicate(myRoll)
|
||||
rollData.diceResult = myRoll.total
|
||||
|
||||
rollData.bonusFormula = "0"
|
||||
if (rollData.charme && rollData.charme.system.bonusmajeur > 0) {
|
||||
rollData.bonusFormula = `${rollData.charme.system.bonusmajeur}d6`
|
||||
rollData.textBonus = "Bonus de Charme"
|
||||
}
|
||||
|
||||
rollData.finalResult = rollData.diceResult
|
||||
let bonusRoll = await new Roll(rollData.bonusFormula).roll()
|
||||
await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode"))
|
||||
rollData.bonusRoll = foundry.utils.duplicate(bonusRoll)
|
||||
rollData.finalResult += rollData.bonusRoll.total
|
||||
|
||||
// Comparer avec le premier jet et garder le meilleur
|
||||
rollData.secondRoll = {
|
||||
diceResult: rollData.diceResult,
|
||||
finalResult: rollData.finalResult
|
||||
}
|
||||
|
||||
if (rollData.firstRoll.finalResult > rollData.finalResult) {
|
||||
// Garder le premier résultat
|
||||
rollData.diceResult = rollData.firstRoll.diceResult
|
||||
rollData.finalResult = rollData.firstRoll.finalResult
|
||||
rollData.keptRoll = "first"
|
||||
} else {
|
||||
// Garder le second résultat
|
||||
rollData.keptRoll = "second"
|
||||
}
|
||||
|
||||
await this.computeResult(rollData)
|
||||
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result-v2.hbs`, rollData)
|
||||
}, rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static getUsers(filter) {
|
||||
return game.users.filter(filter).map(user => user.data._id);
|
||||
@@ -431,7 +636,7 @@ export class WastelandUtility {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static blindMessageToGM(chatOptions) {
|
||||
let chatGM = duplicate(chatOptions);
|
||||
let chatGM = foundry.utils.duplicate(chatOptions);
|
||||
chatGM.whisper = this.getUsers(user => user.isGM);
|
||||
chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content;
|
||||
console.log("blindMessageToGM", chatGM);
|
||||
@@ -493,12 +698,13 @@ export class WastelandUtility {
|
||||
/* -------------------------------------------- */
|
||||
static getBasicRollData() {
|
||||
let rollData = {
|
||||
rollId: randomID(16),
|
||||
rollId: foundry.utils.randomID(16),
|
||||
rollMode: game.settings.get("core", "rollMode"),
|
||||
modificateursOptions: this.getModificateurOptions(),
|
||||
pointAmeOptions: this.getPointAmeOptions(),
|
||||
difficulte: 0,
|
||||
modificateur: 0,
|
||||
config: game.system.wasteland.config,
|
||||
}
|
||||
WastelandUtility.updateWithTarget(rollData)
|
||||
return rollData
|
||||
@@ -510,6 +716,7 @@ export class WastelandUtility {
|
||||
if (target) {
|
||||
rollData.defenderTokenId = target.id
|
||||
let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor
|
||||
rollData.defender = defender.toObject() // For template access
|
||||
rollData.armeDefense = defender.getBestDefenseValue()
|
||||
if (rollData.armeDefense) {
|
||||
rollData.difficulte = rollData.armeDefense.system.totalDefensif
|
||||
@@ -526,7 +733,7 @@ export class WastelandUtility {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static applyBonneAventureRoll(li, changed, addedBonus) {
|
||||
let msgId = li.data("message-id")
|
||||
let msgId = $(li).data("message-id")
|
||||
let msg = game.messages.get(msgId)
|
||||
if (msg) {
|
||||
let rollData = msg.getFlag("world", "wasteland-roll")
|
||||
@@ -545,7 +752,7 @@ export class WastelandUtility {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static applyEclatRoll(li, changed, addedBonus) {
|
||||
let msgId = li.data("message-id")
|
||||
let msgId = $(li).data("message-id")
|
||||
let msg = game.messages.get(msgId)
|
||||
if (msg) {
|
||||
let rollData = msg.getFlag("world", "wasteland-roll")
|
||||
@@ -558,11 +765,90 @@ export class WastelandUtility {
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static cancelBlessure(li) {
|
||||
let msgId = $(li).data("message-id")
|
||||
let msg = game.messages.get(msgId)
|
||||
if (msg) {
|
||||
let rollData = msg.getFlag("world", "wasteland-roll")
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
|
||||
// Vérifier que l'acteur a au moins 1 point d'éclat
|
||||
if (actor.getEclat() < 1) {
|
||||
ui.notifications.warn("Pas assez de points d'Éclat")
|
||||
return
|
||||
}
|
||||
|
||||
// Déduire 1 point d'éclat
|
||||
actor.changeEclat(-1)
|
||||
|
||||
// Annuler la dernière blessure (incrémenter la santé)
|
||||
let currentSante = actor.system.sante.value
|
||||
let maxSante = actor.system.sante.max
|
||||
if (currentSante < maxSante) {
|
||||
actor.update({ 'system.sante.value': currentSante + 1 })
|
||||
ui.notifications.info("Blessure annulée - 1 Point d'Éclat dépensé")
|
||||
} else {
|
||||
ui.notifications.warn("Vous êtes déjà en pleine santé")
|
||||
// Rembourser le point d'éclat
|
||||
actor.changeEclat(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static reloadBA(li) {
|
||||
let msgId = $(li).data("message-id")
|
||||
let msg = game.messages.get(msgId)
|
||||
if (msg) {
|
||||
let rollData = msg.getFlag("world", "wasteland-roll")
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
|
||||
// Vérifier que l'acteur a au moins 1 point d'éclat
|
||||
if (actor.getEclat() < 1) {
|
||||
ui.notifications.warn("Pas assez de points d'Éclat")
|
||||
return
|
||||
}
|
||||
|
||||
// Déduire 1 point d'éclat
|
||||
actor.changeEclat(-1)
|
||||
|
||||
// Recharger les points de Bonne Aventure au maximum
|
||||
let maxBA = actor.system.bonneaventure.base
|
||||
actor.update({ 'system.bonneaventure.actuelle': maxBA })
|
||||
ui.notifications.info(`Points de Bonne Aventure rechargés à ${maxBA} - 1 Point d'Éclat dépensé`)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static incDecSante(li, value, costBA) {
|
||||
let msgId = $(li).data("message-id")
|
||||
let msg = game.messages.get(msgId)
|
||||
if (msg) {
|
||||
let rollData = msg.getFlag("world", "wasteland-roll")
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
|
||||
// Vérifier que l'acteur a assez de points de Bonne Aventure
|
||||
if (actor.getBonneAventure() < costBA) {
|
||||
ui.notifications.warn("Pas assez de points de Bonne Aventure")
|
||||
return
|
||||
}
|
||||
|
||||
// Déduire les points de Bonne Aventure
|
||||
actor.changeBonneAventure(-costBA)
|
||||
|
||||
// Incrémenter la santé
|
||||
actor.incDecSante(value)
|
||||
|
||||
ui.notifications.info(`+${value} Point(s) de Santé - ${costBA} Point(s) de Bonne Aventure dépensé(s)`)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static chatRollMenu(html, options) {
|
||||
let canApply = li => canvas.tokens.controlled.length && li.find(".wasteland-roll").length
|
||||
let hasBA = function (li) {
|
||||
let message = game.messages.get(li.attr("data-message-id"))
|
||||
let message = game.messages.get($(li).attr("data-message-id"))
|
||||
let rollData = message.getFlag("world", "wasteland-roll")
|
||||
if (rollData?.actorId) {
|
||||
let actor = game.actors.get(rollData.actorId)
|
||||
@@ -572,7 +858,7 @@ export class WastelandUtility {
|
||||
return false
|
||||
}
|
||||
let hasBA2 = function (li) {
|
||||
let message = game.messages.get(li.attr("data-message-id"))
|
||||
let message = game.messages.get($(li).attr("data-message-id"))
|
||||
let rollData = message.getFlag("world", "wasteland-roll")
|
||||
if (rollData?.actorId) {
|
||||
let actor = game.actors.get(rollData.actorId)
|
||||
@@ -582,7 +868,7 @@ export class WastelandUtility {
|
||||
return false
|
||||
}
|
||||
let hasBA3 = function (li) {
|
||||
let message = game.messages.get(li.attr("data-message-id"))
|
||||
let message = game.messages.get($(li).attr("data-message-id"))
|
||||
let rollData = message.getFlag("world", "wasteland-roll")
|
||||
if (rollData?.actorId) {
|
||||
let actor = game.actors.get(rollData.actorId)
|
||||
@@ -592,7 +878,7 @@ export class WastelandUtility {
|
||||
return false
|
||||
}
|
||||
let hasPE = function (li) {
|
||||
let message = game.messages.get(li.attr("data-message-id"))
|
||||
let message = game.messages.get($(li).attr("data-message-id"))
|
||||
let rollData = message.getFlag("world", "wasteland-roll")
|
||||
if (rollData?.actorId) {
|
||||
let actor = game.actors.get(rollData.actorId)
|
||||
@@ -602,7 +888,7 @@ export class WastelandUtility {
|
||||
return false
|
||||
}
|
||||
let hasPredilection = function (li) {
|
||||
let message = game.messages.get(li.attr("data-message-id"))
|
||||
let message = game.messages.get($(li).attr("data-message-id"))
|
||||
let rollData = message.getFlag("world", "wasteland-roll")
|
||||
if (rollData.competence) {
|
||||
let nbPred = rollData.competence.data.predilections.filter(pred => !pred.used).length
|
||||
@@ -611,7 +897,7 @@ export class WastelandUtility {
|
||||
return false
|
||||
}
|
||||
let canCompetenceDouble = function (li) {
|
||||
let message = game.messages.get(li.attr("data-message-id"))
|
||||
let message = game.messages.get($(li).attr("data-message-id"))
|
||||
let rollData = message.getFlag("world", "wasteland-roll")
|
||||
if (rollData.competence) {
|
||||
return rollData.competence.data.doublebonus
|
||||
@@ -631,7 +917,7 @@ export class WastelandUtility {
|
||||
name: "Gain de 1 Point de Santé / 24 heure (1 point de Bonne Aventure)",
|
||||
icon: "<i class='fas fa-user-plus'></i>",
|
||||
condition: canApply && hasBA,
|
||||
callback: li => WastelandUtility.incDecSante(1)
|
||||
callback: li => WastelandUtility.incDecSante(li, 1, 1)
|
||||
}
|
||||
)
|
||||
options.push(
|
||||
@@ -639,7 +925,7 @@ export class WastelandUtility {
|
||||
name: "Gain de 2 Points de Santé / 24 heure (2 points de Bonne Aventure)",
|
||||
icon: "<i class='fas fa-user-plus'></i>",
|
||||
condition: canApply && hasBA2,
|
||||
callback: li => WastelandUtility.incDecSante(2)
|
||||
callback: li => WastelandUtility.incDecSante(li, 2, 2)
|
||||
}
|
||||
)
|
||||
options.push(
|
||||
@@ -695,30 +981,24 @@ export class WastelandUtility {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async confirmDelete(actorSheet, li) {
|
||||
let itemId = li.data("item-id");
|
||||
let msgTxt = "<p>Are you sure to remove this Item ?";
|
||||
let buttons = {
|
||||
delete: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "Yes, remove it",
|
||||
callback: () => {
|
||||
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
|
||||
li.slideUp(200, () => actorSheet.render(false));
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
icon: '<i class="fas fa-times"></i>',
|
||||
label: "Cancel"
|
||||
}
|
||||
const itemId = li.dataset.itemId
|
||||
if (!itemId) return
|
||||
|
||||
const item = actorSheet.document.items.get(itemId)
|
||||
if (!item) return
|
||||
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: { title: "Confirmer la suppression" },
|
||||
content: `<p>Êtes-vous sûr de vouloir supprimer <strong>${item.name}</strong> ?</p>`,
|
||||
rejectClose: false,
|
||||
modal: true,
|
||||
yes: { label: "Supprimer", icon: "fa-trash" },
|
||||
no: { label: "Annuler", icon: "fa-times" }
|
||||
})
|
||||
|
||||
if (confirmed) {
|
||||
await actorSheet.document.deleteEmbeddedDocuments("Item", [itemId])
|
||||
}
|
||||
msgTxt += "</p>";
|
||||
let d = new Dialog({
|
||||
title: "Confirm removal",
|
||||
content: msgTxt,
|
||||
buttons: buttons,
|
||||
default: "cancel"
|
||||
});
|
||||
d.render(true);
|
||||
}
|
||||
|
||||
}
|
||||
4971
package-lock.json
generated
Normal file
4971
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
package.json
Normal file
16
package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "fvtt-wasteland",
|
||||
"version": "1.0.0",
|
||||
"description": "Wasteland RPG for FoundryVTT - French",
|
||||
"scripts": {
|
||||
"build": "gulp build",
|
||||
"watch": "gulp watch"
|
||||
},
|
||||
"author": "Uberwald/LeRatierBretonnien",
|
||||
"license": "SEE LICENSE IN LICENCE.txt",
|
||||
"devDependencies": {
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-less": "^5.0.0",
|
||||
"gulp-sourcemaps": "^3.0.0"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
BIN
packs/armes/000211.ldb
Normal file
BIN
packs/armes/000211.ldb
Normal file
Binary file not shown.
@@ -1 +1 @@
|
||||
MANIFEST-000038
|
||||
MANIFEST-000224
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
2023/11/29-22:11:25.434763 7fef57fff6c0 Recovering log #36
|
||||
2023/11/29-22:11:25.445038 7fef57fff6c0 Delete type=3 #34
|
||||
2023/11/29-22:11:25.445125 7fef57fff6c0 Delete type=0 #36
|
||||
2023/11/29-22:14:16.953175 7fef56ffd6c0 Level-0 table #41: started
|
||||
2023/11/29-22:14:16.953201 7fef56ffd6c0 Level-0 table #41: 0 bytes OK
|
||||
2023/11/29-22:14:16.991886 7fef56ffd6c0 Delete type=0 #39
|
||||
2023/11/29-22:14:17.106517 7fef56ffd6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
|
||||
2023/11/29-22:14:17.106560 7fef56ffd6c0 Manual compaction at level-1 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
|
||||
2026/01/10-09:44:14.205626 7f1c56bff6c0 Recovering log #222
|
||||
2026/01/10-09:44:14.218871 7f1c56bff6c0 Delete type=3 #220
|
||||
2026/01/10-09:44:14.219004 7f1c56bff6c0 Delete type=0 #222
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
2023/11/29-20:50:20.610722 7ff1ecbfe6c0 Recovering log #32
|
||||
2023/11/29-20:50:20.620538 7ff1ecbfe6c0 Delete type=3 #30
|
||||
2023/11/29-20:50:20.620594 7ff1ecbfe6c0 Delete type=0 #32
|
||||
2023/11/29-22:03:29.595804 7fef56ffd6c0 Level-0 table #37: started
|
||||
2023/11/29-22:03:29.595831 7fef56ffd6c0 Level-0 table #37: 0 bytes OK
|
||||
2023/11/29-22:03:29.601970 7fef56ffd6c0 Delete type=0 #35
|
||||
2023/11/29-22:03:29.602116 7fef56ffd6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
|
||||
2023/11/29-22:03:29.602150 7fef56ffd6c0 Manual compaction at level-1 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
|
||||
2026/01/10-09:33:50.449402 7f1c553fc6c0 Recovering log #218
|
||||
2026/01/10-09:33:50.499238 7f1c553fc6c0 Delete type=3 #216
|
||||
2026/01/10-09:33:50.499325 7f1c553fc6c0 Delete type=0 #218
|
||||
2026/01/10-09:42:25.332397 7f1c54bfb6c0 Level-0 table #223: started
|
||||
2026/01/10-09:42:25.332474 7f1c54bfb6c0 Level-0 table #223: 0 bytes OK
|
||||
2026/01/10-09:42:25.395595 7f1c54bfb6c0 Delete type=0 #221
|
||||
2026/01/10-09:42:25.519167 7f1c54bfb6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
|
||||
2026/01/10-09:42:25.519295 7f1c54bfb6c0 Manual compaction at level-1 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
|
||||
|
||||
Binary file not shown.
BIN
packs/armes/MANIFEST-000224
Normal file
BIN
packs/armes/MANIFEST-000224
Normal file
Binary file not shown.
BIN
packs/artifex/000072.ldb
Normal file
BIN
packs/artifex/000072.ldb
Normal file
Binary file not shown.
1
packs/artifex/CURRENT
Normal file
1
packs/artifex/CURRENT
Normal file
@@ -0,0 +1 @@
|
||||
MANIFEST-000085
|
||||
3
packs/artifex/LOG
Normal file
3
packs/artifex/LOG
Normal file
@@ -0,0 +1,3 @@
|
||||
2026/01/10-09:44:14.267526 7f1c563fe6c0 Recovering log #83
|
||||
2026/01/10-09:44:14.284469 7f1c563fe6c0 Delete type=3 #81
|
||||
2026/01/10-09:44:14.284596 7f1c563fe6c0 Delete type=0 #83
|
||||
8
packs/artifex/LOG.old
Normal file
8
packs/artifex/LOG.old
Normal file
@@ -0,0 +1,8 @@
|
||||
2026/01/10-09:33:50.652107 7f1c563fe6c0 Recovering log #79
|
||||
2026/01/10-09:33:50.742710 7f1c563fe6c0 Delete type=3 #77
|
||||
2026/01/10-09:33:50.742808 7f1c563fe6c0 Delete type=0 #79
|
||||
2026/01/10-09:42:25.395879 7f1c54bfb6c0 Level-0 table #84: started
|
||||
2026/01/10-09:42:25.395959 7f1c54bfb6c0 Level-0 table #84: 0 bytes OK
|
||||
2026/01/10-09:42:25.461875 7f1c54bfb6c0 Delete type=0 #82
|
||||
2026/01/10-09:42:25.519225 7f1c54bfb6c0 Manual compaction at level-0 from '!items!PqP7BWEkK7aK65yH' @ 72057594037927935 : 1 .. '!items!irEA0eyE731viEYl' @ 0 : 0; will stop at (end)
|
||||
2026/01/10-09:42:25.519316 7f1c54bfb6c0 Manual compaction at level-1 from '!items!PqP7BWEkK7aK65yH' @ 72057594037927935 : 1 .. '!items!irEA0eyE731viEYl' @ 0 : 0; will stop at (end)
|
||||
BIN
packs/artifex/MANIFEST-000085
Normal file
BIN
packs/artifex/MANIFEST-000085
Normal file
Binary file not shown.
BIN
packs/bestiaire/000084.ldb
Normal file
BIN
packs/bestiaire/000084.ldb
Normal file
Binary file not shown.
1
packs/bestiaire/CURRENT
Normal file
1
packs/bestiaire/CURRENT
Normal file
@@ -0,0 +1 @@
|
||||
MANIFEST-000097
|
||||
3
packs/bestiaire/LOG
Normal file
3
packs/bestiaire/LOG
Normal file
@@ -0,0 +1,3 @@
|
||||
2026/01/10-09:44:14.021312 7f1c55bfd6c0 Recovering log #95
|
||||
2026/01/10-09:44:14.040496 7f1c55bfd6c0 Delete type=3 #93
|
||||
2026/01/10-09:44:14.040627 7f1c55bfd6c0 Delete type=0 #95
|
||||
8
packs/bestiaire/LOG.old
Normal file
8
packs/bestiaire/LOG.old
Normal file
@@ -0,0 +1,8 @@
|
||||
2026/01/10-09:33:49.998930 7f1c56bff6c0 Recovering log #91
|
||||
2026/01/10-09:33:50.047028 7f1c56bff6c0 Delete type=3 #89
|
||||
2026/01/10-09:33:50.047160 7f1c56bff6c0 Delete type=0 #91
|
||||
2026/01/10-09:42:24.848889 7f1c54bfb6c0 Level-0 table #96: started
|
||||
2026/01/10-09:42:24.848942 7f1c54bfb6c0 Level-0 table #96: 0 bytes OK
|
||||
2026/01/10-09:42:24.909912 7f1c54bfb6c0 Delete type=0 #94
|
||||
2026/01/10-09:42:25.030500 7f1c54bfb6c0 Manual compaction at level-0 from '!actors!S7FhBajQ5KKhIpj6' @ 72057594037927935 : 1 .. '!folders!BHMWTRHF2lNlAK8u' @ 0 : 0; will stop at (end)
|
||||
2026/01/10-09:42:25.030611 7f1c54bfb6c0 Manual compaction at level-1 from '!actors!S7FhBajQ5KKhIpj6' @ 72057594037927935 : 1 .. '!folders!BHMWTRHF2lNlAK8u' @ 0 : 0; will stop at (end)
|
||||
BIN
packs/bestiaire/MANIFEST-000097
Normal file
BIN
packs/bestiaire/MANIFEST-000097
Normal file
Binary file not shown.
BIN
packs/capacreature/000072.ldb
Normal file
BIN
packs/capacreature/000072.ldb
Normal file
Binary file not shown.
1
packs/capacreature/CURRENT
Normal file
1
packs/capacreature/CURRENT
Normal file
@@ -0,0 +1 @@
|
||||
MANIFEST-000085
|
||||
3
packs/capacreature/LOG
Normal file
3
packs/capacreature/LOG
Normal file
@@ -0,0 +1,3 @@
|
||||
2026/01/10-09:44:14.288059 7f1c553fc6c0 Recovering log #83
|
||||
2026/01/10-09:44:14.307028 7f1c553fc6c0 Delete type=3 #81
|
||||
2026/01/10-09:44:14.307153 7f1c553fc6c0 Delete type=0 #83
|
||||
8
packs/capacreature/LOG.old
Normal file
8
packs/capacreature/LOG.old
Normal file
@@ -0,0 +1,8 @@
|
||||
2026/01/10-09:33:50.746372 7f1c553fc6c0 Recovering log #79
|
||||
2026/01/10-09:33:50.798633 7f1c553fc6c0 Delete type=3 #77
|
||||
2026/01/10-09:33:50.798745 7f1c553fc6c0 Delete type=0 #79
|
||||
2026/01/10-09:42:25.580579 7f1c54bfb6c0 Level-0 table #84: started
|
||||
2026/01/10-09:42:25.580643 7f1c54bfb6c0 Level-0 table #84: 0 bytes OK
|
||||
2026/01/10-09:42:25.633628 7f1c54bfb6c0 Delete type=0 #82
|
||||
2026/01/10-09:42:25.776283 7f1c54bfb6c0 Manual compaction at level-0 from '!items!JzGNaagJD2jLi9tH' @ 72057594037927935 : 1 .. '!items!LaiHuZ30K4iJr6ce' @ 0 : 0; will stop at (end)
|
||||
2026/01/10-09:42:25.776373 7f1c54bfb6c0 Manual compaction at level-1 from '!items!JzGNaagJD2jLi9tH' @ 72057594037927935 : 1 .. '!items!LaiHuZ30K4iJr6ce' @ 0 : 0; will stop at (end)
|
||||
BIN
packs/capacreature/MANIFEST-000085
Normal file
BIN
packs/capacreature/MANIFEST-000085
Normal file
Binary file not shown.
@@ -1,30 +0,0 @@
|
||||
{"name":"Protection du Sorcier","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu est protégé contre les attaques des créatures nées ou invoquées par la sorcellerie (un type d’Élémentaire, les Démons, les Automata…). La protection dont il bénéficie dépend du nombre de points d’Âme sacrifiés.</p>","allegeance":"chaos","prerequis":"6 ou plus en Trempe.","sacrifice":"<ul>\n<li>1 point d’Âme pour 1 point de protection. Accès réservé aux Novices et aux Adeptes.</li>\n<li>2 points d’Âme pour 2 points de protection. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>3 points d’Âme pour un 3 points de protection. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.WUPne9oDFTcdLtW9"}},"_id":"5dGXNiL3WN4cAk7X"}
|
||||
{"name":"Conscience extraplanaire","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu a conscience du tissu de la réalité. Il peut percevoir les passages et les portails menant vers d’autres plans dans une zone de 10 mètres par point d’Aspect chaotique grâce à un simple test de Clairvoyance + Perception / 15. S’il ignore l’existence de ce passage et qu’il passe devant, le MJ peut faire un test de Clairvoyance + Perception / 25 pour l’Élu derrière son écran.</p>","allegeance":"chaos","prerequis":"6 ou plus en Clairvoyance.","sacrifice":"<p>1 point d’Âme.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.1nIGWsNtx1NKxjPn"}},"_id":"5v0Y35LzqfAd0KnX"}
|
||||
{"name":"Santé renforcée (Loi)","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Les points de Santé de l’Élu sont recalculés en ajoutant à [(Puissance + Trempe) x 2]+ 5 le nombre de points d’Âme sacrifiés. Ce Don ne peut être pris qu’une seule fois même si l’Élu conclut plusieurs Pactes.</p>","allegeance":"loi","prerequis":"6 ou plus en Trempe.","sacrifice":"<p>1 point d’Âme par point de Santé supplémentaire, pour un maximum égal à l’Aspect du personnage.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.4ow7N6kdAWXbTApf"}},"_id":"6IS4zstVwJxG2lwy"}
|
||||
{"name":"Repos de l’Âme","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu récupère deux fois plus vite ses points d’Âme, soit 2 par heure et 4 en cas de repos.</p>","allegeance":"chaos","prerequis":"6 ou plus en Présence.","sacrifice":"<p>3 points d’Âme.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.KXciI0xwd6gSOLcZ"}},"_id":"8WLUjxBJtd33mZFp"}
|
||||
{"name":"Trait chaotique","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu gagne un trait du Chaos qu’il tire au hasard dans le tableau de la page 185 avec un d20. Il peut prendre ce Don trois fois. S’il tire un trait qu’il possède déjà, il doit relancer le d20. Ces traits sont permanents et définitifs, à moins que les Seigneurs du Chaos n’en décident autrement, notamment si l’Élu les dissimule trop à leur goût alors qu’il n’y est pas absolument contraint par des impératifs de survie immédiats.</p>","allegeance":"chaos","prerequis":"aucun","sacrifice":"<p>2 points d’Âme par Trait chaotique. L’Élu ne peut en avoir plus que sa Marge.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.BKzeOjGA13wQgO0S"}},"_id":"BuZM5BnSaPIGEiEq"}
|
||||
{"name":"Guérison","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu régénère rapidement. Chaque jour de repos, il regagne un nombre de points de Santé égal à sa Trempe /2 + son Aspect.</p>","allegeance":"loi","prerequis":"6 ou plus en Trempe.","sacrifice":"<p>3 point d’Âme.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.yvY0G3PffZWEl92R"}},"_id":"Eme2iVAF670LCeIH"}
|
||||
{"name":"Aide Élémentaire","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Ce Don permet d’appeler un Élémentaire à son secours une fois par scénario. La Puissance de l’Élémentaire est fi xée lors de l’octroi du Don. La nature de l’Élémentaire correspond à celle du Seigneur Élémentaire avec lequel le Pacte a été passé. Ce Don ne peut être pris qu’une fois par Seigneur Élémentaire vénéré.</p>","allegeance":"elementaires","prerequis":"","sacrifice":"<ul>\n<li>1 point d’Âme pour un Élémentaire mineur disposant de 5 points d’Âme. Accès réservé aux Novices et aux Adeptes.</li>\n<li>3 points d’Âme pour un Élémentaire médian disposant de 10 points d’Âme. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>5 points d’Âme pour un Élémentaire majeur disposant de 15 points d’Âme. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.Wo42z8L33wLerUM8"}},"_id":"JAkSopwrh4QmKrPY"}
|
||||
{"name":"Augmentation du modificateur de dégâts","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Les dégâts causés par l’Élu bénéficient d’un bonus pour <em>un type d’armes donné</em>. Le bonus dépend du nombre de points d’Âme sacrifiés.</p>","allegeance":"loi","prerequis":"6 ou plus en Puissance.","sacrifice":"<ul>\n<li>1 point d’Âme pour un bonus de + 1. Accès réservé aux Novices et aux Adeptes.</li>\n<li>3 points d’Âme pour un bonus de + 2. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>5 points d’Âme pour un bonus de + 3. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.bqdkbOf7EsdsCRqu"}},"_id":"K4H2VC0jTCy7pYj7"}
|
||||
{"name":"L’exemple du Juste","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu est un exemple rayonnant pour ses camarades. Une fois par scénario, si l’Élu adresse pendant un tour de jeu une prière à une divinité de la Loi, lui et ses camarades (une dizaine tout au plus) seront bénis et recevront jusqu’à la fin de la scène un bonus à toutes leurs actions.</p>","allegeance":"loi","prerequis":"6 ou plus en Trempe.","sacrifice":"<ul>\n<li>1 point d’Âme pour un bonus de + 1. Accès réservé aux Novices et aux Adeptes.</li>\n<li>2 points d’Âme pour un bonus de + 2. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>3 points d’Âme pour un bonus de + 3. Accès réservé aux Champions. </li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.C5ofSFNXoXZFo8ak"}},"_id":"KlfScJjqvlPtkcdZ"}
|
||||
{"name":"Abstinence","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu peut survivre en se privant d’un élément vital (nourriture, eau, sommeil, mais pas air) pendant un certain temps. La durée de ce pouvoir dépend du nombre de points d’Âme sacrifiés.</p>","allegeance":"tous","prerequis":"6 ou plus en Trempe","sacrifice":"<ul>\n<li>2 points d’Âme pour un jour. Accès réservé aux Novices et aux Adeptes.</li>\n<li>6 points d’Âme pour une semaine. Accès réservé aux Che\u0002valiers et aux Hérauts.</li>\n<li>10 points d’Âme pour un mois. Accès réservé aux Cham\u0002pions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.Q4Sy7vOGgU7Zgi9p"}},"_id":"OJKMNtFsuVgkV3gh"}
|
||||
{"name":"Rupture Chaotique","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Lorsque l’Élu manie un certain type d’armes, il peut ajouter un bonus à son initiative ou bénéficier d’un bonus aux dégâts lors d’un combat, une fois par séance de jeu.</p>","allegeance":"chaos","prerequis":"6 ou plus en Puissance.","sacrifice":"<ul>\n<li>1 point d’Âme pour un bonus de + 1 aux dégâts ou + 5 en initiative. Accès réservé aux Novices et aux Adeptes.</li>\n<li>3 points d’Âme pour un bonus de + 2 aux dégâts ou + 7 en initiative. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>5 points d’Âme pour un bonus de + 3 aux dégâts ou + 10 en initiative. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.gMI6F4qY5Fi6okZ7"}},"_id":"Q1xDyjQy59lr5wAG"}
|
||||
{"name":"Sens du Pur","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu a immédiatement conscience de la présence passée ou actuelle du Chaos (sortilège à l’œuvre, puissant serviteur…). Le personnage est très sensible aux émanations chaotiques, elles lui soulèvent le cœur. La distance de détection peut aug\u0002menter en sacrifiant davantage de points d’Âme</p>","allegeance":"loi","prerequis":"6 ou plus en Adresse ou Clairvoyance.","sacrifice":"<ul>\n<li>1 point d’Âme pour détecter les ennemis dans un rayon de 10 mètres. Accès réservé aux Novices et aux Adeptes.</li>\n<li>2 points d’Âme pour détecter les ennemis dans un rayon de 100 mètres. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>3 points d’Âme pour détecter les ennemis dans un rayon de 1000 mètres. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.tp2435vHrj63GeAK"}},"_id":"RYgdBIikcBfN8xAp"}
|
||||
{"name":"Gardien des Millions de Sphères","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu a conscience du tissu de la réalité. Il peut sceller, par de longues prières psalmodiées, les passages et les portails donnant sur d’autres plans du Multivers. Cette action lui coûte le sacrifice d’un point d’Éclat. Tant que sa concentration reste tournée vers ce passage, le portail demeure scellé, comme si Donblas lui-même le gardait. Mais dès que le personnage s’endort, la porte, bien que fermée, n’est plus scellée. Certains plans très importants possèdent plusieurs passages menant vers les Jeunes Royaumes.</p>","allegeance":"loi","prerequis":"6 ou plus en Clairvoyance.","sacrifice":"<p>5 points d’Âme.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.tm1fjaETqhMAldqJ"}},"_id":"TKCaHLHccYw7JvFy"}
|
||||
{"name":"Vivacité","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Lors d’un test de Capacité Offensive, l’Élu voit la marge nécessaire à l’obtention d’une réussite héroïque diminuer de 1 (9 ou plus), de 2 (8 ou plus) ou de 3 (7 ou plus). </p>","allegeance":"tous","prerequis":"6 ou plus en Adresse ou Clairvoyance.","sacrifice":"<ul>\n<li>3 points d’Âme pour diminuer la marge de 1 (réussite héroïque sur 9 ou plus). Accès réservé aux Novices et aux Adeptes.</li>\n<li>5 points d’Âme pour diminuer la marge de 2 (réussite héroïque sur 8 ou plus). Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>7 points d’Âme pour diminuer la marge de 3 (réussite héroïque sur 7 ou plus). Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.GlhqSASDW0XdR243"}},"_id":"XaJAo8otOXmgtjzA"}
|
||||
{"name":"Augmentation de compétence (Chaos)","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Ce Don permet d’augmenter une compétence importante pour la voie spirituelle choisie. Le bonus octroyé dépend du nombre de points d’Âme sacrifi és. Ce Don peut être choisi plusieurs fois, mais il ne peut s’appliquer qu’une fois par compétence. </p>","allegeance":"chaos","prerequis":"6 ou plus en Clairvoyance.","sacrifice":"<ul>\n<li>1 point d’Âme pour un + 1 à une compétence. Accès réservé aux Novices et aux Adeptes.</li>\n<li>2 points d’Âme pour un + 2 à une compétence. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>3 points d’Âme pour un + 3 à une compétence. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.75OYBq5iGgdXVQcA"}},"_id":"aP4vSk6WOL4wXyyb"}
|
||||
{"name":"Dur à cuire","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu résiste aux effets liés à la fatigue et à la maladie. Le béné\u0002fi ciaire de ce Don a droit à un bonus de + 1 lors de ses tests de Trempe pour résister à la fatigue et à la maladie. Ce bonus peut augmenter en sacrifi ant davantage de points d’Âme.</p>","allegeance":"tous","prerequis":"6 ou plus en Trempe.","sacrifice":"<ul>\n<li>1 point d’Âme pour un bonus de + 2. Accès réservé aux Novices et aux Adeptes.</li>\n<li>2 points d’Âme pour un bonus de + 3. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>3 points d’Âme pour un bonus de + 4. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.jNeLj9DyylUj7LAl"}},"_id":"gK3glTgKTKRw9ref"}
|
||||
{"name":"Animal apprivoisé","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Le croyant se voit confier un animal normal (c’est-à-dire non surnaturel). L’animal apprivoisé est un membre standard de son espèce et obéit à des ordres simples, mais sans jamais aller contre sa nature. Le coût de ce Don dépend de la Puissance de la créature qui est fixée lors de son octroi. Si 1 point d’Âme supplémentaire est investi, la créature peut communiquer télé\u0002pathiquement avec son maître (et uniquement avec lui).</p>","allegeance":"betes","prerequis":"6 ou plus en Présence.","sacrifice":"<ul>\n<li>1 point d’Âme pour une créature d’une Puissance de 0 à 4. Accès réservé aux Novices et aux Adeptes.</li>\n<li>3 points d’Âme pour une créature d’une Puissance de 5 à 7. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>5 points d’Âme pour une créature d’une Puissance de 8 à 10. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.qDUei4wRjpRYi9Ps"}},"_id":"gN7DtvMJPASpPpQl"}
|
||||
{"name":"Protection du Mage","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu est protégé contre les attaques des créatures engendrées ou invoquées par la Sorcellerie (les Élémentaires, les Démons, les Automata…). La protection dont il bénéficie dépend du nombre de points d’Âme sacrifiés.</p>","allegeance":"loi","prerequis":"6 ou plus en Trempe.","sacrifice":"<ul>\n<li>1 point d’Âme pour 1 point de protection. Accès réservé aux Novices et aux Adeptes.</li>\n<li>2 points d’Âme pour 2 points de protection. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>3 points d’Âme pour un 3 points de protection. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.NVIG52HOiWGm6JcW"}},"_id":"j7iTQOFdiJUviTC9"}
|
||||
{"name":"Sang-Froid","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu peut supporter n’importe quel type de souffrance psy\u0002chique. Il ne subit pas les pénalités dues à l’état Déstabilisé (– 2) et Choqué (– 5). Il est immunisé à la torture mentale.</p>","allegeance":"chaos","prerequis":"6 ou plus en Trempe.","sacrifice":"<p>1 point d’Âme.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.KvdQLDhSpfAd2Iig"}},"_id":"lWXxInM05EiBu6uX"}
|
||||
{"name":"Dur à cuire (Chaos)","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu résiste aux effets liés à la fatigue et à la maladie. Le bénéfi ciaire de ce Don a droit à un bonus de + 1 lors de ses tests de Trempe pour résister à la fatigue et à la maladie. Ce bonus peut augmenter en sacrifi ant davantage de points d’Âme.</p>","allegeance":"chaos","prerequis":"6 ou plus en Trempe.","sacrifice":"<ul>\n<li>1 point d’Âme pour un bonus de + 1. Accès réservé aux Novices et aux Adeptes.</li>\n<li>2 points d’Âme pour un bonus de + 2. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>3 points d’Âme pour un bonus de + 3.Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.E0kyJQ5gqCEcm0Aj"}},"_id":"oAKqbsLVd8cX4tjN"}
|
||||
{"name":"Augmentation de compétence","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Ce Don permet d’augmenter une compétence relative à la voie spirituelle choisie. Le bonus octroyé dépend du nombre de points d’Âme sacrifi és. Ce Don peut être choisi plusieurs fois, mais il ne peut s’appliquer qu’une fois par compétence. </p>","allegeance":"tous","prerequis":"6 ou plus en Clairvoyance.","sacrifice":"<ul>\n<li>1 point d’Âme pour un + 1 à une compétence. Accès réservé aux Novices et aux Adeptes.</li>\n<li>2 points d’Âme pour un + 2 à une compétence. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>3 points d’Âme pour un + 3 à une compétence. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.djDFklKJaW1oVTds"}},"_id":"pPNKneH7scfaEmGq"}
|
||||
{"name":"Augmentation de compétence (Loi)","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Ce Don permet d’augmenter une compétence importante pour la voie spirituelle choisie. Le bonus octroyé dépend du nombre de points d’Âme sacrifiés. Ce Don peut être choisi plusieurs fois mais il ne peut s’appliquer qu’une fois par compétence.</p>","allegeance":"loi","prerequis":"6 ou plus en Clairvoyance.","sacrifice":"<ul>\n<li>1 point d’Âme pour un + 1 à une compétence. Accès réservé aux Novices et aux Adeptes.</li>\n<li>2 points d’Âme pour un + 2 à une compétence. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>3 points d’Âme pour un + 3 à une compétence. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.gNL9lD7hofK32H7D"}},"_id":"qhOE8OTe5hNNgDtt"}
|
||||
{"name":"Santé renforcée","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Les points de Santé de l’Élu sont recalculés en ajoutant à [(Puissance + Trempe) x 2]+ 5 le nombre de points d’Âme sacrifiés. Ce Don ne peut être pris qu’une seule fois même si l’Élu conclut plusieurs Pactes.</p>","allegeance":"tous","prerequis":"6 ou plus en Trempe.","sacrifice":"<p>1 point d’Âme par point de Santé supplémentaire.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.dJnYsDcETkidd2s5"}},"_id":"sLgGn6zhkDdbpQlH"}
|
||||
{"name":"Rectitude","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu peut supporter n’importe quel type de souffrance. Il ne subit pas les pénalités dues à l’état Blessé (-2) et Gravement Blessé (-5). Il est immunisé contre la torture.</p>","allegeance":"loi","prerequis":"6 ou plus en Trempe.","sacrifice":"<p>1 point d’Âme.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.HpoTP9idEKi1NQGL"}},"_id":"t7Ez0rzqYOf48GfY"}
|
||||
{"name":"La voie du milieu","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Lors d’un combat, l’Élu ne subit plus les effets d’une réussite héroïque contre lui lors des tests de Capacité offensive. Il n’en profite plus non plus. À un haut niveau, il ne peut plus être affecté par les échecs dramatiques.</p>","allegeance":"loi","prerequis":"6 ou plus en Adresse ou Clairvoyance.","sacrifice":"<ul>\n<li>3 points d’Âme pour ne plus être affecté par les réussite héroïques.</li>\n<li>7 points d’Âme pour ne plus être affecté ni par les réus\u0002sites héroïques ni par les échecs dramatiques. Accès réser\u0002vé aux Champions</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.rOXDZ2020snvhtR2"}},"_id":"taXCPXiPEuOIKaUO"}
|
||||
{"name":"Perfection","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’un des attributs de l’Élu est augmenté, grâce à la bienveil\u0002lance de la divinité. Il ne peut dépasser le maximum imposé par l’espèce. Ce Don peut être choisi plusieurs fois, mais il ne peut s’appliquer qu’une fois par attribut.</p>","allegeance":"tous","prerequis":"6 ou plus dans l’attribut modifié.","sacrifice":"<ul>\n<li>3 points d’Âme pour un bonus de + 1. Accès réservé aux Novices et aux Adeptes.</li>\n<li>6 points d’Âme pour un bonus de + 2. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>9 points d’Âme pour un bonus de + 3. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.nSlFw6q7TCqxZ6tH"}},"_id":"vZBNRTeGGU5hsXtT"}
|
||||
{"name":"Sixième sens","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu sait où se trouvent des ennemis dont il a conscience et qui cherchent à lui nuire directement dans un rayon de 10 mètres. Attention, cette détection ne fonctionnera pas pour deviner que quelqu’un est simplement hostile à l’Élu ou le hait viscéralement. Il s’agit d’un pouvoir de localisation et de vigilance. La distance de détection peut augmenter en sacrifiant davantage de points d’Âme.</p>","allegeance":"chaos","prerequis":"6 ou plus en Adresse ou Clairvoyance.","sacrifice":"<ul>\n<li>1 point d’Âme pour détecter les ennemis dans un rayon de 10 mètres. Accès réservé aux Novices et aux Adeptes.</li>\n<li>2 points d’Âme pour détecter les ennemis dans un rayon de 100 mètres. Accès réservé aux Chevaliers et aux Hérauts.</li>\n<li>3 points d’Âme pour détecter les ennemis dans un rayon de 1000 mètres. Accès réservé aux Champions.</li>\n</ul>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.6CIPAVjSVsOqZBYc"}},"_id":"wsbGuOoFjHO6aQEm"}
|
||||
{"name":"Endurance","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu peut supporter n’importe quel type de souffrance. Il ne subit pas les pénalités dues à l’état Blessé (-2) et Gravement Blessé (-5). Il est immunisé à la torture si elle est infl igée à partir d’un des éléments tutélaires du personnage.</p>","allegeance":"elementaires","prerequis":"6 ou plus en Trempe.","sacrifice":"<p>3 points d’Âme.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.on94N7pylXJ8gtdF"}},"_id":"yJ6K9dYrdNxr5uWj"}
|
||||
{"name":"Le pilier de la Loi","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>Une fois par scénario, l’Élu peut résister à une menace ou agres\u0002sion, physique ou mentale, sans jeter les dés. Il est toutefois néces\u0002saire que l’Élu ait conscience du danger. L’Élu pourra par exemple résister à la terreur provoquée par une créature du Chaos parti\u0002culièrement abominable ou légendaire, mais ne pourra pas éviter d’être assommé par un habile voleur de Bakshaan qu’il n’avait pas vu. Cette résistance peut même être « extraordinaire » si l’Aspect de l’Élu est très haut.</p>\n<p>S’il est un Adepte ou un Novice, l’Élu peut résister à l’ivresse lors d’une nuit de beuverie chez un Prince-Marchand de Bakshaan.</p>\n<p>S’il est un Chevalier ou un Héraut, l’Élu peut résister à un coup surpuissant (celui-ci est annulé) ou à l’apparition gla\u0002çante d’un Démon. S’il est un Champion, il peut par exemple soutenir l’arche d’un temple qui s’écroule pendant quelques précieuses secondes afi n de laisser le temps à ses camarades de se mettre à l’abri (songez à Maciste, à Samson, ou tout simplement à Conan). Bref, n’hésitez pas à faire dans l’extraordinaire, mais ne bas\u0002culez pas dans le grosbillisme sans limite (ou alors ne nous le dites pas). Il revient au MJ de doser avec justesse ce Don.</p>","allegeance":"loi","prerequis":"7 ou plus en Trempe","sacrifice":"<p>3 points d’Âme.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.yZ84qzrGeIDfZXr1"}},"_id":"zef3VQat06Ronc5W"}
|
||||
{"name":"Œil exercé","type":"don","img":"systems/fvtt-mournblade/assets/icons/don.webp","data":{"description":"<p>L’Élu reçoit de la divinité qu’il vénère l’équivalent d’une Prédilection dans la Capacité spéciale Œil du sorcier (voir p. 199). Une fois par séance, il peut donc relancer le test de Clairvoyance + Perception.</p>","allegeance":"chaos","prerequis":"6 ou plus en Clairvoyance.","sacrifice":"<p>2 points d’Âme.</p>"},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.9Xrm1cfrCmkRtOBc"}},"_id":"zzz9JrtWjELdoAfK"}
|
||||
@@ -1,55 +0,0 @@
|
||||
{"name":"Chapeau fatigué","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":0,"prix":2},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.D26fUtZxrzcTR07B"}},"_id":"1cZd2hlTV9tykDED"}
|
||||
{"name":"Bœuf","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":50},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.whAhz9pO1NUt8Pjm"}},"_id":"3pvWKiyXhc9mmg70"}
|
||||
{"name":"Manteau","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":4},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.FZztAyGbF2cL9wyc"}},"_id":"40P9lOUYI16gEGZS"}
|
||||
{"name":"Poney","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":30},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.VKZrNcrCm9Ju7ayv"}},"_id":"7MdI99vLmaytPNKE"}
|
||||
{"name":"Tenue complète d’homme fortuné","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>(pantalons de coton, chemise de drap fi n, pourpoint de velours, chapeau, bottes montantes ou chaussures à boucles)</p>","rarete":7,"prix":6},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.dw6wLsNmwmEW79ei"}},"_id":"82fzHPjxUkGMCVUX"}
|
||||
{"name":"Laboratoire d’Alchimiste","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>Permet de mettre au point les objets les plus complexes (difficulté de 25 et au-delà)</p>","rarete":10,"prix":2000},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.eq0FJtwQd2jHbdvv"}},"_id":"8GK1Hn6le3JlsT6T"}
|
||||
{"name":"Vieille carne","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>Modificateur de Monte -2</p>","rarete":2,"prix":50},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.iikcWs26aEjtXDKo"}},"_id":"8NPElVs0cYrGbmXO"}
|
||||
{"name":"Nuit d’auberge à l’étable","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":3,"prix":1},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.uo464pRDCNIPsUvW"}},"_id":"9dEaZklkBs1GgdD7"}
|
||||
{"name":"Coursier","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>Modificateur de Monte +2</p>","rarete":7,"prix":120},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.t4PPYVE2B6B9tUy1"}},"_id":"AZDTSEUo52dnf37U"}
|
||||
{"name":"Papier, plume et encre","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":6,"prix":20},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.545S2qajtz6DSzzq"}},"_id":"Bg8jNqGusEdHxV00"}
|
||||
{"name":"Chariot couvert ou bâché","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":3,"prix":100},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.GRhCO7lW7NaHThqU"}},"_id":"FlK49ZIVnceUTy1P"}
|
||||
{"name":"Tenue complète bourgeoise","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p> (chausses, chemise, pourpoint, chapeau, bottes)</p>","rarete":6,"prix":30},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.XZSiLPhbhlj1Wil1"}},"_id":"HdlqqXJ393Pf3GxA"}
|
||||
{"name":"Nuit d’auberge dans une chambre particulière","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":5,"prix":5},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.xtlOL4VOsTDvyUvR"}},"_id":"LnKDtuhsacvcet7B"}
|
||||
{"name":"Louer une petite maison de ville pour un mois","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>Se paie généralement d’avance…</p>","rarete":0,"prix":40},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.Tfu7ywqXoXzXgP6H"}},"_id":"NZ28hyteLgOxlXC7"}
|
||||
{"name":"Repas de fête","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":5,"prix":4},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.gTCljExvFFpG7K1s"}},"_id":"OyRix5REF10GPbSp"}
|
||||
{"name":"Cheval d’attelage","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":4,"prix":80},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.KDfgGmQ4tUj765MN"}},"_id":"QHPoow1iDYEdExgs"}
|
||||
{"name":"Repas chaud","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":0,"prix":1},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.ErKXhChAfgugKU1a"}},"_id":"QojR7XS4mGjbMq5w"}
|
||||
{"name":"Charrette","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":70},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.4HRLU67Ipd1PFqUV"}},"_id":"RXMBJNKXfy3S3yck"}
|
||||
{"name":"Onguent de soin","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>10 tours de jeu sont nécessaires pour l’appliquer. Permet de récupérer [[/roll 1D4]] points de Santé après (10 – Trempe) heures</p>","rarete":5,"prix":20},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.RdqMNJzemUcCYO1H"}},"_id":"RyNJqjQvDbMixok0"}
|
||||
{"name":"Barge","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":5,"prix":300},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.l7tiShWweGQ8fzDp"}},"_id":"TyeTRm4FxLeRNDTU"}
|
||||
{"name":"Pommade à frotter pour faire circuler le sang","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>Protège des effets du froid (+ 5 aux tests de Trempe)</p>","rarete":4,"prix":2},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.BbzFc96y1QHi0Old"}},"_id":"VPkZxH3bkTR8WqXQ"}
|
||||
{"name":"Cape","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":2},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.HZkgEeG4apx4I4Tn"}},"_id":"WL0809BYVR9JxaMM"}
|
||||
{"name":"Poudre blanche","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>(à inhaler pour se sentir en forme)</p>\n<p>S’inhale le temps d’une action (à inhaler pour se sentir en forme) simple. Permet de récupérer immédiatement [[/roll 1D4]] cases de Santé (dégâts non létaux). À chaque inhalation, lancez un [[/roll 1d20]]. Sur un 1 ou un 11, celui qui vient de priser la poudre s’eff ondre et reste inconscient durant (10 – Trempe) heures.</p>\n<p> </p>","rarete":7,"prix":5},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.axOu6tHgHC7kYnG3"}},"_id":"WntSrr0j00DLntZV"}
|
||||
{"name":"Lanterne","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":10},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.OKydo5obAtmOVNup"}},"_id":"Wo0tplt93X9wemlV"}
|
||||
{"name":"Chien dressé","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":10},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.5jFTzXfwTgkVvjv7"}},"_id":"Wus5fIYO731eLG0i"}
|
||||
{"name":"Pain de route (rations pour une semaine)","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":0,"prix":10},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.18Hb7BDncVyHgyeM"}},"_id":"X4nIqFrjWy0hCVbZ"}
|
||||
{"name":"Matériel de survie","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>(sac, gourde, couverture, tente)</p>","rarete":2,"prix":5},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.wv6tWbuJjnZ7jiXH"}},"_id":"e8ZbG2WQCzJTRzBK"}
|
||||
{"name":"Herbes à infuser contre les fièvres légères","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":1},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.iL0fjlODZ4JnNpwN"}},"_id":"eH4V6AlAZNEuwlrw"}
|
||||
{"name":"Barque à rames, canoë","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":60},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.HKxNf2i5EEu5wAN5"}},"_id":"eXP5rck0IJK4Az8c"}
|
||||
{"name":"Petit atelier et son outillage","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>Permet de fabriquer les objets les plus simples (jusqu’à une difficulté de 20)</p>","rarete":8,"prix":50},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.TD0bN2tEIG8VqnbD"}},"_id":"fc2fReiW3mV6jekF"}
|
||||
{"name":"Antipoison","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>Confère un bonus de + 5 au test de Soins pour arrêter les pertes de points de Santé. Note : tous les poisons n’ont pas d’antipoison connu.</p>","rarete":7,"prix":50},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.7NDLIEeS4WVyXDqo"}},"_id":"fkleifA7qbOzRZCy"}
|
||||
{"name":"Fonderie","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":10,"prix":10000},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.Y14CMXm2avlCnqPC"}},"_id":"frS3jj7bs0l9zMGi"}
|
||||
{"name":"Roulotte","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":5,"prix":150},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.X5GxbvOMqQ8aHh49"}},"_id":"g5rDtYzyQ4IxUWXu"}
|
||||
{"name":"Forge","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":4,"prix":100},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.cma6u26NRsZL1awt"}},"_id":"gJStREuVAaaBuwXR"}
|
||||
{"name":"Miroir et peigne","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":10},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.IzlENy0Oxg82cke0"}},"_id":"hRlPFzsoUjbu3Noj"}
|
||||
{"name":"Tenue complète citadine","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>(chausses et chemise de coton, chapeau, chaussures)</p>","rarete":3,"prix":7},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.1ldZnq4lDPHOeVba"}},"_id":"hTLeVLn5yUd2etoS"}
|
||||
{"name":"Nuit d’auberge dans la salle commune","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":3,"prix":3},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.D7HcsRBK8uH6NBi4"}},"_id":"hc4w2PBpYLMQKsXS"}
|
||||
{"name":"Matériel de cuisine","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":0,"prix":2},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.lJafSILU0LtNMONn"}},"_id":"i8BBr1WjfphuLTLI"}
|
||||
{"name":"Chapeau à la mode","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":6,"prix":10},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.lmlWH96vjfTMN2OO"}},"_id":"kkNZoJdG86Dqci2b"}
|
||||
{"name":"Herbes à mâcher les lendemains de beuverie","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":1},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.STMfa8tVupqJPllD"}},"_id":"muCWd5AKL21AbTg8"}
|
||||
{"name":"Tenue complète paysanne","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>(chausses et chemise de jute, grand foulard, sandales)</p>","rarete":0,"prix":4},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.4WIrBhrpn4AlSnh3"}},"_id":"nJKLD6BqEHbBHmOY"}
|
||||
{"name":"Selle et fontes","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":20},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.J1HAwhIBjgkBZ3FH"}},"_id":"oIZv9KxLVQUOfi5d"}
|
||||
{"name":"Destrier","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":8,"prix":800},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.MF74kXfkiwLOwWNS"}},"_id":"oIob9qCttaQw7Aea"}
|
||||
{"name":"Vêtements rapiécés, nippes","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":0,"prix":2},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.rPYf2u8b3qizxxFk"}},"_id":"sLZgByw2rA2PvQms"}
|
||||
{"name":"Petit voilier","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":6,"prix":50},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.tuAI18GrGFkqVHpQ"}},"_id":"tAFXmBLUXcSUKZRL"}
|
||||
{"name":"Matériel hivernal","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>(piolets, raquettes, manteau et graisses)</p>","rarete":4,"prix":20},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.LpdplcMj4j7dWnZU"}},"_id":"tRSk0np2smLpdixK"}
|
||||
{"name":"Herbes parfumées contre les mauvaises odeurs","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":1},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.y6G9LprDaOAoVn9v"}},"_id":"tXYzLGS6SFolD8k4"}
|
||||
{"name":"Barque de pêcheur avec voile","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":3,"prix":100},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.ZXEq8059S3YlFhN0"}},"_id":"vUFnyal2islmmpZF"}
|
||||
{"name":"Torche","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p> (lot de 3)</p>","rarete":1,"prix":5},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.aVBCMdM69vwfrSMW"}},"_id":"vahvt9QGYjnRlY4p"}
|
||||
{"name":"Verre de vin ou de bière","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":0,"prix":1},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.5yA9UC3kExUx6CqN"}},"_id":"vmSrygoMxy6G87EO"}
|
||||
{"name":"Matériel d’escalade","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>(marteau, cordes, pitons)</p>","rarete":3,"prix":8},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.TXj9b73MGGI6L9Av"}},"_id":"vtpjGFTYO7fiKXwE"}
|
||||
{"name":"Longue-vue","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":8,"prix":250},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.n1UQvGJ8Zq6dGuEY"}},"_id":"wHU6MvZuBMmN9xfG"}
|
||||
{"name":"Dose de poison violent","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>Voir le chapitre « Le Combat et la santé » pour connaître les eff ets des principaux poisons</p>","rarete":6,"prix":3},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.EuGLW5D5SIP58uaa"}},"_id":"wQ8cgwazhlIF6WG7"}
|
||||
{"name":"Louer une grange pour un mois","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"<p>Se paie généralement d’avance...</p>","rarete":0,"prix":10},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.8utWpkdD1FvHSYfv"}},"_id":"xy356PaIZisHJdUR"}
|
||||
{"name":"Grande écharpe","type":"equipement","img":"systems/fvtt-mournblade/assets/icons/equipement.webp","data":{"description":"","rarete":2,"prix":2},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"RiMAsQHaUMojde7N":3},"flags":{"core":{"sourceId":"Item.j5Hei6F2XN5nrh7t"}},"_id":"y47dBO3Mf5Pn7tOd"}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user