Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5a0a47b25 | |||
| a48f447b99 | |||
| e398b157f4 | |||
| a349402306 | |||
| bdc305abd8 | |||
| 6ef454d533 | |||
| 74f1b581f7 | |||
| df0a93d715 | |||
| d62d14c1da | |||
| 783d4a16e6 | |||
| fa1c02caa6 | |||
| 86b2cd5777 | |||
| e3002dd602 | |||
|
|
89b3e401a4 | ||
|
|
f1c4f43c66 | ||
|
|
78ad10918c | ||
|
|
770e80fd3d | ||
|
|
d63526831c | ||
|
|
7f10f617c4 | ||
|
|
5de3533691 | ||
|
|
11a10bbbfc | ||
|
|
47dd386fe0 |
81
.gitea/workflows/release.yaml
Normal file
81
.gitea/workflows/release.yaml
Normal file
@@ -0,0 +1,81 @@
|
||||
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: 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/${{gitea.repository}}/releases/download/latest/system.json
|
||||
download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/mgt2.zip
|
||||
|
||||
# Install Node.js and build the JS bundle
|
||||
- name: Setup Node.js
|
||||
uses: https://github.com/actions/setup-node@v3
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build JS bundle
|
||||
run: npm run build
|
||||
|
||||
# Install Sass and compile CSS
|
||||
- name: Install Sass
|
||||
run: npm install -g sass
|
||||
|
||||
- name: Build CSS
|
||||
run: sass src/sass/mgt2.sass styles/mgt2.min.css --style=compressed --no-source-map
|
||||
|
||||
# Create a zip file with all files required by the system to add to the release
|
||||
- name: Install zip
|
||||
run: apt update -y && apt install -y zip
|
||||
|
||||
- name: Create release zip
|
||||
run: zip -r ./mgt2.zip system.json README.md LICENSE mgt2.bundle.js assets/ lang/ styles/ templates/
|
||||
|
||||
- name: setup go
|
||||
uses: https://github.com/actions/setup-go@v4
|
||||
with:
|
||||
go-version: ">=1.20.1"
|
||||
|
||||
- name: Use Go Action
|
||||
id: use-go-action
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
files: |-
|
||||
./mgt2.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: "mgt2"
|
||||
version: ${{github.event.release.tag_name}}
|
||||
manifest: "https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/latest/system.json"
|
||||
notes: "https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/mgt2.zip"
|
||||
compatibility-minimum: "13"
|
||||
compatibility-verified: "14"
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -27,4 +27,6 @@
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
src/
|
||||
mgt2.zip
|
||||
assets/regles/
|
||||
.github
|
||||
|
||||
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,6 +1,22 @@
|
||||
## [0.1.2](https://github.com/xdy/twodsix-foundryvtt/compare/v4.24.5...v4.24.6) (2024-05-04)
|
||||
* `system.json` no longer generates warnings ([#3](https://github.com/JDR-Ninja/foundryvtt-mgt2/issues/3))
|
||||
|
||||
### Bug Fixes
|
||||
## [0.1.4] (2024-05-25)
|
||||
|
||||
### Correctifs
|
||||
* Erreur lors du calcul du poids lors de différent événement (Drop, Delete)
|
||||
|
||||
## [0.1.3] (2024-05-24)
|
||||
|
||||
### Correctifs
|
||||
* Localisation
|
||||
* Ajouter valeur de la difficulté dans le label
|
||||
|
||||
### Features
|
||||
* Support de la v12
|
||||
|
||||
## [0.1.2] (2024-05-16)
|
||||
|
||||
### Correctifs
|
||||
* Affichage de la difficulté pour les Talents Psioniques
|
||||
* Ajout de scrollbar dans la feuille de personnage
|
||||
* Drag & Drop sur la fiche des Carrières, Maladies, Contacts, Espèces
|
||||
|
||||
249
lang/en.json
249
lang/en.json
@@ -18,6 +18,12 @@
|
||||
"Save": "Save",
|
||||
"Cancel": "Cancel",
|
||||
"Close": "Close",
|
||||
"Dialog": {
|
||||
"ConfirmDeleteTitle": "Confirm Deletion",
|
||||
"ConfirmDeleteContent": "Are you sure you want to delete \"{name}\"?",
|
||||
"Yes": "Yes",
|
||||
"No": "No"
|
||||
},
|
||||
"Themes": {
|
||||
"BlackAndRed": "Classic Traveller Cover",
|
||||
"Mwamba": "Oppa Mwamba Style",
|
||||
@@ -54,20 +60,21 @@
|
||||
"Gender": "Gender",
|
||||
"Pronouns": "Pronouns",
|
||||
"Actor": {
|
||||
"StudyPeriod":"Study Period",
|
||||
"TrainingInSkill":"Training In Skill",
|
||||
"Completed":"Completed",
|
||||
"Weeks":"Weeks",
|
||||
"NewCareer":"New Career",
|
||||
"AddCareer":"Add Career",
|
||||
"EditCareer":"Edit Career",
|
||||
"EditTrait":"Éditer Trait",
|
||||
"StudyPeriod": "Study Period",
|
||||
"TrainingInSkill": "Training In Skill",
|
||||
"Completed": "Completed",
|
||||
"Weeks": "Weeks",
|
||||
"NewCareer": "New Career",
|
||||
"AddCareer": "Add Career",
|
||||
"ThisTrait": "this trait",
|
||||
"EditCareer": "Edit Career",
|
||||
"EditTrait": "Éditer Trait",
|
||||
"DeleteTrait": "Supprimer Trait",
|
||||
"DeleteCareer":"Delete Career",
|
||||
"NewSkill":"New Skill",
|
||||
"DeleteSkill":"Delete Skill",
|
||||
"EditSkill":"EditSkill",
|
||||
"PsionicTalents":"Psionic Talents",
|
||||
"DeleteCareer": "Delete Career",
|
||||
"NewSkill": "New Skill",
|
||||
"DeleteSkill": "Delete Skill",
|
||||
"EditSkill": "EditSkill",
|
||||
"PsionicTalents": "Psionic Talents",
|
||||
"NewPsionicTalent": "New Psionic Talent",
|
||||
"AddPsionicTalent": "Add Psionic Talent",
|
||||
"EditPsionic": "Edit Psionic",
|
||||
@@ -123,6 +130,8 @@
|
||||
"NewArmor": "New Armor",
|
||||
"NewAugment": "New Augment",
|
||||
"NewContact": "New Contact",
|
||||
"NewComputer": "New Computer",
|
||||
"NewContainer": "New Container",
|
||||
"NewDisease": "New Diseases",
|
||||
"NewEquipment": "New Equipment",
|
||||
"NewItem": "New Item",
|
||||
@@ -250,14 +259,14 @@
|
||||
},
|
||||
"Difficulty": {
|
||||
"NA": "Not Applicable",
|
||||
"Simple": "Simple",
|
||||
"Easy": "Easy",
|
||||
"Routine": "Routine",
|
||||
"Average": "Average",
|
||||
"Difficult": "Difficult",
|
||||
"VeryDifficult": "Very Difficult",
|
||||
"Formidable": "Formidable",
|
||||
"Impossible": "Impossible"
|
||||
"Simple": "Simple (2)",
|
||||
"Easy": "Easy (4)",
|
||||
"Routine": "Routine (6)",
|
||||
"Average": "Average (8)",
|
||||
"Difficult": "Difficult (10)",
|
||||
"VeryDifficult": "Very Difficult (12)",
|
||||
"Formidable": "Formidable (14)",
|
||||
"Impossible": "Impossible (16)"
|
||||
},
|
||||
"PsionicReach": {
|
||||
"NA": "N/A",
|
||||
@@ -341,7 +350,36 @@
|
||||
"EncumbranceDM": "Encumbrance (DM -2)",
|
||||
"FatigueDM": "Fatigue (DM -2)",
|
||||
"Boon": "Boon",
|
||||
"Bane": "Bane"
|
||||
"Bane": "Bane",
|
||||
"CreatureSkill": "Skill",
|
||||
"NoSkill": "No skill",
|
||||
"Days": "Days",
|
||||
"RangedModifiers": "Ranged Modifiers",
|
||||
"Range": "Range",
|
||||
"RangeShort": "Short Range",
|
||||
"RangeNormal": "Normal Range",
|
||||
"RangeLong": "Long Range",
|
||||
"RangeExtreme": "Extreme Range",
|
||||
"Aim": "Aim",
|
||||
"LaserSight": "Laser Sight",
|
||||
"FastTarget": "Fast-Moving Target",
|
||||
"Cover": "Cover / Diving",
|
||||
"Prone": "Prone Target",
|
||||
"MeleeModifiers": "Melee Modifiers",
|
||||
"Parry": "Parry (defender's Melee score)",
|
||||
"Dodge": "Target Dodging",
|
||||
"DodgeDM": "Dodge DM",
|
||||
"FireMode": "Fire Mode",
|
||||
"AutoSingle": "Single",
|
||||
"AutoBurst": "Burst",
|
||||
"AutoFull": "Full Auto",
|
||||
"AutoNoAim": "Burst / Full-Auto: all aiming advantages are cancelled.",
|
||||
"AutoBurstInfo": "Burst (Auto {level}) — +{level} damage — ammo: {ammo}",
|
||||
"AutoFullInfo": "Full Auto (Auto {level}) — {level} attacks — ammo: {ammo}",
|
||||
"ScopeActive": "Scope active",
|
||||
"ScopeHint": "Scope trait: ignores the automatic Extreme Range rule beyond 100m, provided the traveller aims before firing. Select the actual range below.",
|
||||
"ZeroGActive": "Zero-G",
|
||||
"ZeroGHint": "Zero-G trait: this weapon has little or no recoil. It can be used in low or zero gravity without requiring an Athletics (DEX) test. No additional DM required in those conditions."
|
||||
},
|
||||
"Timeframes": {
|
||||
"Normal": "Normal",
|
||||
@@ -353,10 +391,30 @@
|
||||
"ApplyDamages": "Apply Damages",
|
||||
"Damages": "Roll damages",
|
||||
"Success": "Success",
|
||||
"Failure": "Failure"
|
||||
"Failure": "Failure",
|
||||
"Effect": "Effect",
|
||||
"Dice": "Dice",
|
||||
"Result": "Result",
|
||||
"DiceModifier": "Dice Modifier",
|
||||
"APIgnore": "AP",
|
||||
"APIgnoreHint": "This weapon ignores {ap} points of armor (AP trait)",
|
||||
"BlastArea": "Blast",
|
||||
"BlastHint": "Blast weapon: damage is applied to all targets within the blast radius (in meters). Dodge reactions cannot be used, but targets may dive for cover. Cover between target and explosion center applies.",
|
||||
"BlastRules": "No dodge — dive for cover possible",
|
||||
"StunWeapon": "Stun Weapon — END damage only",
|
||||
"StunHint": "Stun weapon: damage is only deducted from END (after armor). If END reaches 0, the target is incapacitated for a number of rounds equal to the difference between damage dealt and the target's END. Fully healed after 1 hour of rest."
|
||||
},
|
||||
"Radiation": {
|
||||
"Badge": "Radiation Weapon — RADs",
|
||||
"Hint": "This weapon emits radiation. On a successful attack, the target receives 2D×20 additional rads. Click the button to roll radiation damage.",
|
||||
"Title": "Radiation Damage",
|
||||
"Rads": "RADs",
|
||||
"RollButton": "Roll RADs",
|
||||
"Rules": "See p.78 for radiation effects (nausea, illness, death). ×3 multiplier for starship-scale weapons."
|
||||
}
|
||||
},
|
||||
"Items": {
|
||||
"SelectAll": "(all)",
|
||||
"Age": "Age",
|
||||
"Armor": "Armor",
|
||||
"Armors": "Armors",
|
||||
@@ -390,7 +448,7 @@
|
||||
"Informations": "Informations",
|
||||
"Improvement": "Improvement",
|
||||
"Interval": "Interval",
|
||||
"IsMelee": "IsMelee",
|
||||
"IsMelee": "Melee Weapon",
|
||||
"Items": "Items",
|
||||
"Level": "Level",
|
||||
"Location": "Location",
|
||||
@@ -407,6 +465,7 @@
|
||||
"Occupation": "Occupation",
|
||||
"OnHand": "On Hand",
|
||||
"Options": "Options",
|
||||
"PerDay": "per day",
|
||||
"PSICost": "PSI Cost",
|
||||
"Powered": "Powered",
|
||||
"Processing": "Processing",
|
||||
@@ -440,10 +499,150 @@
|
||||
"Weightless": "Weightless",
|
||||
"Quantity": "Quantity"
|
||||
},
|
||||
"WeaponTraits": {
|
||||
"SectionTitle": "Traits",
|
||||
"AP": "AP (Armor Piercing)",
|
||||
"APHint": "Ignores X points of armor protection",
|
||||
"Auto": "Auto",
|
||||
"AutoHint": "Automatic fire — Burst and Full-Auto modes available",
|
||||
"Blast": "Blast",
|
||||
"BlastHint": "Explosion radius in meters",
|
||||
"Bulky": "Bulky",
|
||||
"BulkyHint": "Requires STR 9+ or suffer negative DM",
|
||||
"VeryBulky": "Very Bulky",
|
||||
"VeryBulkyHint": "Requires STR 12+ or suffer negative DM",
|
||||
"Stun": "Stun",
|
||||
"StunHint": "Non-lethal damage — deducted from END only",
|
||||
"Smart": "Smart",
|
||||
"SmartHint": "Guided projectiles — DM = TL difference (min +1, max +6)",
|
||||
"Radiation": "Radiation",
|
||||
"RadiationHint": "Inflicts 2D×20 rads on target",
|
||||
"Scope": "Scope",
|
||||
"ScopeHint": "Ignores extreme range rule (>100m) if aiming",
|
||||
"ZeroG": "Zero-G",
|
||||
"ZeroGHint": "No recoil — no Athletics check needed in microgravity"
|
||||
},
|
||||
"Durations": {
|
||||
"Seconds": "Seconds",
|
||||
"Minutes": "Minutes",
|
||||
"Heures": "Hours"
|
||||
},
|
||||
"Creature": {
|
||||
"Name": "Name",
|
||||
"Life": "HP",
|
||||
"Speed": "Speed",
|
||||
"Armor": "Armor",
|
||||
"Psi": "PSI",
|
||||
"Initiative": "Initiative",
|
||||
"SizeHint": "Estimated size based on HP",
|
||||
"Behavior": "Behavior",
|
||||
"TabSkills": "Skills",
|
||||
"TabAttacks": "Attacks",
|
||||
"TabTraits": "Traits",
|
||||
"TabInfo": "Information",
|
||||
"TabCombat": "Combat",
|
||||
"SkillName": "Skill",
|
||||
"SkillLevel": "Level",
|
||||
"SkillNote": "Note",
|
||||
"AttackName": "Attack",
|
||||
"AttackDamage": "Damage",
|
||||
"AttackSkill": "Skill",
|
||||
"TraitName": "Trait",
|
||||
"TraitValue": "Value",
|
||||
"AddSkill": "Add a skill",
|
||||
"AddAttack": "Add an attack",
|
||||
"AddTrait": "Add a trait",
|
||||
"NoSkills": "No skills",
|
||||
"NoAttacks": "No attacks",
|
||||
"NoTraits": "No traits",
|
||||
"Delete": "Delete",
|
||||
"RollSkill": "Skill roll",
|
||||
"RollAttack": "Attack roll",
|
||||
"SkillLabel": "Skill",
|
||||
"RollTitle": "Creature roll"
|
||||
},
|
||||
"SpeedBands": {
|
||||
"Stoppped": "Stopped",
|
||||
"Idle": "Idle",
|
||||
"VerySlow": "Very Slow",
|
||||
"Slow": "Slow",
|
||||
"Medium": "Medium",
|
||||
"High": "High",
|
||||
"Fast": "Fast",
|
||||
"VeryFast": "Very Fast",
|
||||
"Subsonic": "Subsonic",
|
||||
"Hypersonic": "Hypersonic"
|
||||
},
|
||||
"Vehicule": {
|
||||
"Hull": "Hull",
|
||||
"ArmorFront": "Front",
|
||||
"ArmorSides": "Sides",
|
||||
"ArmorRear": "Rear",
|
||||
"Armor": "Armor",
|
||||
"SpeedCruise": "Cruise Speed",
|
||||
"SpeedMax": "Max Speed",
|
||||
"Agility": "Agility",
|
||||
"Crew": "Crew",
|
||||
"Passengers": "Passengers",
|
||||
"Cargo": "Cargo (t)",
|
||||
"Shipping": "Shipping (t)",
|
||||
"Cost": "Cost (Cr)",
|
||||
"Autopilot": "Autopilot",
|
||||
"TabStats": "Statistics",
|
||||
"TabDescription": "Description"
|
||||
},
|
||||
"CreatureBehaviorType": {
|
||||
"herbivore": "Herbivore",
|
||||
"carnivore": "Carnivore",
|
||||
"charognard": "Scavenger",
|
||||
"omnivore": "Omnivore"
|
||||
},
|
||||
"CreatureBehaviorSubType": {
|
||||
"accumulateur": "Gatherer",
|
||||
"brouteur": "Grazer",
|
||||
"filtreur": "Filter",
|
||||
"intermittent": "Intermittent",
|
||||
"chasseur": "Hunter",
|
||||
"detourneux": "Hijacker",
|
||||
"guetteur": "Pouncer",
|
||||
"mangeur": "Eater",
|
||||
"piegeur": "Trapper",
|
||||
"intimidateur": "Intimidator",
|
||||
"necrophage": "Carrion-eater",
|
||||
"reducteur": "Reducer",
|
||||
"opportuniste": "Opportunist"
|
||||
},
|
||||
"Healing": {
|
||||
"Title": "Healing",
|
||||
"FirstAid": "First Aid",
|
||||
"Surgery": "Surgery",
|
||||
"MedicalCare": "Medical Care",
|
||||
"NaturalHealing": "Natural Healing",
|
||||
"WillRestore": "Will restore",
|
||||
"SurgeryFailed": "Surgery failed - patient takes damage",
|
||||
"ApplyHealing": "Apply Healing",
|
||||
"ApplySurgeryDamage": "Apply Surgery Damage",
|
||||
"SurgeryDamage": "surgery damage",
|
||||
"ApplyToTarget": "Apply healing to selected target",
|
||||
"NoMedicineSkill": "No Medicine Skill",
|
||||
"Heals": "heals"
|
||||
},
|
||||
"Notifications": {
|
||||
"HealingApplied": "{name} has been healed for {amount} points.",
|
||||
"DamageApplied": "{name} has taken {amount} damage.",
|
||||
"DamageAppliedAP": "{name} has taken {amount} damage (armor reduced by {ap} from AP trait).",
|
||||
"BulkyPenalty": "Bulky weapon: insufficient STR (DM too low). Penalty {penalty} applied to roll.",
|
||||
"VeryBulkyPenalty": "Very Bulky weapon: insufficient STR (DM too low). Penalty {penalty} applied to roll.",
|
||||
"StunDamageApplied": "{name} took {amount} stun damage (END only).",
|
||||
"StunIncapacitated": "{name} is incapacitated for {rounds} round(s)! (END reduced to 0)",
|
||||
"AmmoUsed": "{weapon}: {used} round(s) expended. Magazine remaining: {remaining}.",
|
||||
"AmmoEmpty": "{weapon}: magazine empty after this shot!",
|
||||
"NoAmmo": "{weapon}: magazine empty! Reload before firing."
|
||||
},
|
||||
"Errors": {
|
||||
"NoTokenSelected": "No active target. Target a token on the scene before applying.",
|
||||
"InvalidRollFormula": "Invalid roll formula."
|
||||
}
|
||||
}
|
||||
},
|
||||
"TYPES.Actor.creature": "Creature"
|
||||
}
|
||||
257
lang/fr.json
257
lang/fr.json
@@ -18,6 +18,12 @@
|
||||
"Save": "Sauvegarder",
|
||||
"Cancel": "Annuler",
|
||||
"Close": "Fermer",
|
||||
"Dialog": {
|
||||
"ConfirmDeleteTitle": "Confirmer la suppression",
|
||||
"ConfirmDeleteContent": "Êtes-vous sûr de vouloir supprimer \"{name}\" ?",
|
||||
"Yes": "Oui",
|
||||
"No": "Non"
|
||||
},
|
||||
"Themes": {
|
||||
"BlackAndRed": "Couverture Classique Traveller",
|
||||
"Mwamba": "Oppa Mwamba Style",
|
||||
@@ -29,7 +35,7 @@
|
||||
"hint": "Choisissez un thème"
|
||||
},
|
||||
"useWeightMetric": {
|
||||
"name": "Utiliser le Système Métrique pour le Poid",
|
||||
"name": "Utiliser le Système Métrique pour le Poids",
|
||||
"hint": "kilogramme (kg) au lieu de la livre (lbs)"
|
||||
},
|
||||
"useDistanceMetric": {
|
||||
@@ -54,20 +60,21 @@
|
||||
"Gender": "Genre",
|
||||
"Pronouns": "Pronoms",
|
||||
"Actor": {
|
||||
"StudyPeriod":"Période d'étude",
|
||||
"TrainingInSkill":"Compétence en formation",
|
||||
"Completed":"Completée",
|
||||
"Weeks":"Semaines",
|
||||
"NewCareer":"Nouvelle Carrière",
|
||||
"AddCareer":"Ajouter Carrière",
|
||||
"EditCareer":"Éditer Carrière",
|
||||
"EditTrait":"Éditer Trait",
|
||||
"StudyPeriod": "Période d'étude",
|
||||
"TrainingInSkill": "Compétence en formation",
|
||||
"Completed": "Completée",
|
||||
"Weeks": "Semaines",
|
||||
"NewCareer": "Nouvelle Carrière",
|
||||
"AddCareer": "Ajouter Carrière",
|
||||
"ThisTrait": "ce trait",
|
||||
"EditCareer": "Éditer Carrière",
|
||||
"EditTrait": "Éditer Trait",
|
||||
"DeleteTrait": "Supprimer Trait",
|
||||
"DeleteCareer":"Supprimer Carrière",
|
||||
"NewSkill":"Nouvelle Compétence",
|
||||
"DeleteSkill":"Supprimer Compétence",
|
||||
"EditSkill":"Éditer Compétence",
|
||||
"PsionicTalents":"Talents Psionique",
|
||||
"DeleteCareer": "Supprimer Carrière",
|
||||
"NewSkill": "Nouvelle Compétence",
|
||||
"DeleteSkill": "Supprimer Compétence",
|
||||
"EditSkill": "Éditer Compétence",
|
||||
"PsionicTalents": "Talents Psionique",
|
||||
"NewPsionicTalent": "Nouveau Talent Psionique",
|
||||
"AddPsionicTalent": "Ajouter Talent Psionique",
|
||||
"EditPsionic": "Éditer Talent Psionique",
|
||||
@@ -123,6 +130,8 @@
|
||||
"NewArmor": "Nouvelle Armure",
|
||||
"NewAugment": "Nouvelle Augmentation",
|
||||
"NewContact": "Nouveau Contact",
|
||||
"NewContainer": "Nouveau Conteneur",
|
||||
"NewComputer": "Nouvel Ordinateur",
|
||||
"NewDisease": "Nouvelle Maladie",
|
||||
"NewEquipment": "Nouvelle Équipement",
|
||||
"NewItem": "Nouveau Objet",
|
||||
@@ -250,14 +259,14 @@
|
||||
},
|
||||
"Difficulty": {
|
||||
"NA": "N/A",
|
||||
"Simple": "Simple",
|
||||
"Easy": "Facile",
|
||||
"Routine": "Routine",
|
||||
"Average": "Moyenne",
|
||||
"Difficult": "Difficile",
|
||||
"VeryDifficult": "Très Difficile",
|
||||
"Formidable": "Formidable",
|
||||
"Impossible": "Impossible"
|
||||
"Simple": "Simple (2)",
|
||||
"Easy": "Facile (4)",
|
||||
"Routine": "Routine (6)",
|
||||
"Average": "Moyenne (8)",
|
||||
"Difficult": "Difficile (10)",
|
||||
"VeryDifficult": "Très Difficile (12)",
|
||||
"Formidable": "Formidable (14)",
|
||||
"Impossible": "Impossible (16)"
|
||||
},
|
||||
"PsionicReach": {
|
||||
"NA": "N/A",
|
||||
@@ -341,7 +350,36 @@
|
||||
"EncumbranceDM": "Encombrement (MD -2)",
|
||||
"FatigueDM": "Fatigue (MD -2)",
|
||||
"Boon": "Avantage",
|
||||
"Bane": "Désavantage"
|
||||
"Bane": "Désavantage",
|
||||
"CreatureSkill": "Compétence",
|
||||
"NoSkill": "Aucune compétence",
|
||||
"Days": "Jours",
|
||||
"RangedModifiers": "Modificateurs de tir",
|
||||
"Range": "Portée",
|
||||
"RangeShort": "Courte portée",
|
||||
"RangeNormal": "Portée normale",
|
||||
"RangeLong": "Longue portée",
|
||||
"RangeExtreme": "Portée extrême",
|
||||
"Aim": "Visée",
|
||||
"LaserSight": "Pointeur laser",
|
||||
"FastTarget": "Cible bougeant vite",
|
||||
"Cover": "À couvert / Plongé",
|
||||
"Prone": "À plat ventre",
|
||||
"MeleeModifiers": "Modificateurs de mêlée",
|
||||
"Parry": "Parade (score Mêlée du défenseur)",
|
||||
"Dodge": "Esquive de la cible",
|
||||
"DodgeDM": "MD Esquive",
|
||||
"FireMode": "Mode de tir",
|
||||
"AutoSingle": "Simple",
|
||||
"AutoBurst": "Rafale",
|
||||
"AutoFull": "Auto complet",
|
||||
"AutoNoAim": "Rafale / Auto : les avantages de la visée sont annulés.",
|
||||
"AutoBurstInfo": "Rafale (Auto {level}) — +{level} dégâts — munitions: {ammo}",
|
||||
"AutoFullInfo": "Auto complet (Auto {level}) — {level} attaques — munitions: {ammo}",
|
||||
"ScopeActive": "Viseur actif",
|
||||
"ScopeHint": "Trait Viseur : ignore la règle Portée Extrême automatique au-delà de 100m, à condition de viser avant de tirer. Choisissez la portée réelle dans le sélecteur ci-dessous.",
|
||||
"ZeroGActive": "Zéro-G",
|
||||
"ZeroGHint": "Trait Zéro-G : cette arme a peu ou pas de recul. Elle peut être utilisée en gravité faible ou nulle sans nécessiter de test d'Athlétisme (DEX). Aucun MD supplémentaire requis dans ces conditions."
|
||||
},
|
||||
"Timeframes": {
|
||||
"Normal": "Normal",
|
||||
@@ -353,10 +391,30 @@
|
||||
"ApplyDamages": "Appliquer Dégâts",
|
||||
"Damages": "Lancer les Dégâts",
|
||||
"Success": "Succès",
|
||||
"Failure": "Échec"
|
||||
"Failure": "Échec",
|
||||
"Effect": "Effet",
|
||||
"Dice": "Dés",
|
||||
"Result": "Résultat",
|
||||
"DiceModifier": "Modificateur de dés",
|
||||
"APIgnore": "AP",
|
||||
"APIgnoreHint": "Cette arme ignore {ap} points d'armure (trait AP)",
|
||||
"BlastArea": "Explosion",
|
||||
"BlastHint": "Arme à explosion : les dégâts sont infligés à toutes les cibles dans le rayon indiqué (en mètres). Pas d'esquive possible, mais possibilité de plonger à couvert. Le couvert entre la cible et le centre de l'explosion s'applique.",
|
||||
"BlastRules": "Pas d'esquive — plonger à couvert possible",
|
||||
"StunWeapon": "Arme Incapacitante — dégâts END uniquement",
|
||||
"StunHint": "Arme incapacitante : les dégâts sont déduits uniquement de l'END (après armure). Si l'END atteint 0, la cible est neutralisée pendant un nombre de rounds égal à la différence entre les dégâts et l'END initiale. Guérison complète après 1h de repos."
|
||||
},
|
||||
"Radiation": {
|
||||
"Badge": "Arme à Rayonnement — RADs",
|
||||
"Hint": "Cette arme émet des radiations. En cas d'attaque réussie, la cible reçoit 2D×20 rads supplémentaires. Cliquez le bouton pour lancer les dégâts de RAD.",
|
||||
"Title": "Dégâts de Rayonnement",
|
||||
"Rads": "RADs",
|
||||
"RollButton": "Lancer RADs",
|
||||
"Rules": "Consultez p.78 pour l'effet des radiations (nausées, maladies, mort). Multiplicé ×3 pour une arme à l'échelle spatiale."
|
||||
}
|
||||
},
|
||||
"Items": {
|
||||
"SelectAll": "(tous)",
|
||||
"Age": "Âge",
|
||||
"Armor": "Armure",
|
||||
"Armors": "Armures",
|
||||
@@ -390,7 +448,7 @@
|
||||
"Informations": "Informations",
|
||||
"Improvement": "Améliorations",
|
||||
"Interval": "Intervalle",
|
||||
"IsMelee": "Est Mêlée",
|
||||
"IsMelee": "Arme de Mêlée",
|
||||
"Items": "Objets",
|
||||
"Level": "Niveau",
|
||||
"Location": "Localisation",
|
||||
@@ -407,9 +465,10 @@
|
||||
"Occupation": "Profession",
|
||||
"OnHand": "Sur Soi",
|
||||
"Options": "Options",
|
||||
"PerDay": "par jour",
|
||||
"PSICost": "Coût PSI",
|
||||
"Powered": "Alimenté",
|
||||
"Processing": "Traitement",
|
||||
"Processing": "Capacité de Traitement",
|
||||
"Protection": "Protection",
|
||||
"PsionicSkill": "Compétence Psionique",
|
||||
"Qty": "Qté",
|
||||
@@ -436,14 +495,154 @@
|
||||
"Type": "Type",
|
||||
"Weapon": "Arme",
|
||||
"Weapons": "Armes",
|
||||
"Weight": "Poid",
|
||||
"Weightless": "Aucun Poid",
|
||||
"Weight": "Poids",
|
||||
"Weightless": "Aucun Poids",
|
||||
"Quantity": "Quantité"
|
||||
},
|
||||
"WeaponTraits": {
|
||||
"SectionTitle": "Traits",
|
||||
"AP": "AP (Perforant)",
|
||||
"APHint": "Ignore X points de protection d'armure",
|
||||
"Auto": "Auto",
|
||||
"AutoHint": "Tir automatique — Rafale et Auto complet disponibles",
|
||||
"Blast": "Explosion",
|
||||
"BlastHint": "Rayon de l'explosion en mètres",
|
||||
"Bulky": "Encombrant",
|
||||
"BulkyHint": "Requiert FOR 9+ sinon MD négatif",
|
||||
"VeryBulky": "Très Encombrant",
|
||||
"VeryBulkyHint": "Requiert FOR 12+ sinon MD négatif",
|
||||
"Stun": "Incapacitante",
|
||||
"StunHint": "Dégâts non létaux — déduits uniquement de l'END",
|
||||
"Smart": "Intelligente",
|
||||
"SmartHint": "Projectiles guidés — MD = différence de NT (min +1, max +6)",
|
||||
"Radiation": "Rayonnement",
|
||||
"RadiationHint": "Inflige 2D×20 rads à la cible",
|
||||
"Scope": "Viseur",
|
||||
"ScopeHint": "Ignore la règle portée extrême (>100m) si le tireur vise",
|
||||
"ZeroG": "Zéro-G",
|
||||
"ZeroGHint": "Pas de recul — aucun jet d'Athlétisme requis en microgravité"
|
||||
},
|
||||
"Durations": {
|
||||
"Seconds": "Secondes",
|
||||
"Minutes": "Minutes",
|
||||
"Heures": "Heures"
|
||||
},
|
||||
"Creature": {
|
||||
"Name": "Nom",
|
||||
"Life": "PdV",
|
||||
"Speed": "Vitesse",
|
||||
"Armor": "Armure",
|
||||
"Psi": "PSI",
|
||||
"Initiative": "Initiative",
|
||||
"SizeHint": "Taille estimée selon les PdV",
|
||||
"Behavior": "Comportement",
|
||||
"TabSkills": "Compétences",
|
||||
"TabAttacks": "Attaques",
|
||||
"TabTraits": "Traits",
|
||||
"TabInfo": "Informations",
|
||||
"TabCombat": "Combat",
|
||||
"SkillName": "Compétence",
|
||||
"SkillLevel": "Niveau",
|
||||
"SkillNote": "Note",
|
||||
"AttackName": "Attaque",
|
||||
"AttackDamage": "Dommages",
|
||||
"AttackSkill": "Compétence",
|
||||
"TraitName": "Trait",
|
||||
"TraitValue": "Valeur",
|
||||
"AddSkill": "Ajouter une compétence",
|
||||
"AddAttack": "Ajouter une attaque",
|
||||
"AddTrait": "Ajouter un trait",
|
||||
"NoSkills": "Aucune compétence",
|
||||
"NoAttacks": "Aucune attaque",
|
||||
"NoTraits": "Aucun trait",
|
||||
"Delete": "Supprimer",
|
||||
"RollSkill": "Jet de compétence",
|
||||
"RollAttack": "Jet d'attaque",
|
||||
"SkillLabel": "Compétence",
|
||||
"RollTitle": "Jet de créature"
|
||||
},
|
||||
"SpeedBands": {
|
||||
"Stoppped": "Arrêté",
|
||||
"Idle": "Au ralenti",
|
||||
"VerySlow": "Très lent",
|
||||
"Slow": "Lent",
|
||||
"Medium": "Moyen",
|
||||
"High": "Élevé",
|
||||
"Fast": "Rapide",
|
||||
"VeryFast": "Très rapide",
|
||||
"Subsonic": "Subsonique",
|
||||
"Hypersonic": "Hypersonique"
|
||||
},
|
||||
"Vehicule": {
|
||||
"Hull": "Coque",
|
||||
"ArmorFront": "Av.",
|
||||
"ArmorSides": "Lat.",
|
||||
"ArmorRear": "Arr.",
|
||||
"Armor": "Armure",
|
||||
"SpeedCruise": "Vitesse croisière",
|
||||
"SpeedMax": "Vitesse max",
|
||||
"Agility": "Agilité",
|
||||
"Crew": "Équipage",
|
||||
"Passengers": "Passagers",
|
||||
"Cargo": "Cargo (t)",
|
||||
"Shipping": "Expédition (t)",
|
||||
"Cost": "Coût (Cr)",
|
||||
"Autopilot": "Autopilote",
|
||||
"TabStats": "Statistiques",
|
||||
"TabDescription": "Description"
|
||||
},
|
||||
"CreatureBehaviorType": {
|
||||
"herbivore": "Herbivore",
|
||||
"carnivore": "Carnivore",
|
||||
"charognard": "Charognard",
|
||||
"omnivore": "Omnivore"
|
||||
},
|
||||
"CreatureBehaviorSubType": {
|
||||
"accumulateur": "Accumulateur",
|
||||
"brouteur": "Brouteur",
|
||||
"filtreur": "Filtreur",
|
||||
"intermittent": "Intermittent",
|
||||
"chasseur": "Chasseur",
|
||||
"detourneux": "Détourneux",
|
||||
"guetteur": "Guetteur",
|
||||
"mangeur": "Mangeur",
|
||||
"piegeur": "Piégeur",
|
||||
"intimidateur": "Intimidateur",
|
||||
"necrophage": "Nécrophage",
|
||||
"reducteur": "Réducteur",
|
||||
"opportuniste": "Opportuniste"
|
||||
},
|
||||
"Healing": {
|
||||
"Title": "Soins",
|
||||
"FirstAid": "Premiers soins",
|
||||
"Surgery": "Chirurgie",
|
||||
"MedicalCare": "Soins médicaux",
|
||||
"NaturalHealing": "Guérison naturelle",
|
||||
"WillRestore": "Restaurera",
|
||||
"SurgeryFailed": "Chirurgie échouée - patient subit des dégâts",
|
||||
"ApplyHealing": "Appliquer les soins",
|
||||
"ApplySurgeryDamage": "Appliquer les dégâts chirurgicaux",
|
||||
"SurgeryDamage": "dégâts chirurgicaux",
|
||||
"ApplyToTarget": "Appliquer les soins à la cible sélectionnée",
|
||||
"NoMedicineSkill": "Pas de compétence Médecine",
|
||||
"Heals": "soigne"
|
||||
},
|
||||
"Notifications": {
|
||||
"HealingApplied": "{name} a été soigné(e) de {amount} points.",
|
||||
"DamageApplied": "{name} a subi {amount} dégâts.",
|
||||
"DamageAppliedAP": "{name} a subi {amount} dégâts (armure réduite de {ap} par AP).",
|
||||
"BulkyPenalty": "Arme Encombrante : FOR insuffisante (MD FOR trop faible). Pénalité {penalty} appliquée au jet.",
|
||||
"VeryBulkyPenalty": "Arme Très Encombrante : FOR insuffisante (MD FOR trop faible). Pénalité {penalty} appliquée au jet.",
|
||||
"StunDamageApplied": "{name} a subi {amount} dégâts incapacitants (END seulement).",
|
||||
"StunIncapacitated": "{name} est neutralisé(e) pour {rounds} round(s) ! (END réduite à 0)",
|
||||
"AmmoUsed": "{weapon} : {used} munition(s) consommée(s). Chargeur restant : {remaining}.",
|
||||
"AmmoEmpty": "{weapon} : chargeur vide après ce tir !",
|
||||
"NoAmmo": "{weapon} : chargeur vide ! Rechargez avant de tirer."
|
||||
},
|
||||
"Errors": {
|
||||
"NoTokenSelected": "Aucune cible active. Ciblez un token sur la scène avant d'appliquer.",
|
||||
"InvalidRollFormula": "Formule de jet invalide."
|
||||
}
|
||||
}
|
||||
},
|
||||
"TYPES.Actor.creature": "Créature"
|
||||
}
|
||||
6559
mgt2.bundle.js
6559
mgt2.bundle.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
11
package.json
Normal file
11
package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "foundryvtt-mgt2",
|
||||
"description": "Mongoose Traveller 2nd Edition for FoundryVTT",
|
||||
"scripts": {
|
||||
"build": "rollup -c rollup.config.mjs",
|
||||
"watch": "rollup -c rollup.config.mjs --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rollup": "^4"
|
||||
}
|
||||
}
|
||||
8
rollup.config.mjs
Normal file
8
rollup.config.mjs
Normal file
@@ -0,0 +1,8 @@
|
||||
export default {
|
||||
input: 'src/module/core.js',
|
||||
output: {
|
||||
file: 'mgt2.bundle.js',
|
||||
format: 'es',
|
||||
sourcemap: true,
|
||||
},
|
||||
};
|
||||
115
src/module/actors/actor.js
Normal file
115
src/module/actors/actor.js
Normal file
@@ -0,0 +1,115 @@
|
||||
import { ActorCharacter } from "./character.js";
|
||||
|
||||
export class MGT2Combatant extends Combatant {
|
||||
|
||||
}
|
||||
|
||||
export class TravellerActor extends Actor {
|
||||
|
||||
|
||||
prepareDerivedData() {
|
||||
if (this.type === "character") {
|
||||
this.system.initiative = ActorCharacter.getInitiative(this);
|
||||
}
|
||||
}
|
||||
|
||||
async _preCreate(data, options, user) {
|
||||
if ( (await super._preCreate(data, options, user)) === false ) return false;
|
||||
|
||||
if (this.type === "character") {
|
||||
ActorCharacter.preCreate(this, data, options, user);
|
||||
}
|
||||
}
|
||||
|
||||
async _onDeleteDescendantDocuments(parent, collection, documents, ids, options, userId) {
|
||||
await super._onDeleteDescendantDocuments(parent, collection, documents, ids, options, userId);
|
||||
|
||||
if (this.type === "character") {
|
||||
await ActorCharacter.onDeleteDescendantDocuments(this, parent, collection, documents, ids, options, userId);
|
||||
}
|
||||
}
|
||||
|
||||
async _onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId) {
|
||||
super._onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId);
|
||||
//console.log("_onUpdateDescendantDocuments");
|
||||
|
||||
if (this.type === "character") {
|
||||
await ActorCharacter.onUpdateDescendantDocuments(this, parent, collection, documents, changes, options, userId);
|
||||
}
|
||||
}
|
||||
|
||||
async _preUpdate(changed, options, user) {
|
||||
if ((await super._preUpdate(changed, options, user)) === false) return false;
|
||||
|
||||
if (this.type === "character") {
|
||||
await ActorCharacter.preUpdate(this, changed, options, user);
|
||||
}
|
||||
}
|
||||
|
||||
getInitiative($this) {
|
||||
if (this.type === "character") {
|
||||
return ActorCharacter.getInitiative(this);
|
||||
}
|
||||
}
|
||||
|
||||
applyDamage(amount, { ignoreArmor = false, ap = 0, stun = false } = {}) {
|
||||
if (this.type === "character") {
|
||||
return ActorCharacter.applyDamage(this, amount, { ignoreArmor, ap, stun });
|
||||
} else if (this.type === "creature") {
|
||||
if (isNaN(amount) || amount === 0) return Promise.resolve({ incapRounds: 0 });
|
||||
if (amount < 0) amount = Math.abs(amount);
|
||||
const rawArmor = ignoreArmor ? 0 : (this.system.armor ?? 0);
|
||||
const armorValue = Math.max(0, rawArmor - ap);
|
||||
const effective = Math.max(0, amount - armorValue);
|
||||
if (effective === 0) return Promise.resolve({ incapRounds: 0 });
|
||||
const newValue = Math.max(0, (this.system.life.value ?? 0) - effective);
|
||||
return this.update({ "system.life.value": newValue }).then(() => ({ incapRounds: 0 }));
|
||||
}
|
||||
}
|
||||
|
||||
applyHealing(amount) {
|
||||
if (this.type === "character") {
|
||||
return ActorCharacter.applyHealing(this, amount);
|
||||
} else if (this.type === "creature") {
|
||||
if (isNaN(amount) || amount === 0) return;
|
||||
if (amount < 0) amount = Math.abs(amount);
|
||||
const maxValue = this.system.life.max ?? 0;
|
||||
const current = this.system.life.value ?? 0;
|
||||
const newValue = Math.min(current + amount, maxValue);
|
||||
if (newValue !== current) {
|
||||
return this.update({ "system.life.value": newValue });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getContainers() {
|
||||
if (this.type === "character") {
|
||||
return ActorCharacter.getContainers(this);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
getComputers() {
|
||||
if (this.type === "character") {
|
||||
return ActorCharacter.getComputers(this);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
getSkills() {
|
||||
if (this.type === "character") {
|
||||
return ActorCharacter.getSkills(this);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
async recalculateWeight() {
|
||||
if (this.type === "character") {
|
||||
return ActorCharacter.recalculateWeight(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
105
src/module/actors/character-prompts.js
Normal file
105
src/module/actors/character-prompts.js
Normal file
@@ -0,0 +1,105 @@
|
||||
const { DialogV2 } = foundry.applications.api;
|
||||
const { renderTemplate } = foundry.applications.handlebars;
|
||||
const { FormDataExtended } = foundry.applications.ux;
|
||||
|
||||
async function _dialogWithForm(title, templatePath, templateData) {
|
||||
const htmlContent = await renderTemplate(templatePath, templateData);
|
||||
const theme = game.settings.get("mgt2", "theme");
|
||||
return await DialogV2.wait({
|
||||
window: { title },
|
||||
content: htmlContent,
|
||||
rejectClose: false,
|
||||
buttons: [
|
||||
{
|
||||
action: "submit",
|
||||
label: game.i18n.localize("MGT2.Save"),
|
||||
icon: '<i class="fa-solid fa-floppy-disk"></i>',
|
||||
default: true,
|
||||
callback: (event, button, dialog) => {
|
||||
return new FormDataExtended(dialog.element.querySelector('form')).object;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
export class CharacterPrompts {
|
||||
|
||||
static async openConfig(system) {
|
||||
return _dialogWithForm(
|
||||
"Configuration",
|
||||
"systems/mgt2/templates/actors/actor-config-sheet.html",
|
||||
{ config: CONFIG.MGT2, system }
|
||||
);
|
||||
}
|
||||
|
||||
static async openCharacteristic(name, show, showMax, showAll = false) {
|
||||
return _dialogWithForm(
|
||||
"Configuration: " + name,
|
||||
"systems/mgt2/templates/actors/actor-config-characteristic-sheet.html",
|
||||
{ name, show, showMax, showAll }
|
||||
);
|
||||
}
|
||||
|
||||
static async openTraitEdit(data) {
|
||||
const title = data.name ?? game.i18n.localize("MGT2.Actor.EditTrait");
|
||||
return _dialogWithForm(
|
||||
title,
|
||||
"systems/mgt2/templates/actors/trait-sheet.html",
|
||||
{ config: CONFIG.MGT2, data }
|
||||
);
|
||||
}
|
||||
|
||||
static async openEditorFullView(title, html) {
|
||||
const safeTitle = title || game.i18n.localize("MGT2.Actor.Species") || "Species";
|
||||
const htmlContent = await renderTemplate("systems/mgt2/templates/editor-fullview.html", {
|
||||
config: CONFIG.MGT2,
|
||||
html: html ?? ""
|
||||
});
|
||||
const theme = game.settings.get("mgt2", "theme");
|
||||
await DialogV2.wait({
|
||||
window: { title: safeTitle },
|
||||
content: htmlContent,
|
||||
rejectClose: false,
|
||||
buttons: [
|
||||
{
|
||||
action: "close",
|
||||
label: game.i18n.localize("MGT2.Close") || "Fermer",
|
||||
default: true,
|
||||
callback: () => null
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
static async openHealingDays() {
|
||||
return await DialogV2.wait({
|
||||
window: {
|
||||
title: game.i18n.localize("MGT2.Healing.Title")
|
||||
},
|
||||
classes: ["mgt2-roll-dialog"],
|
||||
content: `
|
||||
<form>
|
||||
<div style="padding: 12px;">
|
||||
<div class="form-group">
|
||||
<label>${game.i18n.localize("MGT2.RollPrompt.Days") || "Jours"}</label>
|
||||
<input type="number" name="days" value="1" min="1" max="999" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
`,
|
||||
rejectClose: false,
|
||||
buttons: [
|
||||
{
|
||||
action: "submit",
|
||||
label: game.i18n.localize("MGT2.Save"),
|
||||
icon: '<i class="fa-solid fa-floppy-disk"></i>',
|
||||
default: true,
|
||||
callback: (event, button, dialog) => {
|
||||
return new FormDataExtended(dialog.element.querySelector('form')).object;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
1078
src/module/actors/character-sheet.js
Normal file
1078
src/module/actors/character-sheet.js
Normal file
File diff suppressed because it is too large
Load Diff
520
src/module/actors/character.js
Normal file
520
src/module/actors/character.js
Normal file
@@ -0,0 +1,520 @@
|
||||
export class ActorCharacter {
|
||||
static preCreate($this, data, options, user) {
|
||||
$this.updateSource({ prototypeToken: { actorLink: true } }) // QoL
|
||||
}
|
||||
|
||||
static prepareData(actorData) {
|
||||
actorData.initiative = this.getInitiative(actorData);
|
||||
}
|
||||
|
||||
static getInitiative($this) {
|
||||
let c = $this.system.config.initiative;
|
||||
return $this.system.characteristics[c].dm;
|
||||
}
|
||||
|
||||
static async onDeleteDescendantDocuments($this, parent, collection, documents, ids, options, userId) {
|
||||
const toDeleteIds = [];
|
||||
const itemToUpdates = [];
|
||||
|
||||
for (let d of documents) {
|
||||
if (d.type === "container") {
|
||||
// Delete content
|
||||
for (let item of $this.items) {
|
||||
if (item.system.hasOwnProperty("container") && item.system.container.id === d._id)
|
||||
toDeleteIds.push(item._id);
|
||||
}
|
||||
} else if (d.type === "computer") {
|
||||
// Eject software
|
||||
for (let item of $this.items) {
|
||||
if (item.system.hasOwnProperty("software") && item.system.computerId === d._id) {
|
||||
let clone = foundry.utils.deepClone(item);
|
||||
clone.system.software.computerId = "";
|
||||
itemToUpdates.push(clone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toDeleteIds.length > 0)
|
||||
await $this.deleteEmbeddedDocuments("Item", toDeleteIds);
|
||||
|
||||
if (itemToUpdates.length > 0)
|
||||
await $this.updateEmbeddedDocuments('Item', itemToUpdates);
|
||||
|
||||
await this.recalculateWeight($this);
|
||||
}
|
||||
|
||||
static async onUpdateDescendantDocuments($this, parent, collection, documents, changes, options, userId) {
|
||||
await this.calculEncumbranceAndWeight($this, parent, collection, documents, changes, options, userId);
|
||||
await this.calculComputers($this, parent, collection, documents, changes, options, userId);
|
||||
}
|
||||
|
||||
static async calculComputers($this, parent, collection, documents, changes, options, userId) {
|
||||
let change;
|
||||
let i = 0;
|
||||
|
||||
let recalculProcessing = false;
|
||||
for (let d of documents) {
|
||||
if (changes[i].hasOwnProperty("system")) {
|
||||
change = changes[i];
|
||||
if (d.type === "item" && d.system.subType === "software") {
|
||||
if (change.system.software.hasOwnProperty("bandwidth") || change.system.software.hasOwnProperty("computerId")) {
|
||||
recalculProcessing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recalculProcessing) {
|
||||
let updatedComputers = [];
|
||||
let computerChanges = {};
|
||||
let computers = [];
|
||||
|
||||
for (let item of $this.items) {
|
||||
if (item.system.trash === true) continue;
|
||||
if (item.type === "computer") {
|
||||
computers.push(item);
|
||||
computerChanges[item._id] = { processingUsed: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
for (let item of $this.items) {
|
||||
if (item.type !== "item" && item.system.subType !== "software") continue;
|
||||
|
||||
if (item.system.software.hasOwnProperty("computerId") && item.system.software.computerId !== "") {
|
||||
computerChanges[item.system.software.computerId].processingUsed += item.system.software.bandwidth;
|
||||
}
|
||||
}
|
||||
|
||||
for (let computer of computers) {
|
||||
let newProcessingUsed = computerChanges[computer._id].processingUsed;
|
||||
if (computer.system.processingUsed !== newProcessingUsed) {
|
||||
const cloneComputer = foundry.utils.deepClone($this.getEmbeddedDocument("Item", computer._id));
|
||||
cloneComputer.system.processingUsed = newProcessingUsed;
|
||||
cloneComputer.system.overload = cloneComputer.system.processingUsed > cloneComputer.system.processing;
|
||||
updatedComputers.push(cloneComputer);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedComputers.length > 0) {
|
||||
await $this.updateEmbeddedDocuments('Item', updatedComputers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async calculEncumbranceAndWeight($this, parent, collection, documents, changes, options, userId) {
|
||||
let recalculEncumbrance = false;
|
||||
let recalculWeight = false;
|
||||
|
||||
let change;
|
||||
let i = 0;
|
||||
for (let d of documents) {
|
||||
if (changes[i].hasOwnProperty("system")) {
|
||||
change = changes[i];
|
||||
|
||||
if (d.type === "armor" ||
|
||||
d.type === "computer" ||
|
||||
d.type === "gear" ||
|
||||
d.type === "item" ||
|
||||
d.type === "weapon") {
|
||||
if (change.system.hasOwnProperty("quantity") ||
|
||||
change.system.hasOwnProperty("weight") ||
|
||||
change.system.hasOwnProperty("weightless") ||
|
||||
change.system.hasOwnProperty("container") ||
|
||||
change.system.hasOwnProperty("equipped") ||
|
||||
d.type === "armor") {
|
||||
recalculWeight = true;
|
||||
}
|
||||
} else if (d.type === "talent" && d.system.subType === "skill") {
|
||||
if (change.system.level || (change.system?.hasOwnProperty("skill") && change.system?.skill.hasOwnProperty("reduceEncumbrance"))) {
|
||||
recalculEncumbrance = true;
|
||||
}
|
||||
} else if (d.type === "container" && (change.system.hasOwnProperty("onHand") || change.system.hasOwnProperty("weightless"))) {
|
||||
recalculWeight = true;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (recalculEncumbrance || recalculWeight) {
|
||||
const updateData = {};
|
||||
|
||||
this.recalculateArmor($this, updateData);
|
||||
|
||||
if (recalculEncumbrance) {
|
||||
const str = $this.system.characteristics.strength.value;
|
||||
const end = $this.system.characteristics.endurance.value;
|
||||
let sumSkill = 0;
|
||||
$this.items.filter(x => x.type === "talent" && x.system.subType === "skill" && x.system.skill.reduceEncumbrance === true).forEach(x => sumSkill += x.system.level);
|
||||
let normal = str + end + sumSkill;
|
||||
let heavy = normal * 2;
|
||||
|
||||
updateData["system.states.encumbrance"] = $this.system.inventory.weight > normal;
|
||||
updateData["system.inventory.encumbrance.normal"] = normal;
|
||||
updateData["system.inventory.encumbrance.heavy"] = heavy;
|
||||
}
|
||||
|
||||
if (recalculWeight)
|
||||
await this.recalculateWeight($this, updateData);
|
||||
else if (Object.keys(updateData).length > 0)
|
||||
await $this.update(updateData);
|
||||
}
|
||||
}
|
||||
|
||||
static recalculateArmor($this, updateData) {
|
||||
if (updateData === null || updateData === undefined)
|
||||
updateData = {};
|
||||
|
||||
let armor = 0;
|
||||
for (let item of $this.items) {
|
||||
if (item.type === "armor") {
|
||||
if (item.system.equipped === true && !isNaN(item.system.protection)) {
|
||||
armor += (+item.system.protection || 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateData["system.inventory.armor"] = armor;
|
||||
return updateData;
|
||||
}
|
||||
|
||||
static async recalculateWeight($this, updateData) {
|
||||
|
||||
if (updateData === null || updateData === undefined)
|
||||
updateData = {};
|
||||
|
||||
let updatedContainers = [];
|
||||
let containerChanges = {};
|
||||
|
||||
let containers = [];
|
||||
|
||||
// List all containers
|
||||
for (let item of $this.items) {
|
||||
if (item.system.trash === true) continue;
|
||||
|
||||
if (item.type === "container") {
|
||||
containers.push(item);
|
||||
containerChanges[item._id] = { count: 0, weight: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
let onHandWeight = 0;
|
||||
for (let item of $this.items) {
|
||||
if (item.type === "container") continue;
|
||||
if (item.system.hasOwnProperty("weightless") && item.system.weightless === true) continue;
|
||||
|
||||
let itemWeight = 0;
|
||||
if (item.system.hasOwnProperty("weight")) {
|
||||
let itemQty = item.system.quantity;
|
||||
if (!isNaN(itemQty) && itemQty > 0) {
|
||||
itemWeight = item.system.weight;
|
||||
if (itemWeight > 0) {
|
||||
itemWeight *= itemQty;
|
||||
}
|
||||
}
|
||||
|
||||
if (item.type === "armor") {
|
||||
if (item.system.equipped === true) {
|
||||
if (item.system.powered === true)
|
||||
itemWeight = 0;
|
||||
else
|
||||
itemWeight *= 0.25; // mass of armor that is being worn by 75% OPTIONAL
|
||||
}
|
||||
}
|
||||
|
||||
if (item.system.container && item.system.container.id && item.system.container.id !== "") {
|
||||
// bad deleted container id
|
||||
if (containerChanges.hasOwnProperty(item.system.container.id)) {
|
||||
containerChanges[item.system.container.id].weight += Math.round(itemWeight * 10) / 10;
|
||||
containerChanges[item.system.container.id].count += item.system.quantity;
|
||||
}
|
||||
} else {
|
||||
onHandWeight += Math.round(itemWeight * 10) / 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check containers new weight
|
||||
for (let container of containers) {
|
||||
let newWeight = containerChanges[container._id].weight;
|
||||
let newCount = containerChanges[container._id].count;
|
||||
if (container.system.weight !== newWeight || container.system.count !== newCount) {
|
||||
updatedContainers.push({
|
||||
_id: container._id,
|
||||
"system.weight": newWeight,
|
||||
"system.count": newCount,
|
||||
});
|
||||
|
||||
if (container.system.onHand === true &&
|
||||
(container.system.weight > 0 || container.system.weightless !== true)) {
|
||||
onHandWeight += container.system.weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateData["system.inventory.weight"] = onHandWeight;
|
||||
// Use the threshold from updateData if it was just recalculated (e.g. STR/END talent change),
|
||||
// otherwise fall back to the persisted value.
|
||||
const encumbranceThreshold = updateData["system.inventory.encumbrance.normal"] ?? $this.system.inventory.encumbrance.normal;
|
||||
updateData["system.states.encumbrance"] = onHandWeight > encumbranceThreshold;
|
||||
|
||||
await $this.update(updateData);
|
||||
|
||||
if (updatedContainers.length > 0) {
|
||||
await $this.updateEmbeddedDocuments('Item', updatedContainers);
|
||||
}
|
||||
}
|
||||
|
||||
static async preUpdate($this, changed, options, user) {
|
||||
// Calc encumbrance
|
||||
|
||||
const newStr = foundry.utils.getProperty(changed, "system.characteristics.strength.value") ?? $this.system.characteristics.strength.value;
|
||||
const newEnd = foundry.utils.getProperty(changed, "system.characteristics.endurance.value") ?? $this.system.characteristics.endurance.value;
|
||||
if ((newStr !== $this.system.characteristics.strength.value) || (newEnd !== $this.system.characteristics.endurance.value)) {
|
||||
let sumSkill = 0;
|
||||
$this.items.filter(x => x.type === "talent" && x.system.subType === "skill" && x.system.skill.reduceEncumbrance === true).forEach(x => sumSkill += x.system.level);
|
||||
let normal = newStr + newEnd + sumSkill;
|
||||
let heavy = normal * 2;
|
||||
foundry.utils.setProperty(changed, "system.inventory.encumbrance.normal", normal);
|
||||
foundry.utils.setProperty(changed, "system.inventory.encumbrance.heavy", heavy);
|
||||
// Also update the encumbrance state flag against the new threshold
|
||||
foundry.utils.setProperty(changed, "system.states.encumbrance", $this.system.inventory.weight > normal);
|
||||
}
|
||||
|
||||
//console.log(foundry.utils.getProperty(changed, "system.characteristics.strength.value"));
|
||||
const characteristicModified = this.computeCharacteristics(changed);
|
||||
const strengthValue = foundry.utils.getProperty(changed, "system.characteristics.strength.value") ?? $this.system.characteristics.strength.value;
|
||||
const strengthMax = foundry.utils.getProperty(changed, "system.characteristics.strength.max") ?? $this.system.characteristics.strength.max;
|
||||
const dexterityValue = foundry.utils.getProperty(changed, "system.characteristics.dexterity.value") ?? $this.system.characteristics.dexterity.value;
|
||||
const dexterityMax = foundry.utils.getProperty(changed, "system.characteristics.dexterity.max") ?? $this.system.characteristics.dexterity.max;
|
||||
const enduranceValue = foundry.utils.getProperty(changed, "system.characteristics.endurance.value") ?? $this.system.characteristics.endurance.value;
|
||||
const enduranceMax = foundry.utils.getProperty(changed, "system.characteristics.endurance.max") ?? $this.system.characteristics.endurance.max;
|
||||
const lifeValue = strengthValue + dexterityValue + enduranceValue;
|
||||
const lifeMax = strengthMax + dexterityMax + enduranceMax;
|
||||
|
||||
if ($this.system.life.value !== lifeValue)
|
||||
foundry.utils.setProperty(changed, "system.life.value", lifeValue);
|
||||
if ($this.system.life.max !== lifeMax)
|
||||
foundry.utils.setProperty(changed, "system.life.max", lifeMax);
|
||||
|
||||
if (characteristicModified && $this.system.personal.ucp === undefined || $this.system.personal.ucp === "") {
|
||||
// calc
|
||||
|
||||
}
|
||||
//}
|
||||
|
||||
// Apply changes in Actor size to Token width/height
|
||||
// if ( "size" in (this.system.traits || {}) ) {
|
||||
// const newSize = foundry.utils.getProperty(changed, "system.traits.size");
|
||||
// if ( newSize && (newSize !== this.system.traits?.size) ) {
|
||||
// let size = CONFIG.DND5E.tokenSizes[newSize];
|
||||
// if ( !foundry.utils.hasProperty(changed, "prototypeToken.width") ) {
|
||||
// changed.prototypeToken ||= {};
|
||||
// changed.prototypeToken.height = size;
|
||||
// changed.prototypeToken.width = size;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// static applyHealing($this, amount) {
|
||||
// if (isNaN(amount) || amount === 0) return;
|
||||
|
||||
// const strength = $this.system.characteristics.strength;
|
||||
// const dexterity = $this.system.characteristics.dexterity;
|
||||
// const endurance = $this.system.characteristics.endurance;
|
||||
|
||||
// const data = {
|
||||
// strength: { value: strength.value },
|
||||
// dexterity: { value: dexterity.value },
|
||||
// endurance: { value: endurance.value }
|
||||
// };
|
||||
|
||||
|
||||
|
||||
// $this.update({ system: { characteristics: data } });
|
||||
// }
|
||||
|
||||
static async applyDamage($this, amount, { ignoreArmor = false, ap = 0, stun = false } = {}) {
|
||||
if (isNaN(amount) || amount === 0) return { incapRounds: 0 };
|
||||
const rank1 = $this.system.config.damages.rank1;
|
||||
const rank2 = $this.system.config.damages.rank2;
|
||||
const rank3 = $this.system.config.damages.rank3;
|
||||
|
||||
if (amount < 0) amount = Math.abs(amount);
|
||||
|
||||
if (!ignoreArmor) {
|
||||
const rawArmor = $this.system.inventory?.armor ?? 0;
|
||||
const armorValue = Math.max(0, rawArmor - ap);
|
||||
amount = Math.max(0, amount - armorValue);
|
||||
if (amount === 0) return { incapRounds: 0 };
|
||||
}
|
||||
|
||||
// ── Stun / Incapacitating: only deduct from endurance (rank3) ─────────
|
||||
if (stun) {
|
||||
const endKey = rank3; // "endurance" by default
|
||||
const prevEnd = $this.system.characteristics[endKey].value;
|
||||
const newEnd = Math.max(0, prevEnd - amount);
|
||||
const incapRounds = newEnd === 0 ? Math.max(0, amount - prevEnd) : 0;
|
||||
await $this.update({
|
||||
system: { characteristics: { [endKey]: { value: newEnd, dm: this.getModifier(newEnd) } } }
|
||||
});
|
||||
return { incapRounds };
|
||||
}
|
||||
|
||||
// ── Normal damage cascade: rank1 → rank2 → rank3 ────────────────────
|
||||
const data = {};
|
||||
data[rank1] = { value: $this.system.characteristics[rank1].value };
|
||||
data[rank2] = { value: $this.system.characteristics[rank2].value };
|
||||
data[rank3] = { value: $this.system.characteristics[rank3].value };
|
||||
|
||||
for (const [key, rank] of Object.entries(data)) {
|
||||
if (rank.value > 0) {
|
||||
if (rank.value >= amount) {
|
||||
rank.value -= amount;
|
||||
amount = 0;
|
||||
} else {
|
||||
amount -= rank.value;
|
||||
rank.value = 0;
|
||||
}
|
||||
rank.dm = this.getModifier(rank.value);
|
||||
if (amount <= 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
await $this.update({ system: { characteristics: data } });
|
||||
return { incapRounds: 0 };
|
||||
}
|
||||
|
||||
static applyHealing($this, amount, type) {
|
||||
if (isNaN(amount) || amount === 0) return;
|
||||
|
||||
const rank1 = $this.system.config.damages.rank1;
|
||||
const rank2 = $this.system.config.damages.rank2;
|
||||
const rank3 = $this.system.config.damages.rank3;
|
||||
|
||||
// Data to restore (reverse cascade: END → DEX → STR)
|
||||
const data = {};
|
||||
const rankOrder = [rank3, rank2, rank1]; // Reverse order for healing
|
||||
const maxValues = {
|
||||
[rank1]: $this.system.characteristics[rank1].max,
|
||||
[rank2]: $this.system.characteristics[rank2].max,
|
||||
[rank3]: $this.system.characteristics[rank3].max
|
||||
};
|
||||
|
||||
if (amount < 0) amount = Math.abs(amount);
|
||||
|
||||
// Distribute healing from lowest rank first (END → DEX → STR typically)
|
||||
for (const rank of rankOrder) {
|
||||
const current = $this.system.characteristics[rank].value;
|
||||
const max = maxValues[rank];
|
||||
|
||||
if (current < max && amount > 0) {
|
||||
const canRestore = max - current;
|
||||
const restore = Math.min(amount, canRestore);
|
||||
|
||||
if (!data[rank]) {
|
||||
data[rank] = { value: current };
|
||||
}
|
||||
data[rank].value += restore;
|
||||
data[rank].dm = this.getModifier(data[rank].value);
|
||||
amount -= restore;
|
||||
}
|
||||
}
|
||||
|
||||
// Only update if something was restored
|
||||
if (Object.keys(data).length > 0) {
|
||||
return $this.update({ system: { characteristics: data } });
|
||||
}
|
||||
}
|
||||
|
||||
static getContainers($this) {
|
||||
const containers = [];
|
||||
for (let item of $this.items) {
|
||||
if (item.type == "container") {
|
||||
containers.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
containers.sort(this.compareByName);
|
||||
|
||||
return containers;
|
||||
}
|
||||
|
||||
static getComputers($this) {
|
||||
const containers = [];
|
||||
for (let item of $this.items) {
|
||||
if (item.type == "computer") {
|
||||
containers.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
containers.sort(this.compareByName);
|
||||
|
||||
return containers;
|
||||
}
|
||||
|
||||
static getSkills($this) {
|
||||
const skills = [];
|
||||
for (let item of $this.items) {
|
||||
if (item.type === "talent" && item.system.subType === "skill") {
|
||||
skills.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
skills.sort(this.compareByName);
|
||||
|
||||
return skills;
|
||||
}
|
||||
|
||||
static computeCharacteristics(changed) {
|
||||
let modified = this.computeCharacteristic(changed, "strength");
|
||||
|
||||
if (this.computeCharacteristic(changed, "dexterity") && !modified) modified = true;
|
||||
if (this.computeCharacteristic(changed, "endurance") && !modified) modified = true;
|
||||
if (this.computeCharacteristic(changed, "intellect") && !modified) modified = true;
|
||||
if (this.computeCharacteristic(changed, "education") && !modified) modified = true;
|
||||
if (this.computeCharacteristic(changed, "social") && !modified) modified = true;
|
||||
if (this.computeCharacteristic(changed, "morale") && !modified) modified = true;
|
||||
if (this.computeCharacteristic(changed, "luck") && !modified) modified = true;
|
||||
if (this.computeCharacteristic(changed, "sanity") && !modified) modified = true;
|
||||
if (this.computeCharacteristic(changed, "charm") && !modified) modified = true;
|
||||
if (this.computeCharacteristic(changed, "psionic") && !modified) modified = true;
|
||||
if (this.computeCharacteristic(changed, "other") && !modified) modified = true;
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
static computeCharacteristic(changed, name) {
|
||||
//if (isNaN(c.value) || c.value <= 0) c.value = 0;
|
||||
//c.dm = this._getModifier(c.value)
|
||||
const path = `system.characteristics.${name}`;
|
||||
const newValue = foundry.utils.getProperty(changed, path + ".value");// || this.system.characteristics[name].value;
|
||||
if (newValue) {
|
||||
const dm = this.getModifier(newValue);
|
||||
foundry.utils.setProperty(changed, path + ".dm", dm);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static getModifier(value) {
|
||||
if (isNaN(value) || value <= 0) return -3;
|
||||
if (value >= 1 && value <= 2) return -2;
|
||||
if (value >= 3 && value <= 5) return -1;
|
||||
if (value >= 6 && value <= 8) return 0;
|
||||
if (value >= 9 && value <= 11) return 1;
|
||||
if (value >= 12 && value <= 14) return 2;
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
static compareByName(a, b) {
|
||||
if (!a.hasOwnProperty("name") || !b.hasOwnProperty("name")) {
|
||||
return 0;
|
||||
}
|
||||
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||
}
|
||||
}
|
||||
18
src/module/actors/vehicule-sheet.js
Normal file
18
src/module/actors/vehicule-sheet.js
Normal file
@@ -0,0 +1,18 @@
|
||||
export class VehiculeActorSheet extends ActorSheet {
|
||||
static get defaultOptions() {
|
||||
const options = super.defaultOptions;
|
||||
|
||||
//if (game.user.isGM || options.editable)
|
||||
// options.dragDrop.push({ dragSelector: ".drag-item-list", dropSelector: ".drop-item-list" });
|
||||
|
||||
return foundry.utils.mergeObject(options, {
|
||||
classes: ["mgt2", game.settings.get("mgt2", "theme"), "actor", "vehicule", "nopad"],
|
||||
template: "systems/mgt2/templates/actors/vehicule-sheet.html",
|
||||
width: 780,
|
||||
//height: 600,
|
||||
tabs: [
|
||||
{ navSelector: ".sheet-sidebar", contentSelector: "form" }
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
5
src/module/applications/sheets/_module.mjs
Normal file
5
src/module/applications/sheets/_module.mjs
Normal file
@@ -0,0 +1,5 @@
|
||||
export { default as MGT2ActorSheet } from "./base-actor-sheet.mjs";
|
||||
export { default as TravellerCharacterSheet } from "./character-sheet.mjs";
|
||||
export { default as TravellerVehiculeSheet } from "./vehicule-sheet.mjs";
|
||||
export { default as TravellerCreatureSheet } from "./creature-sheet.mjs";
|
||||
export { default as TravellerItemSheet } from "./item-sheet.mjs";
|
||||
107
src/module/applications/sheets/base-actor-sheet.mjs
Normal file
107
src/module/applications/sheets/base-actor-sheet.mjs
Normal file
@@ -0,0 +1,107 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class MGT2ActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
||||
|
||||
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
this._sheetMode = this.constructor.SHEET_MODES.PLAY;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["mgt2", "sheet", "actor"],
|
||||
position: {
|
||||
width: 780,
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
dragDrop: [{ dragSelector: ".drag-item-list", dropSelector: ".drop-item-list" }],
|
||||
actions: {
|
||||
toggleSheet: MGT2ActorSheet.#onToggleSheet,
|
||||
},
|
||||
}
|
||||
|
||||
get isPlayMode() {
|
||||
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY;
|
||||
return this._sheetMode === this.constructor.SHEET_MODES.PLAY;
|
||||
}
|
||||
|
||||
get isEditMode() {
|
||||
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY;
|
||||
return this._sheetMode === this.constructor.SHEET_MODES.EDIT;
|
||||
}
|
||||
|
||||
tabGroups = { sidebar: "health" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const base = await super._prepareContext();
|
||||
const actor = this.document;
|
||||
return {
|
||||
...base,
|
||||
actor: actor,
|
||||
// Flat shorthands for template backward-compat (AppV1 style)
|
||||
name: actor.name,
|
||||
img: actor.img,
|
||||
cssClass: this.isEditable ? "editable" : "locked",
|
||||
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: CONFIG.MGT2,
|
||||
};
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options);
|
||||
// Inject theme class dynamically (can't use game.settings in static DEFAULT_OPTIONS)
|
||||
const theme = game.settings.get("mgt2", "theme");
|
||||
if (theme) this.element.classList.add(theme);
|
||||
this._activateTabGroups();
|
||||
}
|
||||
|
||||
_activateTabGroups() {
|
||||
for (const [group, activeTab] of Object.entries(this.tabGroups)) {
|
||||
const nav = this.element.querySelector(`nav[data-group="${group}"]`);
|
||||
if (!nav) continue;
|
||||
|
||||
nav.querySelectorAll('[data-tab]').forEach(link => {
|
||||
link.classList.toggle('active', link.dataset.tab === activeTab);
|
||||
link.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
this.tabGroups[group] = link.dataset.tab;
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
|
||||
this.element.querySelectorAll(`[data-group="${group}"][data-tab]`).forEach(content => {
|
||||
content.classList.toggle('active', content.dataset.tab === activeTab);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_canDragDrop(selector) {
|
||||
return this.isEditable;
|
||||
}
|
||||
|
||||
static async #onToggleSheet(event) {
|
||||
event.preventDefault();
|
||||
this._sheetMode = this.isPlayMode
|
||||
? this.constructor.SHEET_MODES.EDIT
|
||||
: this.constructor.SHEET_MODES.PLAY;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
1372
src/module/applications/sheets/character-sheet.mjs
Normal file
1372
src/module/applications/sheets/character-sheet.mjs
Normal file
File diff suppressed because it is too large
Load Diff
295
src/module/applications/sheets/creature-sheet.mjs
Normal file
295
src/module/applications/sheets/creature-sheet.mjs
Normal file
@@ -0,0 +1,295 @@
|
||||
import MGT2ActorSheet from "./base-actor-sheet.mjs";
|
||||
import { RollPromptHelper } from "../../roll-prompt.js";
|
||||
import { MGT2Helper } from "../../helper.js";
|
||||
|
||||
const { renderTemplate } = foundry.applications.handlebars;
|
||||
|
||||
/** Convert Traveller dice notation (e.g. "2D", "4D+2", "3D6") to FoundryVTT formula */
|
||||
function normalizeDice(formula) {
|
||||
if (!formula) return "1d6";
|
||||
return formula
|
||||
.replace(/(\d*)D(\d*)([+-]\d+)?/gi, (_, count, sides, mod) => {
|
||||
const n = count || "1";
|
||||
const d = sides || "6";
|
||||
return mod ? `${n}d${d}${mod}` : `${n}d${d}`;
|
||||
});
|
||||
}
|
||||
|
||||
export default class TravellerCreatureSheet extends MGT2ActorSheet {
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "creature", "nopad"],
|
||||
position: {
|
||||
width: 720,
|
||||
height: 600,
|
||||
},
|
||||
window: {
|
||||
...super.DEFAULT_OPTIONS.window,
|
||||
title: "TYPES.Actor.creature",
|
||||
},
|
||||
actions: {
|
||||
...super.DEFAULT_OPTIONS.actions,
|
||||
rollAttack: TravellerCreatureSheet.#onRollAttack,
|
||||
rollSkill: TravellerCreatureSheet.#onRollSkill,
|
||||
addSkill: TravellerCreatureSheet.#onAddRow,
|
||||
deleteSkill: TravellerCreatureSheet.#onDeleteRow,
|
||||
addAttack: TravellerCreatureSheet.#onAddRow,
|
||||
deleteAttack: TravellerCreatureSheet.#onDeleteRow,
|
||||
addTrait: TravellerCreatureSheet.#onAddRow,
|
||||
deleteTrait: TravellerCreatureSheet.#onDeleteRow,
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
sheet: {
|
||||
template: "systems/mgt2/templates/actors/creature-sheet.html",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = { primary: "combat" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext();
|
||||
const actor = this.document;
|
||||
const enrich = (html) => foundry.applications.ux.TextEditor.implementation.enrichHTML(html ?? "", { async: true });
|
||||
|
||||
context.enrichedBiography = await enrich(actor.system.biography);
|
||||
context.enrichedNotes = await enrich(actor.system.notes);
|
||||
context.sizeLabel = this._getSizeLabel(actor.system.life.max);
|
||||
context.sizeTraitLabel = this._getSizeTrait(actor.system.life.max);
|
||||
context.config = CONFIG.MGT2;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
_getSizeLabel(pdv) {
|
||||
if (pdv <= 2) return "Souris / Rat";
|
||||
if (pdv <= 5) return "Chat";
|
||||
if (pdv <= 7) return "Blaireau / Chien";
|
||||
if (pdv <= 13) return "Chimpanzé / Chèvre";
|
||||
if (pdv <= 28) return "Humain";
|
||||
if (pdv <= 35) return "Vache / Cheval";
|
||||
if (pdv <= 49) return "Requin";
|
||||
if (pdv <= 70) return "Rhinocéros";
|
||||
if (pdv <= 90) return "Éléphant";
|
||||
if (pdv <= 125) return "Carnosaure";
|
||||
return "Sauropode / Baleine";
|
||||
}
|
||||
|
||||
_getSizeTrait(pdv) {
|
||||
if (pdv <= 2) return "Petit (−4)";
|
||||
if (pdv <= 5) return "Petit (−3)";
|
||||
if (pdv <= 7) return "Petit (−2)";
|
||||
if (pdv <= 13) return "Petit (−1)";
|
||||
if (pdv <= 28) return "—";
|
||||
if (pdv <= 35) return "Grand (+1)";
|
||||
if (pdv <= 49) return "Grand (+2)";
|
||||
if (pdv <= 70) return "Grand (+3)";
|
||||
if (pdv <= 90) return "Grand (+4)";
|
||||
if (pdv <= 125) return "Grand (+5)";
|
||||
return "Grand (+6)";
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────── Roll Helpers
|
||||
|
||||
static async #postCreatureRoll({ actor, roll, rollLabel, dm, difficulty, difficultyLabel, rollMode, extraTooltip, damageFormula }) {
|
||||
const diffTarget = MGT2Helper.getDifficultyValue(difficulty ?? "Average");
|
||||
const hasDifficulty = !!difficulty;
|
||||
const success = hasDifficulty ? roll.total >= diffTarget : true;
|
||||
const effect = roll.total - diffTarget;
|
||||
const effectStr = (effect >= 0 ? "+" : "") + effect;
|
||||
|
||||
const diceRawTotal = roll.dice.reduce((s, d) => s + d.total, 0);
|
||||
const breakdownParts = [game.i18n.localize("MGT2.Chat.Roll.Dice") + " " + diceRawTotal];
|
||||
if (dm !== 0) breakdownParts.push(`DM ${dm >= 0 ? "+" : ""}${dm}`);
|
||||
if (hasDifficulty) breakdownParts.push(game.i18n.localize("MGT2.Chat.Roll.Effect") + " " + effectStr);
|
||||
if (extraTooltip) breakdownParts.push(extraTooltip);
|
||||
const rollBreakdown = breakdownParts.join(" | ");
|
||||
|
||||
const showRollDamage = success && !!damageFormula;
|
||||
|
||||
const chatData = {
|
||||
creatureName: actor.name,
|
||||
creatureImg: actor.img,
|
||||
rollLabel,
|
||||
formula: roll.formula,
|
||||
total: roll.total,
|
||||
tooltip: await roll.getTooltip(),
|
||||
rollBreakdown,
|
||||
difficulty: hasDifficulty ? diffTarget : null,
|
||||
difficultyLabel: difficultyLabel ?? MGT2Helper.getDifficultyDisplay(difficulty),
|
||||
success: hasDifficulty ? success : null,
|
||||
failure: hasDifficulty ? !success : null,
|
||||
effect: hasDifficulty ? effect : null,
|
||||
effectStr: hasDifficulty ? effectStr : null,
|
||||
modifiers: dm !== 0 ? [`DM ${dm >= 0 ? "+" : ""}${dm}`] : [],
|
||||
showRollDamage,
|
||||
};
|
||||
|
||||
const chatContent = await renderTemplate(
|
||||
"systems/mgt2/templates/chat/creature-roll.html",
|
||||
chatData
|
||||
);
|
||||
|
||||
const flags = showRollDamage ? {
|
||||
mgt2: {
|
||||
damage: {
|
||||
formula: normalizeDice(damageFormula),
|
||||
effect,
|
||||
rollObjectName: actor.name,
|
||||
rollTypeName: rollLabel,
|
||||
}
|
||||
}
|
||||
} : {};
|
||||
|
||||
await ChatMessage.create({
|
||||
content: chatContent,
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
rolls: [roll],
|
||||
rollMode: rollMode ?? game.settings.get("core", "rollMode"),
|
||||
flags,
|
||||
});
|
||||
|
||||
return { success, effect, total: roll.total };
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────── Roll Handlers
|
||||
|
||||
/** Roll a skill check (2d6 + level vs difficulty) — uses unified dialog */
|
||||
static async #onRollSkill(event, target) {
|
||||
const index = parseInt(target.dataset.index ?? 0);
|
||||
const actor = this.document;
|
||||
const skill = actor.system.skills[index];
|
||||
if (!skill) return;
|
||||
|
||||
const result = await RollPromptHelper.roll({
|
||||
isCreature: true,
|
||||
showSkillSelector: false,
|
||||
skillName: skill.name,
|
||||
skillLevel: skill.level,
|
||||
difficulty: "Average",
|
||||
title: game.i18n.localize("MGT2.Creature.RollSkill") + " — " + skill.name,
|
||||
});
|
||||
if (!result) return;
|
||||
|
||||
const customDM = parseInt(result.customDM ?? "0", 10) || 0;
|
||||
const skillLevel = parseInt(skill.level ?? 0, 10) || 0;
|
||||
const dm = skillLevel + customDM;
|
||||
const diceModifier = result.diceModifier ?? "";
|
||||
|
||||
// Build formula exactly like character-sheet: parts joined without spaces
|
||||
const parts = [];
|
||||
if (diceModifier) {
|
||||
parts.push("3d6", diceModifier);
|
||||
} else {
|
||||
parts.push("2d6");
|
||||
}
|
||||
if (dm !== 0) parts.push(MGT2Helper.getFormulaDM(dm));
|
||||
const fullFormula = parts.join("");
|
||||
|
||||
const roll = await new Roll(fullFormula).evaluate();
|
||||
const rollLabel = `${skill.name.toUpperCase()} (${skillLevel >= 0 ? "+" : ""}${skillLevel})`;
|
||||
|
||||
const tooltipParts = [`Dés: ${roll.dice.reduce((s, d) => s + d.total, 0)}`];
|
||||
if (skillLevel !== 0) tooltipParts.push(`${skill.name} ${skillLevel >= 0 ? "+" : ""}${skillLevel}`);
|
||||
if (customDM !== 0) tooltipParts.push(`MD perso ${customDM >= 0 ? "+" : ""}${customDM}`);
|
||||
|
||||
await TravellerCreatureSheet.#postCreatureRoll({
|
||||
actor, roll, rollLabel,
|
||||
dm,
|
||||
difficulty: result.difficulty,
|
||||
rollMode: result.rollMode,
|
||||
extraTooltip: tooltipParts.join(" | "),
|
||||
});
|
||||
}
|
||||
|
||||
/** Roll an attack: dialog with skill selector, then roll 2d6+skill+DM vs difficulty; on success roll damage */
|
||||
static async #onRollAttack(event, target) {
|
||||
const index = parseInt(target.dataset.index ?? 0);
|
||||
const actor = this.document;
|
||||
const attack = actor.system.attacks[index];
|
||||
if (!attack) return;
|
||||
|
||||
const skills = actor.system.skills ?? [];
|
||||
|
||||
const result = await RollPromptHelper.roll({
|
||||
isCreature: true,
|
||||
showSkillSelector: true,
|
||||
creatureSkills: skills,
|
||||
selectedSkillIndex: attack.skill ?? -1,
|
||||
difficulty: "Average",
|
||||
title: game.i18n.localize("MGT2.Creature.RollAttack") + " — " + attack.name,
|
||||
});
|
||||
if (!result) return;
|
||||
|
||||
const skillIndex = parseInt(result.creatureSkillIndex ?? "-1", 10);
|
||||
const chosenSkill = (skillIndex >= 0 && skillIndex < skills.length) ? skills[skillIndex] : null;
|
||||
const skillLevel = parseInt(chosenSkill?.level ?? 0, 10) || 0;
|
||||
const customDM = parseInt(result.customDM ?? "0", 10) || 0;
|
||||
const dm = skillLevel + customDM;
|
||||
const diceModifier = result.diceModifier ?? "";
|
||||
|
||||
// Build formula exactly like character-sheet: parts joined without spaces
|
||||
const parts = [];
|
||||
if (diceModifier) {
|
||||
parts.push("3d6", diceModifier);
|
||||
} else {
|
||||
parts.push("2d6");
|
||||
}
|
||||
if (dm !== 0) parts.push(MGT2Helper.getFormulaDM(dm));
|
||||
const fullFormula = parts.join("");
|
||||
|
||||
const roll = await new Roll(fullFormula).evaluate();
|
||||
const rollLabel = chosenSkill
|
||||
? `${attack.name} — ${chosenSkill.name} (${skillLevel >= 0 ? "+" : ""}${skillLevel})`
|
||||
: attack.name;
|
||||
|
||||
const tooltipParts = [`Dés: ${roll.dice.reduce((s, d) => s + d.total, 0)}`];
|
||||
if (chosenSkill) tooltipParts.push(`${chosenSkill.name} ${skillLevel >= 0 ? "+" : ""}${skillLevel}`);
|
||||
if (customDM !== 0) tooltipParts.push(`MD perso ${customDM >= 0 ? "+" : ""}${customDM}`);
|
||||
|
||||
await TravellerCreatureSheet.#postCreatureRoll({
|
||||
actor, roll, rollLabel,
|
||||
dm,
|
||||
difficulty: result.difficulty,
|
||||
rollMode: result.rollMode,
|
||||
extraTooltip: tooltipParts.join(" | "),
|
||||
damageFormula: attack.damage || null,
|
||||
});
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────── CRUD Handlers
|
||||
|
||||
static async #onAddRow(event, target) {
|
||||
const prop = target.dataset.prop;
|
||||
if (!prop) return;
|
||||
const actor = this.document;
|
||||
const arr = foundry.utils.deepClone(actor.system[prop] ?? []);
|
||||
arr.push(this._getDefaultRow(prop));
|
||||
await actor.update({ [`system.${prop}`]: arr });
|
||||
}
|
||||
|
||||
static async #onDeleteRow(event, target) {
|
||||
const prop = target.dataset.prop;
|
||||
const index = parseInt(target.dataset.index);
|
||||
if (!prop || isNaN(index)) return;
|
||||
const actor = this.document;
|
||||
const arr = foundry.utils.deepClone(actor.system[prop] ?? []);
|
||||
arr.splice(index, 1);
|
||||
await actor.update({ [`system.${prop}`]: arr });
|
||||
}
|
||||
|
||||
_getDefaultRow(prop) {
|
||||
switch (prop) {
|
||||
case "skills": return { name: "", level: 0, note: "" };
|
||||
case "attacks": return { name: "", damage: "1D", skill: -1, description: "" };
|
||||
case "traits": return { name: "", value: "", description: "" };
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
285
src/module/applications/sheets/item-sheet.mjs
Normal file
285
src/module/applications/sheets/item-sheet.mjs
Normal file
@@ -0,0 +1,285 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
import { MGT2Helper } from "../../helper.js";
|
||||
|
||||
export default class TravellerItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["mgt2", "sheet", "item"],
|
||||
position: { width: 630 },
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false,
|
||||
},
|
||||
window: { resizable: true },
|
||||
actions: {
|
||||
careerEventCreate: TravellerItemSheet.#onCareerEventCreate,
|
||||
careerEventDelete: TravellerItemSheet.#onCareerEventDelete,
|
||||
optionCreate: TravellerItemSheet.#onOptionCreate,
|
||||
optionDelete: TravellerItemSheet.#onOptionDelete,
|
||||
modifierCreate: TravellerItemSheet.#onModifierCreate,
|
||||
modifierDelete: TravellerItemSheet.#onModifierDelete,
|
||||
},
|
||||
}
|
||||
|
||||
/** Dynamic PARTS: template resolved per item type */
|
||||
get PARTS() {
|
||||
const type = this.document?.type ?? "item";
|
||||
return {
|
||||
sheet: {
|
||||
template: `systems/mgt2/templates/items/${type}-sheet.html`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Resolve template dynamically based on item type */
|
||||
get template() {
|
||||
return `systems/mgt2/templates/items/${this.document.type}-sheet.html`;
|
||||
}
|
||||
|
||||
tabGroups = { primary: "tab1" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const item = this.document;
|
||||
const source = item.toObject();
|
||||
|
||||
const settings = {
|
||||
usePronouns: game.settings.get("mgt2", "usePronouns"),
|
||||
};
|
||||
|
||||
let containers = null;
|
||||
let computers = null;
|
||||
let hadContainer = false;
|
||||
|
||||
if (item.actor !== null) {
|
||||
hadContainer = true;
|
||||
containers = [{ name: "", _id: "" }].concat(item.actor.getContainers());
|
||||
computers = [{ name: "", _id: "" }].concat(item.actor.getComputers());
|
||||
}
|
||||
|
||||
let weight = null;
|
||||
if (item.system.hasOwnProperty("weight")) {
|
||||
weight = MGT2Helper.convertWeightForDisplay(item.system.weight);
|
||||
}
|
||||
|
||||
let skills = [];
|
||||
if (this.actor !== null) {
|
||||
for (let actorItem of this.actor.items) {
|
||||
if (actorItem.type === "talent" && actorItem.system.subType === "skill")
|
||||
skills.push({ _id: actorItem._id, name: actorItem.getRollDisplay() });
|
||||
}
|
||||
}
|
||||
skills.sort(MGT2Helper.compareByName);
|
||||
skills = [{ _id: "NP", name: game.i18n.localize("MGT2.Items.NotProficient") }].concat(skills);
|
||||
|
||||
const enrich = (html) => foundry.applications.ux.TextEditor.implementation.enrichHTML(html ?? "", { async: true });
|
||||
|
||||
return {
|
||||
item: item,
|
||||
document: item,
|
||||
cssClass: this.isEditable ? "editable" : "locked",
|
||||
system: item.system,
|
||||
source: source.system,
|
||||
fields: item.schema.fields,
|
||||
systemFields: item.system.schema.fields,
|
||||
isEditable: this.isEditable,
|
||||
isGM: game.user.isGM,
|
||||
config: CONFIG.MGT2,
|
||||
settings: settings,
|
||||
containers: containers,
|
||||
computers: computers,
|
||||
hadContainer: hadContainer,
|
||||
weight: weight,
|
||||
unitlabels: { weight: MGT2Helper.getWeightLabel() },
|
||||
skills: skills,
|
||||
enrichedDescription: await enrich(item.system.description),
|
||||
enrichedDescriptionLong: await enrich(item.system.descriptionLong),
|
||||
enrichedNotes: await enrich(item.system.notes),
|
||||
enrichedLockedDescription: await enrich(item.system.lockedDescription),
|
||||
};
|
||||
}
|
||||
|
||||
/** @override — resolve the per-type template before rendering */
|
||||
async _renderHTML(context, options) {
|
||||
const templatePath = `systems/mgt2/templates/items/${this.document.type}-sheet.html`;
|
||||
const html = await foundry.applications.handlebars.renderTemplate(templatePath, context);
|
||||
return { sheet: html };
|
||||
}
|
||||
|
||||
/** @override — put rendered HTML into the window content */
|
||||
_replaceHTML(result, content, options) {
|
||||
content.innerHTML = result.sheet;
|
||||
// Inject theme class dynamically (can't use game.settings in static DEFAULT_OPTIONS)
|
||||
const theme = game.settings.get("mgt2", "theme");
|
||||
if (theme) this.element.classList.add(theme);
|
||||
this._activateTabGroups();
|
||||
this._bindItemEvents();
|
||||
}
|
||||
|
||||
/** Bind CSS class-based events (templates not yet migrated to data-action) */
|
||||
_bindItemEvents() {
|
||||
const html = this.element;
|
||||
if (!this.isEditable) return;
|
||||
const bind = (sel, handler) => {
|
||||
for (const el of html.querySelectorAll(sel)) {
|
||||
el.addEventListener("click", (ev) => handler.call(this, ev, ev.currentTarget));
|
||||
}
|
||||
};
|
||||
bind(".event-create", TravellerItemSheet.#onCareerEventCreate);
|
||||
bind(".event-delete", TravellerItemSheet.#onCareerEventDelete);
|
||||
bind(".options-create", TravellerItemSheet.#onOptionCreate);
|
||||
bind(".options-delete", TravellerItemSheet.#onOptionDelete);
|
||||
bind(".modifiers-create", TravellerItemSheet.#onModifierCreate);
|
||||
bind(".modifiers-delete", TravellerItemSheet.#onModifierDelete);
|
||||
|
||||
// Activate ProseMirror editors for HTMLField fields
|
||||
for (const btn of html.querySelectorAll(".editor-edit")) {
|
||||
btn.addEventListener("click", async (event) => {
|
||||
event.preventDefault();
|
||||
const editorWrapper = btn.closest(".editor");
|
||||
if (!editorWrapper) return;
|
||||
const editorContent = editorWrapper.querySelector(".editor-content");
|
||||
if (!editorContent || editorContent.classList.contains("ProseMirror")) return;
|
||||
const target = editorContent.dataset.edit;
|
||||
const value = foundry.utils.getProperty(this.document, target) ?? "";
|
||||
btn.remove();
|
||||
editorWrapper.classList.add("prosemirror");
|
||||
await ProseMirrorEditor.create(editorContent, value, {
|
||||
document: this.document,
|
||||
fieldName: target,
|
||||
plugins: {},
|
||||
collaborate: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_activateTabGroups() {
|
||||
for (const [group, activeTab] of Object.entries(this.tabGroups)) {
|
||||
const nav = this.element.querySelector(`nav[data-group="${group}"], .horizontal-tabs`);
|
||||
if (!nav) continue;
|
||||
|
||||
nav.querySelectorAll('[data-tab]').forEach(link => {
|
||||
link.classList.toggle('active', link.dataset.tab === activeTab);
|
||||
link.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
this.tabGroups[group] = link.dataset.tab;
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
|
||||
this.element.querySelectorAll(`.itemsheet-panel [data-tab], [data-group="${group}"][data-tab]`).forEach(content => {
|
||||
content.classList.toggle('active', content.dataset.tab === activeTab);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** @override — process form data before submit (weight/qty/cost conversions + container logic) */
|
||||
_prepareSubmitData(event, form, formData) {
|
||||
const data = foundry.utils.expandObject(formData.object);
|
||||
|
||||
if (data.hasOwnProperty("weight")) {
|
||||
data.system = data.system || {};
|
||||
data.system.weight = MGT2Helper.convertWeightFromInput(data.weight);
|
||||
delete data.weight;
|
||||
}
|
||||
|
||||
if (data.system?.hasOwnProperty("quantity")) {
|
||||
data.system.quantity = MGT2Helper.getIntegerFromInput(data.system.quantity);
|
||||
}
|
||||
|
||||
if (data.system?.hasOwnProperty("cost")) {
|
||||
data.system.cost = MGT2Helper.getIntegerFromInput(data.system.cost);
|
||||
}
|
||||
|
||||
// Container/equipped logic
|
||||
if (data.system?.hasOwnProperty("container") && this.document.system.hasOwnProperty("equipped")) {
|
||||
const equippedChange = this.document.system.equipped !== data.system.equipped;
|
||||
const containerChange = this.document.system.container?.id !== data.system.container?.id;
|
||||
|
||||
if (equippedChange && data.system.equipped === true) {
|
||||
data.system.container = { id: "" };
|
||||
} else if (containerChange && data.system.container?.id !== "" && this.document.system.container?.id === "") {
|
||||
data.system.equipped = false;
|
||||
}
|
||||
}
|
||||
|
||||
return foundry.utils.flattenObject(data);
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Actions
|
||||
// =========================================================
|
||||
|
||||
static async #onCareerEventCreate(event) {
|
||||
event.preventDefault();
|
||||
const events = this.document.system.events;
|
||||
let newEvents;
|
||||
if (!events || events.length === 0) {
|
||||
newEvents = [{ age: "", description: "" }];
|
||||
} else {
|
||||
newEvents = [...events, { age: "", description: "" }];
|
||||
}
|
||||
return this.document.update({ system: { events: newEvents } });
|
||||
}
|
||||
|
||||
static async #onCareerEventDelete(event, target) {
|
||||
event.preventDefault();
|
||||
const element = target.closest("[data-events-part]");
|
||||
const index = Number(element.dataset.eventsPart);
|
||||
const events = foundry.utils.deepClone(this.document.system.events);
|
||||
const newEvents = Object.entries(events)
|
||||
.filter(([key]) => Number(key) !== index)
|
||||
.map(([, val]) => val);
|
||||
return this.document.update({ system: { events: newEvents } });
|
||||
}
|
||||
|
||||
static async #onOptionCreate(event, target) {
|
||||
event.preventDefault();
|
||||
const property = target.dataset.property;
|
||||
const options = this.document.system[property];
|
||||
let newOptions;
|
||||
if (!options || options.length === 0) {
|
||||
newOptions = [{ name: "", description: "" }];
|
||||
} else {
|
||||
newOptions = [...options, { name: "", description: "" }];
|
||||
}
|
||||
return this.document.update({ [`system.${property}`]: newOptions });
|
||||
}
|
||||
|
||||
static async #onOptionDelete(event, target) {
|
||||
event.preventDefault();
|
||||
const element = target.closest("[data-options-part]");
|
||||
const property = element.dataset.property;
|
||||
const index = Number(element.dataset.optionsPart);
|
||||
const options = foundry.utils.deepClone(this.document.system[property]);
|
||||
const newOptions = Object.entries(options)
|
||||
.filter(([key]) => Number(key) !== index)
|
||||
.map(([, val]) => val);
|
||||
return this.document.update({ [`system.${property}`]: newOptions });
|
||||
}
|
||||
|
||||
static async #onModifierCreate(event) {
|
||||
event.preventDefault();
|
||||
const modifiers = this.document.system.modifiers;
|
||||
let newModifiers;
|
||||
if (!modifiers || modifiers.length === 0) {
|
||||
newModifiers = [{ characteristic: "Endurance", value: null }];
|
||||
} else {
|
||||
newModifiers = [...modifiers, { characteristic: "Endurance", value: null }];
|
||||
}
|
||||
return this.document.update({ system: { modifiers: newModifiers } });
|
||||
}
|
||||
|
||||
static async #onModifierDelete(event, target) {
|
||||
event.preventDefault();
|
||||
const element = target.closest("[data-modifiers-part]");
|
||||
const index = Number(element.dataset.modifiersPart);
|
||||
const modifiers = foundry.utils.deepClone(this.document.system.modifiers);
|
||||
const newModifiers = Object.entries(modifiers)
|
||||
.filter(([key]) => Number(key) !== index)
|
||||
.map(([, val]) => val);
|
||||
return this.document.update({ system: { modifiers: newModifiers } });
|
||||
}
|
||||
}
|
||||
35
src/module/applications/sheets/vehicule-sheet.mjs
Normal file
35
src/module/applications/sheets/vehicule-sheet.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
import MGT2ActorSheet from "./base-actor-sheet.mjs";
|
||||
|
||||
export default class TravellerVehiculeSheet extends MGT2ActorSheet {
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "vehicule", "nopad"],
|
||||
window: {
|
||||
...super.DEFAULT_OPTIONS.window,
|
||||
title: "TYPES.Actor.vehicule",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
sheet: {
|
||||
template: "systems/mgt2/templates/actors/vehicule-sheet.html",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = { primary: "stats" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext();
|
||||
const actor = this.document;
|
||||
const enrich = (html) => foundry.applications.ux.TextEditor.implementation.enrichHTML(html ?? "", { async: true });
|
||||
|
||||
context.enrichedDescription = await enrich(actor.system.description);
|
||||
context.enrichedNotes = await enrich(actor.system.notes);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
238
src/module/chatHelper.js
Normal file
238
src/module/chatHelper.js
Normal file
@@ -0,0 +1,238 @@
|
||||
import { MGT2Helper } from "./helper.js";
|
||||
|
||||
export class ChatHelper {
|
||||
|
||||
static setupCardListeners(message, element, messageData) {
|
||||
if (!message || !element) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore disabled state for already-applied buttons
|
||||
const appliedActions = message.flags?.mgt2?.appliedActions ?? [];
|
||||
if (appliedActions.length > 0) {
|
||||
appliedActions.forEach(action => {
|
||||
element.querySelectorAll(`button[data-action="${action}"]`).forEach(btn => {
|
||||
btn.disabled = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Apply buttons are GM-only: hide them for players
|
||||
const GM_ACTIONS = ["damage", "healing", "surgeryDamage"];
|
||||
if (!game.user.isGM) {
|
||||
GM_ACTIONS.forEach(action => {
|
||||
element.querySelectorAll(`button[data-action="${action}"]`).forEach(btn => {
|
||||
btn.style.display = "none";
|
||||
});
|
||||
});
|
||||
// Hide the buttons container if no visible buttons remain
|
||||
element.querySelectorAll(".mgt2-buttons").forEach(container => {
|
||||
const hasVisible = [...container.querySelectorAll("button")]
|
||||
.some(b => b.style.display !== "none");
|
||||
if (!hasVisible) container.style.display = "none";
|
||||
});
|
||||
}
|
||||
|
||||
element.querySelectorAll('button[data-action="rollDamage"]').forEach(el => {
|
||||
el.addEventListener('click', async event => {
|
||||
await this._processRollDamageButtonEvent(message, event);
|
||||
});
|
||||
});
|
||||
|
||||
element.querySelectorAll('button[data-action="rollRadiation"]').forEach(el => {
|
||||
el.addEventListener('click', async event => {
|
||||
await this._rollRadiationDamage(message, event);
|
||||
});
|
||||
});
|
||||
|
||||
element.querySelectorAll('button[data-action="damage"]').forEach(el => {
|
||||
el.addEventListener('click', async event => {
|
||||
await this._applyChatCardDamage(message, event);
|
||||
});
|
||||
});
|
||||
|
||||
element.querySelectorAll('button[data-action="healing"]').forEach(el => {
|
||||
el.addEventListener('click', async event => {
|
||||
await this._applyChatCardHealing(message, event);
|
||||
});
|
||||
});
|
||||
|
||||
element.querySelectorAll('button[data-action="surgeryDamage"]').forEach(el => {
|
||||
el.addEventListener('click', async event => {
|
||||
await this._applyChatCardSurgeryDamage(message, event);
|
||||
});
|
||||
});
|
||||
|
||||
element.querySelectorAll('button[data-index]:not([data-action])').forEach(el => {
|
||||
el.addEventListener('click', async event => {
|
||||
await this._processRollButtonEvent(message, event);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static async _processRollButtonEvent(message, event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
let buttons = message.flags.mgt2.buttons;
|
||||
const index = event.target.dataset.index;
|
||||
const button = buttons[index];
|
||||
let roll = await new Roll(button.formula, {}).roll();
|
||||
|
||||
const chatData = {
|
||||
user: game.user.id,
|
||||
speaker: message.speaker,
|
||||
formula: roll._formula,
|
||||
tooltip: await roll.getTooltip(),
|
||||
total: Math.round(roll.total * 100) / 100,
|
||||
rollObjectName: button.message.objectName,
|
||||
rollMessage: MGT2Helper.format(button.message.flavor, Math.round(roll.total * 100) / 100),
|
||||
};
|
||||
|
||||
const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
|
||||
chatData.content = html;
|
||||
return roll.toMessage(chatData);
|
||||
}
|
||||
|
||||
static async _processRollDamageButtonEvent(message, event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const damageFlags = message.flags?.mgt2?.damage;
|
||||
if (!damageFlags?.formula) {
|
||||
ui.notifications.warn(game.i18n.localize("MGT2.Errors.InvalidRollFormula"));
|
||||
return;
|
||||
}
|
||||
|
||||
const effect = damageFlags.effect ?? 0;
|
||||
const effectPart = effect > 0 ? `+${effect}` : effect < 0 ? `${effect}` : "";
|
||||
const rollFormula = damageFlags.formula + effectPart;
|
||||
|
||||
let roll;
|
||||
try {
|
||||
roll = await new Roll(rollFormula, {}).roll();
|
||||
} catch (e) {
|
||||
ui.notifications.warn(game.i18n.localize("MGT2.Errors.InvalidRollFormula"));
|
||||
return;
|
||||
}
|
||||
|
||||
const rollTypeName = damageFlags.rollTypeName
|
||||
? damageFlags.rollTypeName + " " + game.i18n.localize("MGT2.Actor.Damage")
|
||||
: null;
|
||||
|
||||
const ap = damageFlags.ap ?? 0;
|
||||
const blast = damageFlags.blast ?? 0;
|
||||
const stun = damageFlags.stun ?? false;
|
||||
const radiation = damageFlags.radiation ?? false;
|
||||
|
||||
const chatData = {
|
||||
user: game.user.id,
|
||||
speaker: message.speaker,
|
||||
formula: roll._formula,
|
||||
tooltip: await roll.getTooltip(),
|
||||
total: Math.round(roll.total * 100) / 100,
|
||||
showButtons: true,
|
||||
hasDamage: true,
|
||||
rollTypeName,
|
||||
rollObjectName: damageFlags.rollObjectName,
|
||||
apValue: ap > 0 ? ap : null,
|
||||
blastRadius: blast > 0 ? blast : null,
|
||||
stunWeapon: stun || null,
|
||||
radiationWeapon: radiation || null,
|
||||
};
|
||||
|
||||
const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
|
||||
chatData.content = html;
|
||||
|
||||
// Persist ap, blast, stun, radiation in damage message flags so handlers can read them
|
||||
if (ap > 0 || blast > 0 || stun || radiation) chatData.flags = { mgt2: { damage: { ap, blast, stun, radiation, rollObjectName: damageFlags.rollObjectName } } };
|
||||
|
||||
return roll.toMessage(chatData);
|
||||
}
|
||||
|
||||
static async #markButtonApplied(message, btn, action) {
|
||||
const existing = message.flags?.mgt2?.appliedActions ?? [];
|
||||
if (!existing.includes(action)) {
|
||||
await message.setFlag("mgt2", "appliedActions", [...existing, action]);
|
||||
}
|
||||
if (btn) btn.disabled = true;
|
||||
}
|
||||
|
||||
static async _applyChatCardDamage(message, event) {
|
||||
if (game.user.targets.size === 0) {
|
||||
ui.notifications.warn(game.i18n.localize("MGT2.Errors.NoTokenSelected"));
|
||||
return;
|
||||
}
|
||||
const btn = event.currentTarget;
|
||||
const roll = message.rolls[0];
|
||||
const amount = Math.round(roll.total * 100) / 100;
|
||||
const ap = message.flags?.mgt2?.damage?.ap ?? 0;
|
||||
const stun = message.flags?.mgt2?.damage?.stun ?? false;
|
||||
await Promise.all([...game.user.targets].map(async t => {
|
||||
const result = await t.actor.applyDamage(amount, { ap, stun });
|
||||
if (stun) {
|
||||
const incapRounds = result?.incapRounds ?? 0;
|
||||
if (incapRounds > 0) {
|
||||
ui.notifications.warn(game.i18n.format("MGT2.Notifications.StunIncapacitated", { name: t.actor.name, rounds: incapRounds }));
|
||||
} else {
|
||||
ui.notifications.info(game.i18n.format("MGT2.Notifications.StunDamageApplied", { name: t.actor.name, amount }));
|
||||
}
|
||||
} else {
|
||||
const notifKey = ap > 0 ? "MGT2.Notifications.DamageAppliedAP" : "MGT2.Notifications.DamageApplied";
|
||||
ui.notifications.info(game.i18n.format(notifKey, { name: t.actor.name, amount, ap }));
|
||||
}
|
||||
}));
|
||||
await ChatHelper.#markButtonApplied(message, btn, "damage");
|
||||
}
|
||||
|
||||
static async _applyChatCardHealing(message, event) {
|
||||
if (game.user.targets.size === 0) {
|
||||
ui.notifications.warn(game.i18n.localize("MGT2.Errors.NoTokenSelected"));
|
||||
return;
|
||||
}
|
||||
const btn = event.currentTarget;
|
||||
const amount = message.flags?.mgt2?.healing?.amount
|
||||
?? message.flags?.mgt2?.surgery?.healing
|
||||
?? Math.max(1, message.rolls[0].total);
|
||||
await Promise.all([...game.user.targets].map(async t => {
|
||||
await t.actor.applyHealing(amount);
|
||||
ui.notifications.info(game.i18n.format("MGT2.Notifications.HealingApplied", { name: t.actor.name, amount }));
|
||||
}));
|
||||
await ChatHelper.#markButtonApplied(message, btn, "healing");
|
||||
}
|
||||
|
||||
static async _applyChatCardSurgeryDamage(message, event) {
|
||||
if (game.user.targets.size === 0) {
|
||||
ui.notifications.warn(game.i18n.localize("MGT2.Errors.NoTokenSelected"));
|
||||
return;
|
||||
}
|
||||
const btn = event.currentTarget;
|
||||
const amount = message.flags?.mgt2?.surgery?.surgeryDamage ?? 3;
|
||||
await Promise.all([...game.user.targets].map(async t => {
|
||||
await t.actor.applyDamage(amount, { ignoreArmor: true });
|
||||
ui.notifications.info(game.i18n.format("MGT2.Notifications.DamageApplied", { name: t.actor.name, amount }));
|
||||
}));
|
||||
await ChatHelper.#markButtonApplied(message, btn, "surgeryDamage");
|
||||
}
|
||||
|
||||
static async _rollRadiationDamage(message, event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const damageFlags = message.flags?.mgt2?.damage;
|
||||
const rollObjectName = damageFlags?.rollObjectName ?? "";
|
||||
|
||||
// 2D × 20 rads (MGT2 rule: 2d6 × 20)
|
||||
const roll = await new Roll("2d6 * 20", {}).roll();
|
||||
|
||||
const chatData = {
|
||||
user: game.user.id,
|
||||
speaker: message.speaker,
|
||||
formula: roll._formula,
|
||||
tooltip: await roll.getTooltip(),
|
||||
total: Math.round(roll.total),
|
||||
rollObjectName,
|
||||
};
|
||||
|
||||
const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/radiation.html", chatData);
|
||||
chatData.content = html;
|
||||
return roll.toMessage(chatData);
|
||||
}
|
||||
}
|
||||
187
src/module/config.js
Normal file
187
src/module/config.js
Normal file
@@ -0,0 +1,187 @@
|
||||
export const MGT2 = {};
|
||||
|
||||
MGT2.MetricRange = Object.freeze({
|
||||
meter: "MGT2.MetricRange.meter",
|
||||
kilometer: "MGT2.MetricRange.kilometer"
|
||||
});
|
||||
|
||||
MGT2.MetricWeight = Object.freeze({
|
||||
kilogram: "MGT2.MetricWeight.kilogram",
|
||||
ton: "MGT2.MetricWeight.ton"
|
||||
});
|
||||
|
||||
MGT2.Difficulty = Object.freeze({
|
||||
NA: "MGT2.Difficulty.NA",
|
||||
Simple: "MGT2.Difficulty.Simple",
|
||||
Easy: "MGT2.Difficulty.Easy",
|
||||
Routine: "MGT2.Difficulty.Routine",
|
||||
Average: "MGT2.Difficulty.Average",
|
||||
Difficult: "MGT2.Difficulty.Difficult",
|
||||
VeryDifficult: "MGT2.Difficulty.VeryDifficult",
|
||||
Formidable: "MGT2.Difficulty.Formidable",
|
||||
Impossible: "MGT2.Difficulty.Impossible"
|
||||
});
|
||||
|
||||
MGT2.ItemSubType = Object.freeze({
|
||||
loot: "MGT2.ItemSubType.loot",
|
||||
software: "MGT2.ItemSubType.software"
|
||||
});
|
||||
|
||||
MGT2.EquipmentSubType = Object.freeze({
|
||||
augment: "MGT2.EquipmentSubType.augment",
|
||||
clothing: "MGT2.EquipmentSubType.clothing",
|
||||
equipment: "MGT2.EquipmentSubType.equipment",
|
||||
trinket: "MGT2.EquipmentSubType.trinket",
|
||||
toolkit: "MGT2.EquipmentSubType.toolkit"
|
||||
});
|
||||
|
||||
MGT2.TalentSubType = Object.freeze({
|
||||
skill: "MGT2.TalentSubType.skill",
|
||||
psionic: "MGT2.TalentSubType.psionic"
|
||||
});
|
||||
|
||||
MGT2.DiseaseSubType = Object.freeze({
|
||||
disease: "MGT2.DiseaseSubType.disease",
|
||||
poison: "MGT2.DiseaseSubType.poison",
|
||||
wound: "MGT2.DiseaseSubType.wound"
|
||||
});
|
||||
|
||||
MGT2.PsionicReach = Object.freeze({
|
||||
NA: "MGT2.PsionicReach.NA",
|
||||
Personal: "MGT2.PsionicReach.Personal",
|
||||
Close: "MGT2.PsionicReach.Close",
|
||||
Short: "MGT2.PsionicReach.Short",
|
||||
Medium: "MGT2.PsionicReach.Medium",
|
||||
Long: "MGT2.PsionicReach.Long",
|
||||
VeryLong: "MGT2.PsionicReach.VeryLong",
|
||||
Distant: "MGT2.PsionicReach.Distant",
|
||||
VeryDistant: "MGT2.PsionicReach.VeryDistant",
|
||||
Continental: "MGT2.PsionicReach.Continental",
|
||||
Planetary: "MGT2.PsionicReach.Planetary"
|
||||
});
|
||||
|
||||
MGT2.ContactRelations = Object.freeze({
|
||||
Allie: "MGT2.Contact.Relation.Allie",
|
||||
Contact: "MGT2.Contact.Relation.Contact",
|
||||
Rival: "MGT2.Contact.Relation.Rival",
|
||||
Enemy: "MGT2.Contact.Relation.Enemy"
|
||||
});
|
||||
|
||||
MGT2.ContactStatus = Object.freeze({
|
||||
Alive: "MGT2.Contact.Status.Alive",
|
||||
Unknow: "MGT2.Contact.Status.Unknow",
|
||||
Dead: "MGT2.Contact.Status.Dead"
|
||||
});
|
||||
|
||||
MGT2.Attitudes = Object.freeze({
|
||||
Unknow: "MGT2.Contact.Attitude.Unknow",
|
||||
Hostile: "MGT2.Contact.Attitude.Hostile",
|
||||
Unfriendly: "MGT2.Contact.Attitude.Unfriendly",
|
||||
Indifferent: "MGT2.Contact.Attitude.Indifferent",
|
||||
Friendly: "MGT2.Contact.Attitude.Friendly",
|
||||
Helpful: "MGT2.Contact.Attitude.Helpful",
|
||||
Complicated: "MGT2.Contact.Attitude.Complicated"
|
||||
});
|
||||
|
||||
MGT2.Characteristics = Object.freeze({
|
||||
strength: "MGT2.Characteristics.strength.name",
|
||||
dexterity: "MGT2.Characteristics.dexterity.name",
|
||||
endurance: "MGT2.Characteristics.endurance.name",
|
||||
intellect: "MGT2.Characteristics.intellect.name",
|
||||
education: "MGT2.Characteristics.education.name",
|
||||
social: "MGT2.Characteristics.social.name",
|
||||
morale: "MGT2.Characteristics.morale.name",
|
||||
luck: "MGT2.Characteristics.luck.name",
|
||||
sanity: "MGT2.Characteristics.sanity.name",
|
||||
charm: "MGT2.Characteristics.charm.name",
|
||||
psionic: "MGT2.Characteristics.psionic.name",
|
||||
other: "MGT2.Characteristics.other.name"
|
||||
});
|
||||
|
||||
MGT2.InitiativeCharacteristics = Object.freeze({
|
||||
dexterity: "MGT2.Characteristics.dexterity.name",
|
||||
intellect: "MGT2.Characteristics.intellect.name"
|
||||
});
|
||||
|
||||
MGT2.DamageCharacteristics = Object.freeze({
|
||||
strength: "MGT2.Characteristics.strength.name",
|
||||
dexterity: "MGT2.Characteristics.dexterity.name",
|
||||
endurance: "MGT2.Characteristics.endurance.name"
|
||||
});
|
||||
|
||||
MGT2.TL = Object.freeze({
|
||||
NA: "MGT2.TL.NA",
|
||||
Unknow: "MGT2.TL.Unknow",
|
||||
NotIdentified: "MGT2.TL.NotIdentified",
|
||||
TL00: "MGT2.TL.L00",
|
||||
TL01: "MGT2.TL.L01",
|
||||
TL02: "MGT2.TL.L02",
|
||||
TL03: "MGT2.TL.L03",
|
||||
TL04: "MGT2.TL.L04",
|
||||
TL05: "MGT2.TL.L05",
|
||||
TL06: "MGT2.TL.L06",
|
||||
TL07: "MGT2.TL.L07",
|
||||
TL08: "MGT2.TL.L08",
|
||||
TL09: "MGT2.TL.L09",
|
||||
TL10: "MGT2.TL.L10",
|
||||
TL11: "MGT2.TL.L11",
|
||||
TL12: "MGT2.TL.L12",
|
||||
TL13: "MGT2.TL.L13",
|
||||
TL14: "MGT2.TL.L14",
|
||||
TL15: "MGT2.TL.L15"
|
||||
});
|
||||
|
||||
MGT2.Timeframes = Object.freeze({
|
||||
Normal: "MGT2.Timeframes.Normal",
|
||||
Slower: "MGT2.Timeframes.Slower",
|
||||
Faster: "MGT2.Timeframes.Faster"
|
||||
});
|
||||
|
||||
MGT2.SpeedBands = Object.freeze({
|
||||
Stoppped: "MGT2.SpeedBands.Stoppped",
|
||||
Idle: "MGT2.SpeedBands.Idle",
|
||||
VerySlow: "MGT2.SpeedBands.VerySlow",
|
||||
Slow: "MGT2.SpeedBands.Slow",
|
||||
Medium: "MGT2.SpeedBands.Medium",
|
||||
High: "MGT2.SpeedBands.High",
|
||||
Fast: "MGT2.SpeedBands.Fast",
|
||||
VeryFast: "MGT2.SpeedBands.VeryFast",
|
||||
Subsonic: "MGT2.SpeedBands.Subsonic",
|
||||
Hypersonic: "MGT2.SpeedBands.Hypersonic"
|
||||
});
|
||||
|
||||
MGT2.Durations = Object.freeze({
|
||||
Seconds: "MGT2.Durations.Seconds",
|
||||
Minutes: "MGT2.Durations.Minutes",
|
||||
Heures: "MGT2.Durations.Heures"
|
||||
});
|
||||
|
||||
MGT2.CreatureBehaviorType = Object.freeze({
|
||||
herbivore: "MGT2.CreatureBehaviorType.herbivore",
|
||||
carnivore: "MGT2.CreatureBehaviorType.carnivore",
|
||||
charognard: "MGT2.CreatureBehaviorType.charognard",
|
||||
omnivore: "MGT2.CreatureBehaviorType.omnivore"
|
||||
});
|
||||
|
||||
MGT2.CreatureBehaviorSubType = Object.freeze({
|
||||
accumulateur: "MGT2.CreatureBehaviorSubType.accumulateur",
|
||||
brouteur: "MGT2.CreatureBehaviorSubType.brouteur",
|
||||
filtreur: "MGT2.CreatureBehaviorSubType.filtreur",
|
||||
intermittent: "MGT2.CreatureBehaviorSubType.intermittent",
|
||||
chasseur: "MGT2.CreatureBehaviorSubType.chasseur",
|
||||
detourneur: "MGT2.CreatureBehaviorSubType.detourneux",
|
||||
guetteur: "MGT2.CreatureBehaviorSubType.guetteur",
|
||||
mangeur: "MGT2.CreatureBehaviorSubType.mangeur",
|
||||
piegeur: "MGT2.CreatureBehaviorSubType.piegeur",
|
||||
intimidateur: "MGT2.CreatureBehaviorSubType.intimidateur",
|
||||
necrophage: "MGT2.CreatureBehaviorSubType.necrophage",
|
||||
reducteur: "MGT2.CreatureBehaviorSubType.reducteur",
|
||||
opportuniste: "MGT2.CreatureBehaviorSubType.opportuniste"
|
||||
});
|
||||
|
||||
MGT2.HealingType = Object.freeze({
|
||||
FIRST_AID: "MGT2.Healing.FirstAid",
|
||||
SURGERY: "MGT2.Healing.Surgery",
|
||||
MEDICAL_CARE: "MGT2.Healing.MedicalCare",
|
||||
NATURAL_HEALING: "MGT2.Healing.NaturalHealing"
|
||||
});
|
||||
1
src/module/constants.js
Normal file
1
src/module/constants.js
Normal file
@@ -0,0 +1 @@
|
||||
export const ATTRIBUTE_TYPES = ["String", "Number", "Boolean", "Formula", "Resource"];
|
||||
133
src/module/core.js
Normal file
133
src/module/core.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import {
|
||||
CharacterData,
|
||||
VehiculeData,
|
||||
CreatureData,
|
||||
ItemData,
|
||||
EquipmentData,
|
||||
DiseaseData,
|
||||
CareerData,
|
||||
TalentData,
|
||||
ContactData,
|
||||
ArmorData,
|
||||
ComputerData,
|
||||
WeaponData,
|
||||
ItemContainerData,
|
||||
SpeciesData
|
||||
} from "./models/index.mjs";
|
||||
|
||||
import { MGT2 } from "./config.js";
|
||||
import { TravellerActor, MGT2Combatant } from "./actors/actor.js";
|
||||
import { TravellerItem } from "./item.js";
|
||||
import { TravellerItemSheet, TravellerCharacterSheet, TravellerVehiculeSheet, TravellerCreatureSheet } from "./applications/sheets/_module.mjs";
|
||||
import { preloadHandlebarsTemplates } from "./templates.js";
|
||||
//import { MGT2Helper } from "./helper.js";
|
||||
import {ChatHelper} from "./chatHelper.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Foundry VTT Initialization */
|
||||
/* -------------------------------------------- */
|
||||
import { registerSettings } from "./settings.js";
|
||||
|
||||
function registerHandlebarsHelpers() {
|
||||
Handlebars.registerHelper('showDM', function (dm) {
|
||||
if (dm === 0) return "0";
|
||||
if (dm > 0) return `+${dm}`;
|
||||
if (dm < 0) return `${dm}`;
|
||||
return "";
|
||||
});
|
||||
}
|
||||
|
||||
Hooks.once("init", async function () {
|
||||
CONFIG.MGT2 = MGT2;
|
||||
CONFIG.Combat.initiative = {
|
||||
formula: "2d6 + @initiative",
|
||||
decimals: 2
|
||||
};
|
||||
|
||||
CONFIG.Actor.trackableAttributes = {
|
||||
character: {
|
||||
bar: ["life",
|
||||
"characteristics.strength",
|
||||
"characteristics.dexterity",
|
||||
"characteristics.endurance",
|
||||
"characteristics.intellect",
|
||||
"characteristics.education",
|
||||
"characteristics.social",
|
||||
"characteristics.morale",
|
||||
"characteristics.luck",
|
||||
"characteristics.sanity",
|
||||
"characteristics.charm",
|
||||
"characteristics.psionic",
|
||||
"characteristics.other"
|
||||
],
|
||||
value: ["life.value",
|
||||
"health.radiations",
|
||||
"characteristics.strength.value",
|
||||
"characteristics.dexterity.value",
|
||||
"characteristics.endurance.value",
|
||||
"characteristics.intellect.value",
|
||||
"characteristics.education.value",
|
||||
"characteristics.social.value",
|
||||
"characteristics.morale.value",
|
||||
"characteristics.luck.value",
|
||||
"characteristics.sanity.value",
|
||||
"characteristics.charm.value",
|
||||
"characteristics.psionic.value",
|
||||
"characteristics.other.value"]
|
||||
},
|
||||
creature: {
|
||||
bar: ["life"],
|
||||
value: ["life.value", "life.max", "speed", "armor", "psi"]
|
||||
}
|
||||
};
|
||||
|
||||
game.mgt2 = {
|
||||
TravellerActor,
|
||||
TravellerItem
|
||||
};
|
||||
|
||||
registerHandlebarsHelpers();
|
||||
registerSettings();
|
||||
|
||||
CONFIG.Combatant.documentClass = MGT2Combatant;
|
||||
CONFIG.Actor.documentClass = TravellerActor;
|
||||
CONFIG.Item.documentClass = TravellerItem;
|
||||
|
||||
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
|
||||
foundry.documents.collections.Actors.registerSheet("mgt2", TravellerCharacterSheet, { types: ["character"], makeDefault: true, label: "Traveller Sheet" });
|
||||
foundry.documents.collections.Actors.registerSheet("mgt2", TravellerVehiculeSheet, { types: ["vehicule"], makeDefault: true, label: "Vehicule Sheet" });
|
||||
foundry.documents.collections.Actors.registerSheet("mgt2", TravellerCreatureSheet, { types: ["creature"], makeDefault: true, label: "Creature Sheet" });
|
||||
|
||||
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
|
||||
foundry.documents.collections.Items.registerSheet("mgt2", TravellerItemSheet, { makeDefault: true });
|
||||
|
||||
Object.assign(CONFIG.Actor.dataModels, {
|
||||
"character": CharacterData,
|
||||
"vehicule": VehiculeData,
|
||||
"creature": CreatureData
|
||||
});
|
||||
|
||||
Object.assign(CONFIG.Item.dataModels, {
|
||||
"item": ItemData,
|
||||
"equipment": EquipmentData,
|
||||
"disease": DiseaseData,
|
||||
"career": CareerData,
|
||||
"talent": TalentData,
|
||||
"contact": ContactData,
|
||||
"weapon": WeaponData,
|
||||
"computer": ComputerData,
|
||||
"armor": ArmorData,
|
||||
"container": ItemContainerData,
|
||||
"species": SpeciesData
|
||||
});
|
||||
|
||||
|
||||
Hooks.on("renderChatMessageHTML", (message, element, messageData) => {
|
||||
ChatHelper.setupCardListeners(message, element, messageData);
|
||||
});
|
||||
|
||||
// Preload template partials
|
||||
await preloadHandlebarsTemplates();
|
||||
});
|
||||
|
||||
export { MGT2 };
|
||||
221
src/module/helper.js
Normal file
221
src/module/helper.js
Normal file
@@ -0,0 +1,221 @@
|
||||
export class MGT2Helper {
|
||||
static POUNDS_CONVERT = 2.20462262185;
|
||||
|
||||
static decimalSeparator;
|
||||
static badDecimalSeparator;
|
||||
|
||||
static {
|
||||
this.decimalSeparator = Number(1.1).toLocaleString().charAt(1);
|
||||
this.badDecimalSeparator = (this.decimalSeparator === "." ? "," : ".");
|
||||
}
|
||||
|
||||
static format = function() {
|
||||
var s = arguments[0];
|
||||
for (var i = 0; i < arguments.length - 1; i++) {
|
||||
var reg = new RegExp("\\{" + i + "\\}", "gm");
|
||||
s = s.replace(reg, arguments[i + 1]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static hasValue(object, property) {
|
||||
return object !== undefined && object.hasOwnProperty(property) && object[property] !== null && object[property] !== undefined && object[property] !== "";
|
||||
}
|
||||
|
||||
static getItemsWeight(items) {
|
||||
let weight = 0;
|
||||
for (let i of items) {
|
||||
let item = i.hasOwnProperty("system") ? i.system : i;
|
||||
if (item.hasOwnProperty("weightless") && item.weightless === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.hasOwnProperty("weight")) {
|
||||
let itemQty = item.quantity
|
||||
if (!isNaN(itemQty) && itemQty > 0) {
|
||||
let itemWeight = item.weight;
|
||||
if (itemWeight > 0) {
|
||||
weight += itemWeight * itemQty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
static generateUID() {
|
||||
let result = '';
|
||||
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
for (let i = 0; i < 36; i++) {
|
||||
const randomIndex = Math.floor(Math.random() * characters.length);
|
||||
result += characters.charAt(randomIndex);
|
||||
if (i === 8 || i === 12 || i === 16 || i === 20)
|
||||
result += "-";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static compareByName(a, b) {
|
||||
if (!a.hasOwnProperty("name") || !b.hasOwnProperty("name")) {
|
||||
return 0;
|
||||
}
|
||||
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||
}
|
||||
|
||||
static getDisplayDM(dm) {
|
||||
if (dm === 0) return " (0)";
|
||||
if (dm > 0) return ` (+${dm})`;
|
||||
if (dm < 0) return ` (${dm})`;
|
||||
return "";
|
||||
}
|
||||
static getFormulaDM(dm) {
|
||||
if (dm === 0) return "+0";
|
||||
if (dm > 0) return `+${dm}`;
|
||||
if (dm < 0) return `${dm}`;
|
||||
return "";
|
||||
}
|
||||
|
||||
static getDiceResults(roll) {
|
||||
const results = [];
|
||||
for (const die of roll.dice) {
|
||||
results.push(die.results);
|
||||
}
|
||||
return results.flat(2);
|
||||
}
|
||||
|
||||
static getDiceTotal(roll) {
|
||||
let total = 0;
|
||||
for (const die of roll.dice) {
|
||||
total += die.total;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static getDifficultyValue(difficulty) {
|
||||
switch(difficulty) {
|
||||
case "Simple": return 2;
|
||||
case "Easy": return 4;
|
||||
case "Routine": return 6;
|
||||
case "Average": return 8;
|
||||
case "Difficult": return 10;
|
||||
case "VeryDifficult": return 12;
|
||||
case "Formidable": return 14;
|
||||
case "Impossible": return 16;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static getDifficultyDisplay(difficulty) {
|
||||
const key = `MGT2.Difficulty.${difficulty}`;
|
||||
const label = game.i18n.localize(key);
|
||||
return label !== key ? label : null;
|
||||
}
|
||||
|
||||
static getRangeDisplay(range) {
|
||||
let value = Number(range.value);
|
||||
|
||||
if (isNaN(value)) return null;
|
||||
|
||||
let label;
|
||||
//if (game.settings.get("mgt2", "useDistanceMetric") === true) {
|
||||
if (range.unit !== null && range.unit !== undefined && range.unit !== "")
|
||||
label = game.i18n.localize(`MGT2.MetricRange.${range.unit}`).toLowerCase();
|
||||
else
|
||||
label = "";
|
||||
//} else {
|
||||
// TODO
|
||||
//}
|
||||
|
||||
return `${value}${label}`;
|
||||
}
|
||||
|
||||
static getWeightLabel() {
|
||||
//const label = game.settings.get("mgt2", "useWeightMetric") === true ? "MGT2.MetricSystem.Weight.kg" : "MGT2.ImperialSystem.Weight.lb";
|
||||
//return game.i18n.localize(label);
|
||||
return game.i18n.localize("MGT2.MetricSystem.Weight.kg");
|
||||
}
|
||||
|
||||
static getDistanceLabel() {
|
||||
//const label = game.settings.get("mgt2", "useDistanceMetric") === true ? "MGT2.MetricSystem.Distance.km" : "MGT2.ImperialSystem.Distance.mi";
|
||||
//return game.i18n.localize(label);
|
||||
return game.i18n.localize("MGT2.MetricSystem.Distance.km");
|
||||
}
|
||||
|
||||
static getIntegerFromInput(data) {
|
||||
return Math.trunc(this.getNumberFromInput(data));
|
||||
}
|
||||
|
||||
static getNumberFromInput(data) {
|
||||
if (data === undefined || data === null) return 0;
|
||||
|
||||
if (typeof data === "string") {
|
||||
let converted = Number(data.replace(/\s+/g, '').replace(this.badDecimalSeparator, this.decimalSeparator).trim());
|
||||
if (isNaN(converted))
|
||||
return 0;
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
let converted = Number(data);
|
||||
|
||||
if (isNaN(converted))
|
||||
return 0;
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
static convertWeightForDisplay(weight) {
|
||||
//if (game.settings.get("mgt2", "useWeightMetric") === true || weight === 0)
|
||||
return weight;
|
||||
|
||||
// Metric to Imperial
|
||||
//const pounds = weight * this.POUNDS_CONVERT;
|
||||
//return Math.round(pounds * 10) / 10;
|
||||
}
|
||||
|
||||
static convertWeightFromInput(weight) {
|
||||
//if (game.settings.get("mgt2", "useWeightMetric") === true || weight === 0)
|
||||
return Math.round(weight * 10) / 10;
|
||||
|
||||
// Imperial to Metric
|
||||
//const kg = this.POUNDS_CONVERT / weight;
|
||||
//return Math.round(kg * 10) / 10;
|
||||
}
|
||||
|
||||
static getDataFromDropEvent(event) {
|
||||
let data;
|
||||
try {
|
||||
return JSON.parse(event.dataTransfer?.getData("text/plain"));
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if ( data.type !== "Item" ) return false;
|
||||
//const item = await Item.implementation.fromDropData(data);
|
||||
}
|
||||
|
||||
static async getItemDataFromDropData(dropData) {
|
||||
//console.log("getItemDataFromDropData");
|
||||
let item;
|
||||
if (game.modules.get("monks-enhanced-journal")?.active && dropData.itemId && dropData.uuid.includes("JournalEntry")) {
|
||||
const journalEntry = await fromUuid(dropData.uuid);
|
||||
} else if (dropData.hasOwnProperty("uuid")) {
|
||||
item = await fromUuid(dropData.uuid);
|
||||
} else {
|
||||
let uuid = `${dropData.type}.${dropData.data._id}`;
|
||||
item = await fromUuid(uuid);
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
throw new Error(game.i18n.localize("Errors.CouldNotFindItem").replace("_ITEM_ID_", dropData.uuid));
|
||||
}
|
||||
if (item.pack) {
|
||||
const pack = game.packs.get(item.pack);
|
||||
item = await pack?.getDocument(item._id);
|
||||
}
|
||||
return deepClone(item);
|
||||
}
|
||||
}
|
||||
402
src/module/item-sheet.js
Normal file
402
src/module/item-sheet.js
Normal file
@@ -0,0 +1,402 @@
|
||||
import { MGT2Helper } from "./helper.js";
|
||||
|
||||
/**
|
||||
* Extend the basic ItemSheet with some very simple modifications
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class TravellerItemSheet extends ItemSheet {
|
||||
|
||||
/** @inheritdoc */
|
||||
static get defaultOptions() {
|
||||
const options = super.defaultOptions;
|
||||
return foundry.utils.mergeObject(options, {
|
||||
classes: ["mgt2", game.settings.get("mgt2", "theme"), "sheet"],
|
||||
width: 630,
|
||||
tabs: [{ navSelector: ".horizontal-tabs", contentSelector: ".itemsheet-panel", initial: "tab1" }]
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
get template() {
|
||||
const path = "systems/mgt2/templates/items";
|
||||
return `${path}/${this.item.type}-sheet.html`;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
async getData(options) {
|
||||
const context = await super.getData(options);
|
||||
//console.log('-=getData=-');
|
||||
//console.log(context);
|
||||
const item = context.item;
|
||||
|
||||
const source = item.toObject();
|
||||
context.config = CONFIG.MGT2;
|
||||
|
||||
const settings = {};
|
||||
settings.usePronouns = game.settings.get("mgt2", "usePronouns");
|
||||
|
||||
let containers = null;
|
||||
let computers = null;;
|
||||
let hadContainer;
|
||||
if (context.item.actor != null) {
|
||||
hadContainer = true;
|
||||
containers = [{ "name": "", "_id": "" }].concat(context.item.actor.getContainers());
|
||||
computers = [{ "name": "", "_id": "" }].concat(context.item.actor.getComputers());
|
||||
} else {
|
||||
hadContainer = false;
|
||||
}
|
||||
|
||||
let weight = null;
|
||||
if (item.system.hasOwnProperty("weight")) {
|
||||
weight = MGT2Helper.convertWeightForDisplay(item.system.weight);
|
||||
}
|
||||
let unitlabels = {
|
||||
weight: MGT2Helper.getWeightLabel()
|
||||
};
|
||||
let skills = [];
|
||||
|
||||
if (this.actor !== null) {
|
||||
for (let item of this.actor.items) {
|
||||
if (item.type === "talent") {
|
||||
if (item.system.subType === "skill")
|
||||
skills.push({ _id: item._id, name: item.getRollDisplay() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skills.sort(MGT2Helper.compareByName);
|
||||
skills = [{ _id: "NP", name: game.i18n.localize("MGT2.Items.NotProficient") }].concat(skills);
|
||||
|
||||
foundry.utils.mergeObject(context, {
|
||||
source: source.system,
|
||||
system: item.system,
|
||||
settings: settings,
|
||||
containers: containers,
|
||||
computers: computers,
|
||||
hadContainer: hadContainer,
|
||||
weight: weight,
|
||||
unitlabels: unitlabels,
|
||||
editable: this.isEditable,
|
||||
isGM: game.user.isGM,
|
||||
skills: skills,
|
||||
config: CONFIG
|
||||
//rollData: this.item.getRollData(),
|
||||
});
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritdoc */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
// Everything below here is only needed if the sheet is editable
|
||||
if (!this.isEditable) return;
|
||||
|
||||
//let handler = ev => this._onDropCustom(ev);
|
||||
|
||||
//console.log(html);
|
||||
// itemsheet-panel
|
||||
//html.addEventListener("dragstart", this._onDropCustom, false);
|
||||
html.find('div.itemsheet-panel').each((i, li) => {
|
||||
// //if (li.classList.contains("inventory-header")) return;
|
||||
//li.setAttribute("draggable", true);
|
||||
//li.addEventListener("drop", handler, false);
|
||||
});
|
||||
|
||||
|
||||
//html.find('div.dropitem').each((i, li) => {
|
||||
// //if (li.classList.contains("inventory-header")) return;
|
||||
// li.setAttribute("draggable", true);
|
||||
// li.addEventListener("dragstart", handler, false);
|
||||
//});
|
||||
|
||||
// if (this.item.type == "weapon") {
|
||||
// html.find('.trait-create').click(this._onTraitCreate.bind(this));
|
||||
// html.find('.trait-delete').click(this._onTraitDelete.bind(this));
|
||||
// }
|
||||
|
||||
if (this.item.type == "career") {
|
||||
html.find('.event-create').click(this._onCareerEventCreate.bind(this));
|
||||
html.find('.event-delete').click(this._onCareerEventDelete.bind(this));
|
||||
}
|
||||
|
||||
else if (this.item.type == "armor" ||
|
||||
this.item.type == "computer" ||
|
||||
this.item.type == "species" ||
|
||||
this.item.type == "weapon") {
|
||||
html.find('.options-create').click(this._onOptionCreate.bind(this));
|
||||
html.find('.options-delete').click(this._onOptionDelete.bind(this));
|
||||
}
|
||||
|
||||
if (this.item.type == "species") {
|
||||
html.find('.modifiers-create').click(this._onModifierEventCreate.bind(this));
|
||||
html.find('.modifiers-delete').click(this._onModifierEventDelete.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
async _onModifierEventCreate(event) {
|
||||
event.preventDefault();
|
||||
await this._onSubmit(event);
|
||||
|
||||
let modifiers = this.item.system.modifiers;
|
||||
let index;
|
||||
if (modifiers.length === 0) {
|
||||
modifiers = {};
|
||||
modifiers["0"] = { characteristic: "Endurance", value: null };
|
||||
} else {
|
||||
index = Math.max(...Object.keys(modifiers));
|
||||
index++;
|
||||
modifiers[index] = { characteristic: "Endurance", value: null };
|
||||
}
|
||||
|
||||
let update = {
|
||||
system: {
|
||||
modifiers: modifiers
|
||||
}
|
||||
};
|
||||
|
||||
return this.item.update(update);
|
||||
}
|
||||
|
||||
async _onModifierEventDelete(event) {
|
||||
event.preventDefault();
|
||||
await this._onSubmit(event);
|
||||
const element = event.currentTarget.closest(".modifiers-part");
|
||||
const modifiers = foundry.utils.deepClone(this.item.system.modifiers);
|
||||
let index = Number(element.dataset.modifiersPart);
|
||||
|
||||
const newModifiers = [];
|
||||
let entries = Object.entries(modifiers);
|
||||
if (entries.length > 1) {
|
||||
for (const [key, value] of entries) {
|
||||
if (key != index)
|
||||
newModifiers.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
let update = {
|
||||
system: {
|
||||
modifiers: newModifiers
|
||||
}
|
||||
};
|
||||
|
||||
return this.item.update(update);
|
||||
}
|
||||
|
||||
async _onCareerEventCreate(event) {
|
||||
event.preventDefault();
|
||||
await this._onSubmit(event);
|
||||
|
||||
let events = this.item.system.events;
|
||||
let index;
|
||||
if (events.length === 0) {
|
||||
events = {};
|
||||
events["0"] = { age: "", description: "" };
|
||||
} else {
|
||||
index = Math.max(...Object.keys(events));
|
||||
index++;
|
||||
events[index] = { age: "", description: "" };
|
||||
}
|
||||
|
||||
let update = {
|
||||
system: {
|
||||
events: events
|
||||
}
|
||||
};
|
||||
|
||||
return this.item.update(update);
|
||||
}
|
||||
|
||||
async _onCareerEventDelete(event) {
|
||||
event.preventDefault();
|
||||
await this._onSubmit(event);
|
||||
const element = event.currentTarget.closest(".events-part");
|
||||
const events = foundry.utils.deepClone(this.item.system.events);
|
||||
let index = Number(element.dataset.eventsPart);
|
||||
|
||||
const newEvents = [];
|
||||
let entries = Object.entries(events);
|
||||
if (entries.length > 1) {
|
||||
for (const [key, value] of entries) {
|
||||
if (key != index)
|
||||
newEvents.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
let update = {
|
||||
system: {
|
||||
events: newEvents
|
||||
}
|
||||
};
|
||||
|
||||
return this.item.update(update);
|
||||
}
|
||||
|
||||
async _onOptionCreate(event) {
|
||||
event.preventDefault();
|
||||
await this._onSubmit(event);
|
||||
|
||||
//const subType = event.currentTarget.dataset.subType;
|
||||
const property = event.currentTarget.dataset.property;
|
||||
|
||||
//let options = this.item.system[subType][property];
|
||||
let options = this.item.system[property];
|
||||
let index;
|
||||
if (options.length === 0) {
|
||||
options = {};
|
||||
options["0"] = { name: "", description: "" };
|
||||
} else {
|
||||
index = Math.max(...Object.keys(options));
|
||||
index++;
|
||||
options[index] = { name: "", description: "" };
|
||||
}
|
||||
|
||||
let update = {};
|
||||
//update[`system.${subType}.${property}`] = options;
|
||||
update[`system.${property}`] = options;
|
||||
return this.item.update(update);
|
||||
}
|
||||
|
||||
async _onOptionDelete(event) {
|
||||
event.preventDefault();
|
||||
await this._onSubmit(event);
|
||||
const element = event.currentTarget.closest(".options-part");
|
||||
//const subType = element.dataset.subType;
|
||||
const property = element.dataset.property;
|
||||
//const options = foundry.utils.deepClone(this.item.system[subType][property]);
|
||||
const options = foundry.utils.deepClone(this.item.system[property]);
|
||||
let index = Number(element.dataset.optionsPart);
|
||||
|
||||
const newOptions = [];
|
||||
let entries = Object.entries(options);
|
||||
if (entries.length > 1) {
|
||||
for (const [key, value] of entries) {
|
||||
if (key != index)
|
||||
newOptions.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
let update = {};
|
||||
//update[`system.${subType}.${property}`] = newOptions;
|
||||
update[`system.${property}`] = newOptions;
|
||||
return this.item.update(update);
|
||||
}
|
||||
|
||||
// async _onTraitCreate(event) {
|
||||
// event.preventDefault();
|
||||
// await this._onSubmit(event);
|
||||
// const traits = this.item.system.traits;
|
||||
// return this.item.update({ "system.traits.parts": traits.parts.concat([["", ""]]) });
|
||||
// }
|
||||
|
||||
// async _onTraitDelete(event) {
|
||||
// event.preventDefault();
|
||||
// await this._onSubmit(event);
|
||||
// const element = event.currentTarget.closest(".traits-part");
|
||||
// const traits = foundry.utils.deepClone(this.item.system.traits);
|
||||
// traits.parts.splice(Number(element.dataset.traitsPart), 1);
|
||||
// return this.item.update({ "system.traits.parts": traits.parts });
|
||||
// }
|
||||
|
||||
_getSubmitData(updateData = {}) {
|
||||
const formData = foundry.utils.expandObject(super._getSubmitData(updateData));
|
||||
|
||||
// Gestion des containers
|
||||
if (formData.hasOwnProperty("system") && formData.system.hasOwnProperty("container") &&
|
||||
(this.item.system.hasOwnProperty("equipped"))) {
|
||||
//*console.log('-=_getSubmitData=-');
|
||||
//console.log(this.item.system.onHand);
|
||||
//console.log(formData.system.onHand);
|
||||
//const onHandChange = this.item.system.onHand !== formData.system.onHand;
|
||||
const equippedChange = this.item.system.equipped !== formData.system.equipped;
|
||||
const containerChange = this.item.system.container.id !== formData.system.container.id;
|
||||
// Maintenant équipé
|
||||
if (equippedChange) {
|
||||
if (formData.system.equipped === true) {
|
||||
//formData.system.onHand = true;
|
||||
//console.log("clear container");
|
||||
formData.system.container = {
|
||||
//inContainer: false,
|
||||
id: ""
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*else if (onHandChange) {
|
||||
// Maintenant à portée
|
||||
if (formData.system.onHand === true) {
|
||||
//console.log("clear container");
|
||||
formData.system.container = {
|
||||
inContainer: false,
|
||||
id: ""
|
||||
};
|
||||
} else {
|
||||
formData.system.equipped = false;
|
||||
}
|
||||
}*/
|
||||
|
||||
else if (containerChange) {
|
||||
// Mise en storage
|
||||
if (formData.system.container.id !== "" && (this.item.system.container.id === "" || this.item.system.container.id === null)) {
|
||||
//console.log("put in container");
|
||||
//formData.system.onHand = false;
|
||||
formData.system.equipped = false;
|
||||
//formData.system.container.inContainer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if (this.item.type == "weapon") {
|
||||
// const traits = formData.system?.traits;
|
||||
// if (traits)
|
||||
// traits.parts = Object.values(traits?.parts || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||
// }
|
||||
|
||||
// else if (this.item.type == "career") {
|
||||
// const events = formData.system?.events;
|
||||
// if (events)
|
||||
// events.parts = Object.values(events?.parts || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||
// }
|
||||
|
||||
// else if (this.item.type == "equipment") {
|
||||
// if (this.item.system.subType == "armor") {
|
||||
// // const armor = formData.system?.armor;
|
||||
// // if (armor)
|
||||
// // //options.parts = Object.values(options?.parts || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||
// // console.log(armor.options);
|
||||
// // armor.options = Object.values(armor?.options || {})
|
||||
// // .map(d => [d.name || "", d.description || ""]);
|
||||
// // console.log(armor.options);
|
||||
// } else if (this.item.system.subType == "computer") {
|
||||
// const computer = formData.system?.computer;
|
||||
// if (computer)
|
||||
// //options.parts = Object.values(options?.parts || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||
// computer.options = Object.values(computer?.options || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (formData.hasOwnProperty("weight")) {
|
||||
formData.system.weight = MGT2Helper.convertWeightFromInput(formData.weight);
|
||||
delete formData.weight;
|
||||
}
|
||||
|
||||
if (formData.system.hasOwnProperty("quantity")) {
|
||||
formData.system.quantity = MGT2Helper.getIntegerFromInput(formData.system.quantity);
|
||||
}
|
||||
|
||||
if (formData.system.hasOwnProperty("cost")) {
|
||||
formData.system.cost = MGT2Helper.getIntegerFromInput(formData.system.cost);
|
||||
}
|
||||
//console.log("before flatten");
|
||||
//console.log(formData);
|
||||
//console.log("after flatten");
|
||||
// let x = foundry.utils.flattenObject(formData);;
|
||||
// console.log(x);
|
||||
// return x;
|
||||
return foundry.utils.flattenObject(formData);
|
||||
}
|
||||
}
|
||||
61
src/module/item.js
Normal file
61
src/module/item.js
Normal file
@@ -0,0 +1,61 @@
|
||||
export class TravellerItem extends Item {
|
||||
|
||||
/** @inheritdoc */
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
|
||||
}
|
||||
|
||||
async _preUpdate(changed, options, user) {
|
||||
if ((await super._preUpdate(changed, options, user)) === false) return false;
|
||||
|
||||
if (this.type === "computer") {
|
||||
// Overload
|
||||
const newProcessing = foundry.utils.getProperty(changed, "system.processing") ?? this.system.processing;
|
||||
if (newProcessing !== this.system.processing) {
|
||||
let overload = this.system.processingUsed > newProcessing;
|
||||
foundry.utils.setProperty(changed, "system.overload", overload);
|
||||
}
|
||||
}
|
||||
|
||||
// Qty max 1
|
||||
if (this.type === "computer" || this.type === "container" || (this.type === "item" && this.system.subType === "software")) {
|
||||
const newQty = foundry.utils.getProperty(changed, "system.quantity") ?? this.system.quantity;
|
||||
if (newQty !== this.system.quantity && newQty > 1) {
|
||||
foundry.utils.setProperty(changed, "system.quantity", 1);
|
||||
}
|
||||
}
|
||||
|
||||
// No Weight
|
||||
if (this.type === "item" && this.system.subType === "software") {
|
||||
const newWeight = foundry.utils.getProperty(changed, "system.weight") ?? this.system.weight;
|
||||
if (newWeight !== this.system.weight && newWeight > 0) {
|
||||
foundry.utils.setProperty(changed, "system.weight", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getRollDisplay() {
|
||||
if (this.type === "talent") {
|
||||
if (this.system.subType === "skill") {
|
||||
let label;
|
||||
if (this.system.skill.speciality !== "" && this.system.skill.speciality !== undefined) {
|
||||
label = `${this.name} (${this.system.skill.speciality})`;
|
||||
} else {
|
||||
label = this.name;
|
||||
}
|
||||
|
||||
if (this.system.level > 0)
|
||||
label += ` (+${this.system.level})`;
|
||||
else if (this.system.level < 0)
|
||||
label += ` (${this.system.level})`;
|
||||
|
||||
return label;
|
||||
} else if (this.system.subType === "psionic") {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
98
src/module/models/character.mjs
Normal file
98
src/module/models/character.mjs
Normal file
@@ -0,0 +1,98 @@
|
||||
import { createCharacteristicField } from "./items/base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class CharacterData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
name: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
life: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
personal: new fields.SchemaField({
|
||||
title: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
species: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
speciesText: new fields.SchemaField({
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
descriptionLong: new fields.HTMLField({ required: false, blank: true, trim: true })
|
||||
}),
|
||||
age: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
gender: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
pronouns: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
homeworld: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
ucp: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
traits: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
)
|
||||
}),
|
||||
biography: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
|
||||
characteristics: new fields.SchemaField({
|
||||
strength: createCharacteristicField(true, true),
|
||||
dexterity: createCharacteristicField(true, true),
|
||||
endurance: createCharacteristicField(true, true),
|
||||
intellect: createCharacteristicField(true, false),
|
||||
education: createCharacteristicField(true, false),
|
||||
social: createCharacteristicField(true, false),
|
||||
morale: createCharacteristicField(true, false),
|
||||
luck: createCharacteristicField(true, false),
|
||||
sanity: createCharacteristicField(true, false),
|
||||
charm: createCharacteristicField(true, false),
|
||||
psionic: createCharacteristicField(true, false),
|
||||
other: createCharacteristicField(true, false)
|
||||
}),
|
||||
|
||||
health: new fields.SchemaField({
|
||||
radiations: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
lastFirstAidDate: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
healingRecoveryMode: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
}),
|
||||
study: new fields.SchemaField({
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
total: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
completed: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
}),
|
||||
finance: new fields.SchemaField({
|
||||
pension: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
credits: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
cashOnHand: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
debt: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
livingCost: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
monthlyShipPayments: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
notes: new fields.HTMLField({ required: false, blank: true, trim: true })
|
||||
}),
|
||||
containerView: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
containerDropIn: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
notes: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
|
||||
inventory: new fields.SchemaField({
|
||||
armor: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
weight: new fields.NumberField({ required: true, initial: 0, min: 0, integer: false }),
|
||||
encumbrance: new fields.SchemaField({
|
||||
normal: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
heavy: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true })
|
||||
})
|
||||
}),
|
||||
states: new fields.SchemaField({
|
||||
encumbrance: new fields.BooleanField({ required: false, initial: false }),
|
||||
fatigue: new fields.BooleanField({ required: false, initial: false }),
|
||||
unconscious: new fields.BooleanField({ required: false, initial: false }),
|
||||
surgeryRequired: new fields.BooleanField({ required: false, initial: false })
|
||||
}),
|
||||
|
||||
config: new fields.SchemaField({
|
||||
psionic: new fields.BooleanField({ required: false, initial: true }),
|
||||
initiative: new fields.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
damages: new fields.SchemaField({
|
||||
rank1: new fields.StringField({ required: false, blank: true, initial: "strength" }),
|
||||
rank2: new fields.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
rank3: new fields.StringField({ required: false, blank: true, initial: "endurance" })
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
97
src/module/models/creature.mjs
Normal file
97
src/module/models/creature.mjs
Normal file
@@ -0,0 +1,97 @@
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class CreatureData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
life: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, initial: 10, min: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: true, initial: 10, min: 0, integer: true })
|
||||
}),
|
||||
|
||||
speed: new fields.NumberField({ required: true, initial: 6, min: 0, integer: true }),
|
||||
|
||||
armor: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
|
||||
psi: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
|
||||
initiativeBonus: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
|
||||
skills: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true, initial: "" }),
|
||||
level: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
note: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
})
|
||||
),
|
||||
|
||||
attacks: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true, initial: "" }),
|
||||
damage: new fields.StringField({ required: true, blank: true, trim: true, initial: "1D" }),
|
||||
skill: new fields.NumberField({ required: false, initial: -1, integer: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
})
|
||||
),
|
||||
|
||||
traits: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true, initial: "" }),
|
||||
value: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
})
|
||||
),
|
||||
|
||||
behavior: new fields.SchemaField({
|
||||
type: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
subtype: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
}),
|
||||
|
||||
biography: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
notes: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
};
|
||||
}
|
||||
|
||||
/** @override */
|
||||
prepareDerivedData() {
|
||||
// Compute initiative bonus from Métabolisme traits
|
||||
let bonus = 0;
|
||||
for (const trait of this.traits) {
|
||||
const nameLower = trait.name.toLowerCase();
|
||||
if (nameLower.includes("métabolisme rapide") || nameLower.includes("metabolisme rapide")) {
|
||||
const val = parseInt(trait.value);
|
||||
if (!isNaN(val)) bonus += val;
|
||||
} else if (nameLower.includes("métabolisme lent") || nameLower.includes("metabolisme lent")) {
|
||||
const val = parseInt(trait.value);
|
||||
if (!isNaN(val)) bonus -= val;
|
||||
}
|
||||
}
|
||||
this.initiativeBonus = bonus;
|
||||
|
||||
// Compute armor from Armure trait if not set manually
|
||||
if (this.armor === 0) {
|
||||
for (const trait of this.traits) {
|
||||
if (trait.name.toLowerCase().startsWith("armure")) {
|
||||
const val = parseInt(trait.value);
|
||||
if (!isNaN(val)) {
|
||||
this.armor = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute PSI from Psionique trait
|
||||
if (this.psi === 0) {
|
||||
for (const trait of this.traits) {
|
||||
if (trait.name.toLowerCase().startsWith("psionique")) {
|
||||
const val = parseInt(trait.value);
|
||||
if (!isNaN(val)) {
|
||||
this.psi = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/module/models/index.mjs
Normal file
17
src/module/models/index.mjs
Normal file
@@ -0,0 +1,17 @@
|
||||
// Actor DataModels
|
||||
export { default as CharacterData } from "./character.mjs";
|
||||
export { default as VehiculeData } from "./vehicule.mjs";
|
||||
export { default as CreatureData } from "./creature.mjs";
|
||||
|
||||
// Item DataModels
|
||||
export { default as ItemData } from "./items/item.mjs";
|
||||
export { default as EquipmentData } from "./items/equipment.mjs";
|
||||
export { default as DiseaseData } from "./items/disease.mjs";
|
||||
export { default as CareerData } from "./items/career.mjs";
|
||||
export { default as TalentData } from "./items/talent.mjs";
|
||||
export { default as ContactData } from "./items/contact.mjs";
|
||||
export { default as WeaponData } from "./items/weapon.mjs";
|
||||
export { default as ArmorData } from "./items/armor.mjs";
|
||||
export { default as ComputerData } from "./items/computer.mjs";
|
||||
export { default as ItemContainerData } from "./items/container.mjs";
|
||||
export { default as SpeciesData } from "./items/species.mjs";
|
||||
23
src/module/models/items/armor.mjs
Normal file
23
src/module/models/items/armor.mjs
Normal file
@@ -0,0 +1,23 @@
|
||||
import { PhysicalItemData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class ArmorData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.radiations = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.protection = new fields.StringField({ required: false, blank: false, trim: true });
|
||||
// A Traveller suffers DM-1 to all checks per missing skill level in the required skill.
|
||||
schema.requireSkill = new fields.StringField({ required: false, blank: false });
|
||||
schema.requireSkillLevel = new fields.NumberField({ required: false, min: 0, integer: true });
|
||||
// Powered armour supports its own weight and is effectively weightless for encumbrance.
|
||||
schema.powered = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
41
src/module/models/items/base-item.mjs
Normal file
41
src/module/models/items/base-item.mjs
Normal file
@@ -0,0 +1,41 @@
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export function createCharacteristicField(show = true, showMax = false) {
|
||||
return new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
dm: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
show: new fields.BooleanField({ required: false, initial: show }),
|
||||
showMax: new fields.BooleanField({ required: false, initial: showMax })
|
||||
});
|
||||
}
|
||||
|
||||
export class ItemBaseData extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
return {
|
||||
description: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
subType: new fields.StringField({ required: false, blank: false, nullable: true })
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class PhysicalItemData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.quantity = new fields.NumberField({ required: true, initial: 1, min: 0, integer: true });
|
||||
schema.weight = new fields.NumberField({ required: true, initial: 0, min: 0, integer: false });
|
||||
schema.weightless = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.tl = new fields.StringField({ required: true, blank: false, initial: "TL12" });
|
||||
schema.container = new fields.SchemaField({
|
||||
id: new fields.StringField({ required: false, blank: true })
|
||||
});
|
||||
schema.roll = new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
difficulty: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
schema.trash = new fields.BooleanField({ required: false, initial: false });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
21
src/module/models/items/career.mjs
Normal file
21
src/module/models/items/career.mjs
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ItemBaseData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class CareerData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.difficulty = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.damage = new fields.StringField({ required: false, blank: true });
|
||||
schema.interval = new fields.StringField({ required: false, blank: true });
|
||||
schema.assignment = new fields.StringField({ required: false, blank: true });
|
||||
schema.terms = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.rank = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.events = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
age: new fields.NumberField({ required: false, integer: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
18
src/module/models/items/computer.mjs
Normal file
18
src/module/models/items/computer.mjs
Normal file
@@ -0,0 +1,18 @@
|
||||
import { PhysicalItemData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class ComputerData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.processing = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.processingUsed = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.overload = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
27
src/module/models/items/contact.mjs
Normal file
27
src/module/models/items/contact.mjs
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ItemBaseData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class ContactData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "skill";
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 1, min: 0, integer: true });
|
||||
schema.skill = new fields.SchemaField({
|
||||
speciality: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
schema.status = new fields.StringField({ required: false, blank: true, trim: true, initial: "Alive" });
|
||||
schema.attitude = new fields.StringField({ required: false, blank: true, trim: true, initial: "Unknow" });
|
||||
schema.relation = new fields.StringField({ required: false, blank: true, trim: true, initial: "Contact" });
|
||||
schema.title = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.nickname = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.species = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.gender = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.pronouns = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.homeworld = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.location = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.occupation = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.notes = new fields.HTMLField({ required: false, blank: true, trim: true });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
16
src/module/models/items/container.mjs
Normal file
16
src/module/models/items/container.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ItemBaseData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class ItemContainerData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.onHand = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.location = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.count = new fields.NumberField({ required: false, initial: 0, integer: true });
|
||||
schema.weight = new fields.NumberField({ required: false, initial: 0, integer: false });
|
||||
schema.weightless = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.locked = new fields.BooleanField({ required: false, initial: false }); // GM only
|
||||
schema.lockedDescription = new fields.HTMLField({ required: false, blank: true, trim: true });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
13
src/module/models/items/disease.mjs
Normal file
13
src/module/models/items/disease.mjs
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ItemBaseData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class DiseaseData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "disease"; // disease, poison
|
||||
schema.difficulty = new fields.StringField({ required: true, initial: "Average" });
|
||||
schema.damage = new fields.StringField({ required: false, blank: true });
|
||||
schema.interval = new fields.StringField({ required: false, blank: true });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
14
src/module/models/items/equipment.mjs
Normal file
14
src/module/models/items/equipment.mjs
Normal file
@@ -0,0 +1,14 @@
|
||||
import { PhysicalItemData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class EquipmentData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.augment = new fields.SchemaField({
|
||||
improvement: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
schema.subType.initial = "equipment"; // augment, clothing, trinket, toolkit, equipment
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
15
src/module/models/items/item.mjs
Normal file
15
src/module/models/items/item.mjs
Normal file
@@ -0,0 +1,15 @@
|
||||
import { PhysicalItemData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class ItemData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "loot";
|
||||
schema.software = new fields.SchemaField({
|
||||
bandwidth: new fields.NumberField({ required: false, initial: 0, min: 0, max: 10, integer: true }),
|
||||
effect: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
computerId: new fields.StringField({ required: false, blank: true, initial: "" })
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
22
src/module/models/items/species.mjs
Normal file
22
src/module/models/items/species.mjs
Normal file
@@ -0,0 +1,22 @@
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class SpeciesData extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
return {
|
||||
description: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
descriptionLong: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
traits: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
),
|
||||
modifiers: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
value: new fields.NumberField({ required: false, integer: true, nullable: true })
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
27
src/module/models/items/talent.mjs
Normal file
27
src/module/models/items/talent.mjs
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ItemBaseData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class TalentData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "skill";
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.level = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.skill = new fields.SchemaField({
|
||||
speciality: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
reduceEncumbrance: new fields.BooleanField({ required: false, initial: false })
|
||||
});
|
||||
schema.psionic = new fields.SchemaField({
|
||||
reach: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
cost: new fields.NumberField({ required: false, initial: 1, min: 0, integer: true }),
|
||||
duration: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
durationUnit: new fields.StringField({ required: false })
|
||||
});
|
||||
schema.roll = new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
difficulty: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
53
src/module/models/items/weapon.mjs
Normal file
53
src/module/models/items/weapon.mjs
Normal file
@@ -0,0 +1,53 @@
|
||||
import { PhysicalItemData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class WeaponData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.range = new fields.SchemaField({
|
||||
isMelee: new fields.BooleanField({ required: false, initial: false }),
|
||||
value: new fields.NumberField({ required: false, integer: true, nullable: true }),
|
||||
unit: new fields.StringField({ required: false, blank: true, nullable: true })
|
||||
});
|
||||
schema.damage = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.magazine = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.magazineCost = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.traits = new fields.SchemaField({
|
||||
ap: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
auto: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
blast: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
bulky: new fields.BooleanField({ required: false, initial: false }),
|
||||
veryBulky: new fields.BooleanField({ required: false, initial: false }),
|
||||
stun: new fields.BooleanField({ required: false, initial: false }),
|
||||
smart: new fields.BooleanField({ required: false, initial: false }),
|
||||
radiation: new fields.BooleanField({ required: false, initial: false }),
|
||||
scope: new fields.BooleanField({ required: false, initial: false }),
|
||||
zeroG: new fields.BooleanField({ required: false, initial: false })
|
||||
});
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
return schema;
|
||||
}
|
||||
|
||||
/** Returns a compact display string of active traits (e.g. "AP 2, Auto 3, Blast 5, Bulky") */
|
||||
static getTraitsSummary(traits) {
|
||||
if (!traits) return "";
|
||||
const parts = [];
|
||||
if (traits.ap > 0) parts.push(`AP ${traits.ap}`);
|
||||
if (traits.auto > 0) parts.push(`Auto ${traits.auto}`);
|
||||
if (traits.blast > 0) parts.push(`Blast ${traits.blast}`);
|
||||
if (traits.bulky) parts.push(game.i18n.localize("MGT2.WeaponTraits.Bulky"));
|
||||
if (traits.veryBulky) parts.push(game.i18n.localize("MGT2.WeaponTraits.VeryBulky"));
|
||||
if (traits.stun) parts.push(game.i18n.localize("MGT2.WeaponTraits.Stun"));
|
||||
if (traits.smart) parts.push(game.i18n.localize("MGT2.WeaponTraits.Smart"));
|
||||
if (traits.radiation) parts.push(game.i18n.localize("MGT2.WeaponTraits.Radiation"));
|
||||
if (traits.scope) parts.push(game.i18n.localize("MGT2.WeaponTraits.Scope"));
|
||||
if (traits.zeroG) parts.push(game.i18n.localize("MGT2.WeaponTraits.ZeroG"));
|
||||
return parts.join(", ");
|
||||
}
|
||||
}
|
||||
35
src/module/models/vehicule.mjs
Normal file
35
src/module/models/vehicule.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class VehiculeData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
name: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
skillId: new fields.StringField({ required: false, initial: "", blank: true, trim: true }),
|
||||
speed: new fields.SchemaField({
|
||||
cruise: new fields.StringField({ required: false, initial: "Slow", blank: true }),
|
||||
maximum: new fields.StringField({ required: false, initial: "Medium", blank: true })
|
||||
}),
|
||||
agility: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
crew: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
passengers: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
cargo: new fields.NumberField({ required: false, min: 0, integer: false }),
|
||||
life: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
shipping: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
cost: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
armor: new fields.SchemaField({
|
||||
front: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
rear: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
sides: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
skills: new fields.SchemaField({
|
||||
autopilot: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
description: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
notes: new fields.HTMLField({ required: false, blank: true, trim: true })
|
||||
};
|
||||
}
|
||||
}
|
||||
84
src/module/roll-prompt.js
Normal file
84
src/module/roll-prompt.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const { DialogV2 } = foundry.applications.api;
|
||||
const { renderTemplate } = foundry.applications.handlebars;
|
||||
const { FormDataExtended } = foundry.applications.ux;
|
||||
|
||||
export class RollPromptHelper {
|
||||
|
||||
static async roll(options) {
|
||||
// Backward compat: allow (actor, options) or just (options)
|
||||
if (options.rollTypeName || options.characteristics || options.skill !== undefined) {
|
||||
// Normal call with options
|
||||
} else {
|
||||
// Called with (actor, options)
|
||||
options = arguments[1] || options;
|
||||
}
|
||||
|
||||
const htmlContent = await renderTemplate('systems/mgt2/templates/roll-prompt.html', {
|
||||
config: CONFIG.MGT2,
|
||||
// Character-mode fields
|
||||
characteristics: options.characteristics ?? [],
|
||||
characteristic: options.characteristic ?? "",
|
||||
skills: options.skills ?? [],
|
||||
skill: options.skill ?? "",
|
||||
fatigue: options.fatigue ?? false,
|
||||
encumbrance: options.encumbrance ?? false,
|
||||
difficulty: options.difficulty ?? "Average",
|
||||
timeframe: options.timeframe ?? "Normal",
|
||||
customDM: options.customDM ?? "0",
|
||||
rollMode: options.rollMode ?? "publicroll",
|
||||
// Creature-mode flags
|
||||
isCreature: options.isCreature ?? false,
|
||||
creatureSkills: options.creatureSkills ?? [],
|
||||
selectedSkillIndex: options.selectedSkillIndex ?? -1,
|
||||
showSkillSelector: options.showSkillSelector ?? false,
|
||||
skillName: options.skillName ?? "",
|
||||
skillLevel: options.skillLevel ?? 0,
|
||||
// Healing fields
|
||||
showHeal: options.showHeal ?? false,
|
||||
healType: options.healType ?? null,
|
||||
// Ranged/Melee weapon flags
|
||||
isRanged: options.isRanged ?? false,
|
||||
isMelee: options.isMelee ?? false,
|
||||
isAttack: (options.isRanged ?? false) || (options.isMelee ?? false),
|
||||
autoLevel: options.autoLevel ?? 0,
|
||||
hasScope: options.scope ?? false,
|
||||
hasZeroG: options.zeroG ?? false,
|
||||
});
|
||||
|
||||
return await DialogV2.wait({
|
||||
window: { title: options.title ?? options.rollTypeName ?? game.i18n.localize("MGT2.RollPrompt.Roll") },
|
||||
classes: ["mgt2-roll-dialog"],
|
||||
content: htmlContent,
|
||||
rejectClose: false,
|
||||
buttons: [
|
||||
{
|
||||
action: "boon",
|
||||
label: game.i18n.localize("MGT2.RollPrompt.Boon"),
|
||||
callback: (event, button, dialog) => {
|
||||
const formData = new FormDataExtended(dialog.element.querySelector('form')).object;
|
||||
formData.diceModifier = "dl";
|
||||
return formData;
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "submit",
|
||||
label: game.i18n.localize("MGT2.RollPrompt.Roll"),
|
||||
icon: '<i class="fa-solid fa-dice"></i>',
|
||||
default: true,
|
||||
callback: (event, button, dialog) => {
|
||||
return new FormDataExtended(dialog.element.querySelector('form')).object;
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "bane",
|
||||
label: game.i18n.localize("MGT2.RollPrompt.Bane"),
|
||||
callback: (event, button, dialog) => {
|
||||
const formData = new FormDataExtended(dialog.element.querySelector('form')).object;
|
||||
formData.diceModifier = "dh";
|
||||
return formData;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
86
src/module/settings.js
Normal file
86
src/module/settings.js
Normal file
@@ -0,0 +1,86 @@
|
||||
export const registerSettings = function () {
|
||||
|
||||
game.settings.register("mgt2", "theme", {
|
||||
name: "MGT2.Settings.theme.name",
|
||||
hint: "MGT2.Settings.theme.hint",
|
||||
scope: "client",
|
||||
config: true,
|
||||
default: "black-and-red",
|
||||
type: String,
|
||||
choices: {
|
||||
"black-and-red": "MGT2.Themes.BlackAndRed",
|
||||
"mwamba": "MGT2.Themes.Mwamba",
|
||||
"blue": "MGT2.Themes.Blue"
|
||||
},
|
||||
requiresReload: true
|
||||
});
|
||||
|
||||
game.settings.register('mgt2', 'usePronouns', {
|
||||
name: "MGT2.Settings.usePronouns.name",
|
||||
hint: "MGT2.Settings.usePronouns.hint",
|
||||
default: false,
|
||||
scope: 'world',
|
||||
type: Boolean,
|
||||
config: true,
|
||||
requiresReload: false
|
||||
});
|
||||
|
||||
game.settings.register('mgt2', 'useGender', {
|
||||
name: "MGT2.Settings.useGender.name",
|
||||
hint: "MGT2.Settings.useGender.hint",
|
||||
default: false,
|
||||
scope: 'world',
|
||||
type: Boolean,
|
||||
config: true,
|
||||
requiresReload: false
|
||||
});
|
||||
|
||||
game.settings.register('mgt2', 'showLife', {
|
||||
name: "MGT2.Settings.showLife.name",
|
||||
hint: "MGT2.Settings.showLife.hint",
|
||||
default: false,
|
||||
scope: 'world',
|
||||
type: Boolean,
|
||||
config: true,
|
||||
requiresReload: false
|
||||
});
|
||||
|
||||
// game.settings.register('mgt2', 'useWeightMetric', {
|
||||
// name: "MGT2.Settings.useWeightMetric.name",
|
||||
// hint: "MGT2.Settings.useWeightMetric.hint",
|
||||
// default: true,
|
||||
// scope: 'world',
|
||||
// type: Boolean,
|
||||
// config: true,
|
||||
// requiresReload: true
|
||||
// });
|
||||
|
||||
// game.settings.register('mgt2', 'useDistanceMetric', {
|
||||
// name: "MGT2.Settings.useDistanceMetric.name",
|
||||
// hint: "MGT2.Settings.useDistanceMetric.hint",
|
||||
// default: true,
|
||||
// scope: 'world',
|
||||
// type: Boolean,
|
||||
// config: true,
|
||||
// requiresReload: true
|
||||
// });
|
||||
|
||||
// game.settings.register('mgt2', 'showTrash', {
|
||||
// name: "Show Trash tab to Player",
|
||||
// hint: "Player can see the Trash tab and recover item",
|
||||
// default: false,
|
||||
// scope: 'world',
|
||||
// type: Boolean,
|
||||
// config: true,
|
||||
// requiresReload: false
|
||||
// });
|
||||
|
||||
/*game.settings.register('mgt2', 'containerDropIn', {
|
||||
name: "Test",
|
||||
hint: "Mon hint",
|
||||
default: true,
|
||||
scope: 'client',
|
||||
type: Boolean,
|
||||
config: true
|
||||
});*/
|
||||
};
|
||||
37
src/module/templates.js
Normal file
37
src/module/templates.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Define a set of template paths to pre-load
|
||||
* Pre-loaded templates are compiled and cached for fast access when rendering
|
||||
* @return {Promise}
|
||||
*/
|
||||
export const preloadHandlebarsTemplates = async function() {
|
||||
|
||||
const templatePaths = [
|
||||
"systems/mgt2/templates/items/armor-sheet.html",
|
||||
"systems/mgt2/templates/items/career-sheet.html",
|
||||
"systems/mgt2/templates/items/computer-sheet.html",
|
||||
"systems/mgt2/templates/items/contact-sheet.html",
|
||||
"systems/mgt2/templates/items/container-sheet.html",
|
||||
"systems/mgt2/templates/items/disease-sheet.html",
|
||||
"systems/mgt2/templates/items/equipment-sheet.html",
|
||||
"systems/mgt2/templates/items/item-sheet.html",
|
||||
"systems/mgt2/templates/items/species-sheet.html",
|
||||
"systems/mgt2/templates/items/talent-sheet.html",
|
||||
"systems/mgt2/templates/items/weapon-sheet.html",
|
||||
"systems/mgt2/templates/items/parts/sheet-configuration.html",
|
||||
"systems/mgt2/templates/items/parts/sheet-physical-item.html",
|
||||
"systems/mgt2/templates/items/parts/sheet-physical-item-tab.html",
|
||||
"systems/mgt2/templates/roll-prompt.html",
|
||||
"systems/mgt2/templates/chat/roll.html",
|
||||
//"systems/mgt2/templates/chat/roll-characteristic.html",
|
||||
"systems/mgt2/templates/chat/radiation.html",
|
||||
"systems/mgt2/templates/actors/actor-config-sheet.html",
|
||||
"systems/mgt2/templates/actors/actor-config-characteristic-sheet.html",
|
||||
"systems/mgt2/templates/actors/trait-sheet.html",
|
||||
"systems/mgt2/templates/actors/creature-sheet.html",
|
||||
"systems/mgt2/templates/chat/creature-roll.html",
|
||||
"systems/mgt2/templates/editor-fullview.html"
|
||||
];
|
||||
|
||||
const loader = foundry.applications?.handlebars?.loadTemplates ?? loadTemplates;
|
||||
return loader(templatePaths);
|
||||
};
|
||||
292
src/sass/components/_character.sass
Normal file
292
src/sass/components/_character.sass
Normal file
@@ -0,0 +1,292 @@
|
||||
.characteristics-panel
|
||||
.tab
|
||||
padding: 4px
|
||||
|
||||
.species
|
||||
font-size: 13px
|
||||
margin: 4px 1rem 0 1rem
|
||||
text-align: justify
|
||||
a
|
||||
margin-right: 1rem
|
||||
|
||||
.mgt2
|
||||
.characteristics
|
||||
flex-basis: 138px
|
||||
flex-grow: 0
|
||||
flex-shrink: 0
|
||||
position: relative
|
||||
|
||||
.characteristics-header
|
||||
color: var(--mgt2-color-primary)
|
||||
background: var(--mgt2-bgcolor-primary)
|
||||
font-family: "Rubik", monospace
|
||||
font-style: normal
|
||||
font-size: 1rem
|
||||
line-height: 2rem
|
||||
text-transform: uppercase
|
||||
|
||||
.characteristic-row
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: center
|
||||
justify-content: center
|
||||
position: relative
|
||||
|
||||
.characteristic-minmax
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: center
|
||||
justify-content: center
|
||||
flex-wrap: nowrap
|
||||
|
||||
.characteristic-dm
|
||||
span
|
||||
&.label
|
||||
font-size: 0.8rem
|
||||
font-weight: 600
|
||||
|
||||
.characteristic-label
|
||||
font-family: "Barlow Condensed", sans-serif
|
||||
font-size: 1.2rem
|
||||
font-weight: 600
|
||||
font-style: italic
|
||||
text-align: center
|
||||
color: var(--mgt2-color-form)
|
||||
position: relative
|
||||
& > a
|
||||
&.roll
|
||||
color: black
|
||||
position: absolute
|
||||
left: 0
|
||||
a
|
||||
&.cfg-characteristic
|
||||
display: none
|
||||
font-size: 12px
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
&:hover
|
||||
a
|
||||
&.cfg-characteristic
|
||||
display: block
|
||||
|
||||
.characteristic-input
|
||||
color: var(--mgt2-bgcolor-primary)
|
||||
text-align: center
|
||||
font-size: 1.5rem
|
||||
font-weight: 500
|
||||
width: 2.4rem
|
||||
height: 2rem
|
||||
box-sizing: border-box
|
||||
border: none
|
||||
outline: none
|
||||
background: linear-gradient(45deg, #0000 5.66px, #000 0 calc(5.66px + 2px), #0000 0 calc(100% - 5.66px - 2px), #000 0 calc(100% - 5.66px), #0000 0), linear-gradient(-45deg, #0000 5.66px, #000 0 calc(5.66px + 2px), #0000 0 calc(100% - 5.66px - 2px), #000 0 calc(100% - 5.66px), #0000 0), linear-gradient(90deg, #000 4px, #0000 0) -2px 50%/100% calc(100% - 16px) repeat-x, linear-gradient(#000 4px, #0000 0) 50% -2px/calc(100% - 16px) 100% repeat-y
|
||||
|
||||
.characteristic-dm
|
||||
color: var(--mgt2-color-dm)
|
||||
background-color: transparent
|
||||
text-align: center
|
||||
font-size: 1rem
|
||||
width: 1.5rem
|
||||
height: 1.4rem
|
||||
padding: 0
|
||||
outline: none
|
||||
border: none
|
||||
margin: 0
|
||||
position: absolute
|
||||
right: 0
|
||||
background-color: var(--mgt2-bgcolor-dm)
|
||||
border-radius: 9px
|
||||
margin-right: 2px
|
||||
&:focus
|
||||
outline: none
|
||||
box-shadow: none
|
||||
|
||||
.characteristic-dm-minmax
|
||||
&:focus
|
||||
outline: none
|
||||
box-shadow: none
|
||||
|
||||
.minmaxwrapper
|
||||
display: flex
|
||||
flex-direction: row
|
||||
flex-wrap: nowrap
|
||||
justify-content: center
|
||||
justify-content: center
|
||||
align-items: center
|
||||
margin: 0 0.5rem
|
||||
box-sizing: border-box
|
||||
background: linear-gradient(45deg, #0000 7.07px, #000 0 calc(7.07px + 2px), #0000 0 calc(100% - 7.07px - 2px), #000 0 calc(100% - 7.07px), #0000 0), linear-gradient(-45deg, #0000 7.07px, #000 0 calc(7.07px + 2px), #0000 0 calc(100% - 7.07px - 2px), #000 0 calc(100% - 7.07px), #0000 0), linear-gradient(90deg, #000 4px, #0000 0) -2px 50%/100% calc(100% - 20px) repeat-x, linear-gradient(#000 4px, #0000 0) 50% -2px/calc(100% - 20px) 100% repeat-y
|
||||
input
|
||||
display: inline-block
|
||||
color: black
|
||||
background-color: transparent
|
||||
text-align: center
|
||||
font-size: 1.5rem
|
||||
width: 3rem
|
||||
height: 2rem
|
||||
border: none
|
||||
outline: none
|
||||
&:focus
|
||||
outline: none
|
||||
box-shadow: none
|
||||
&:first-child
|
||||
margin-left: 5px
|
||||
&:last-child
|
||||
margin-right: 5px
|
||||
span
|
||||
font-size: 1.5rem
|
||||
font-weight: 500
|
||||
.computer-overload,
|
||||
.computer-overload i
|
||||
color: var(--mgt2-color-warning) !important
|
||||
ul
|
||||
&.softwares
|
||||
list-style: none
|
||||
margin: 0
|
||||
padding: 0
|
||||
li
|
||||
display: inline-block
|
||||
color: var(--mgt2-color-software)
|
||||
background-color: var(--mgt2-bgcolor-software)
|
||||
padding: 3px 7px
|
||||
border-radius: 8px
|
||||
a
|
||||
display: none
|
||||
&:first-child
|
||||
margin: 0 0.5rem
|
||||
&:hover
|
||||
a
|
||||
display: inline-block
|
||||
|
||||
.character-header
|
||||
display: flex
|
||||
margin-top: 8px
|
||||
margin-right: 8px
|
||||
flex-direction: row
|
||||
flex-wrap: nowrap
|
||||
flex-grow: 0
|
||||
flex-shrink: 0
|
||||
justify-content: flex-start
|
||||
align-items: flex-start
|
||||
.character-header-img
|
||||
flex-basis: 138px
|
||||
flex-grow: 0
|
||||
flex-shrink: 0
|
||||
text-align: center
|
||||
.character-summary
|
||||
flex: 0 0 100%
|
||||
margin: 0
|
||||
padding: 0
|
||||
list-style: none
|
||||
border-top: 5px double var(--mgt2-bgcolor-primary)
|
||||
li
|
||||
float: left
|
||||
margin: 0
|
||||
padding: 0
|
||||
color: var(--mgt2-color-form)
|
||||
input
|
||||
display: block
|
||||
border: none
|
||||
font-weight: bold
|
||||
font-family: "Roboto Condensed", sans-serif
|
||||
background-color: #fff
|
||||
color: var(--mgt2-color-form)
|
||||
font-size: 0.8rem
|
||||
border: 1px solid #fff
|
||||
&:hover
|
||||
border: 1px solid #111
|
||||
.character-header-body
|
||||
display: flex
|
||||
flex-direction: column
|
||||
flex-wrap: nowrap
|
||||
justify-content: flex-start
|
||||
legend
|
||||
font-size: 0.7rem
|
||||
text-transform: uppercase
|
||||
text-wrap: nowrap
|
||||
color: var(--mgt2-color-form)
|
||||
i
|
||||
margin-right: 0.25rem
|
||||
.character-body
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-content: flex-start
|
||||
flex-wrap: nowrap
|
||||
min-height: 330px
|
||||
.tab
|
||||
width: 100%
|
||||
|
||||
|
||||
.lifes
|
||||
height: 100%
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: center
|
||||
align-items: center
|
||||
div
|
||||
font-size: 2rem
|
||||
.character-states
|
||||
margin: 0
|
||||
padding: 0
|
||||
list-style: none
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: flex-start
|
||||
align-items: flex-start
|
||||
width: 100%
|
||||
float: right
|
||||
li
|
||||
display: flex
|
||||
margin: 0
|
||||
padding: 0
|
||||
color: var(--mgt2-color-form)
|
||||
justify-content: space-between
|
||||
align-items: center
|
||||
width: 100%
|
||||
font-size: 0.7rem
|
||||
line-height: 1.1rem
|
||||
|
||||
.encumbrance-normal
|
||||
color: var(--mgt2-encumbrance-normal)!important
|
||||
|
||||
.encumbrance-heavy
|
||||
color: var(--mgt2-encumbrance-heavy)!important
|
||||
font-weight: bold
|
||||
|
||||
.character-body
|
||||
height: 100%
|
||||
overflow: hidden
|
||||
display: flex
|
||||
flex-direction: row
|
||||
width: 100%
|
||||
justify-content: flex-start
|
||||
align-items: flex-start
|
||||
border-top: 3px solid black
|
||||
|
||||
.actor-footer
|
||||
bottom: 0
|
||||
color: var(--mgt2-color-primary)
|
||||
background-color: var(--mgt2-bgcolor-primary)
|
||||
width: 100%
|
||||
margin: 0 -8px
|
||||
height: 1.5rem
|
||||
justify-content: space-between
|
||||
align-items: center
|
||||
padding: 0 1rem
|
||||
flex-grow: 0
|
||||
flex-shrink: 0
|
||||
display: flex
|
||||
flex-direction: row
|
||||
|
||||
|
||||
// ── HTMLField editor min-height in notes/biography/finance tabs ──────────────
|
||||
.mgt2.character
|
||||
.tab[data-tab="notes"],
|
||||
.tab[data-tab="biography"],
|
||||
.tab[data-tab="finance"]
|
||||
.editor,
|
||||
.editor-content,
|
||||
prose-mirror
|
||||
min-height: 300px !important
|
||||
height: auto !important
|
||||
482
src/sass/components/_chat-sidebar.sass
Normal file
482
src/sass/components/_chat-sidebar.sass
Normal file
@@ -0,0 +1,482 @@
|
||||
// ─── MGT2 Chat Roll Cards ──────────────────────────────────────────────────
|
||||
|
||||
// Light theme for ALL chat messages - matching character sheet
|
||||
li.chat-message
|
||||
background: #ffffff !important
|
||||
border: 1px solid #ccbbbb !important
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.18) !important
|
||||
color: #0A0405
|
||||
padding: 0 !important
|
||||
|
||||
.message-header
|
||||
background: #0A0405
|
||||
border-bottom: 3px solid #EE4050
|
||||
padding: 4px 10px
|
||||
|
||||
.message-sender
|
||||
color: #EE4050
|
||||
font-family: 'Barlow Condensed', sans-serif
|
||||
font-weight: 700
|
||||
font-size: 0.75rem
|
||||
text-transform: uppercase
|
||||
letter-spacing: 1.5px
|
||||
margin: 0
|
||||
|
||||
.message-timestamp
|
||||
color: #888888
|
||||
font-size: 0.68rem
|
||||
|
||||
.message-delete i
|
||||
color: #888888
|
||||
|
||||
&:hover .message-delete i
|
||||
color: #EE4050
|
||||
|
||||
// Plain Foundry roll cards (without .mgt2-chat-roll wrapper)
|
||||
.message-content > .dice-roll
|
||||
background: #fdf8f8
|
||||
padding: 6px 12px
|
||||
|
||||
.dice-formula
|
||||
background: #f5eeee
|
||||
border: 1px solid #ddc8c8
|
||||
border-radius: 3px
|
||||
color: #664444
|
||||
font-size: 0.78rem
|
||||
padding: 2px 10px
|
||||
text-align: center
|
||||
font-family: 'Barlow Condensed', sans-serif
|
||||
display: inline-block
|
||||
margin: 0 auto 4px auto
|
||||
|
||||
.dice-result
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
gap: 3px
|
||||
|
||||
h4.dice-total
|
||||
font-family: 'Barlow Condensed', sans-serif
|
||||
font-size: 2.2rem !important
|
||||
font-weight: 900
|
||||
color: #0A0405 !important
|
||||
margin: 2px 0
|
||||
line-height: 1
|
||||
text-align: center
|
||||
background: transparent !important
|
||||
width: 100% !important
|
||||
display: block !important
|
||||
|
||||
.dice-tooltip
|
||||
.part-header
|
||||
background: #f5eeee
|
||||
padding: 2px 8px
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
.part-formula, .part-total
|
||||
color: #EE4050
|
||||
font-weight: 700
|
||||
font-size: 0.78rem
|
||||
.dice-rolls
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
justify-content: center
|
||||
gap: 4px
|
||||
padding: 6px 8px
|
||||
list-style: none
|
||||
margin: 0
|
||||
.roll.die
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
width: 26px
|
||||
height: 26px
|
||||
background: #f5eeee
|
||||
border: 1.5px solid #ddc8c8
|
||||
border-radius: 4px
|
||||
color: #3a2020
|
||||
font-weight: 700
|
||||
font-size: 0.9rem
|
||||
|
||||
// ─── Inner roll card structure ────────────────────────────────────────────
|
||||
|
||||
.mgt2-chat-roll
|
||||
font-family: 'Barlow Condensed', sans-serif
|
||||
|
||||
// ── Header: characteristic name + roll type ──
|
||||
.mgt2-roll-header
|
||||
background: #0A0405 !important
|
||||
border-left: 4px solid #EE4050 !important
|
||||
padding: 5px 10px 4px 10px !important
|
||||
|
||||
.mgt2-roll-char-name
|
||||
display: block
|
||||
color: #ffffff
|
||||
font-size: 1.2rem
|
||||
font-weight: 800
|
||||
text-transform: uppercase
|
||||
letter-spacing: 1px
|
||||
line-height: 1
|
||||
|
||||
.mgt2-roll-meta
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: 6px
|
||||
margin-top: 2px
|
||||
|
||||
.mgt2-roll-type
|
||||
color: #EE4050
|
||||
font-size: 0.68rem
|
||||
font-weight: 700
|
||||
text-transform: uppercase
|
||||
letter-spacing: 2px
|
||||
|
||||
.mgt2-roll-sep
|
||||
color: #888888
|
||||
|
||||
.mgt2-roll-difficulty
|
||||
color: #bbbbbb
|
||||
font-size: 0.68rem
|
||||
text-transform: uppercase
|
||||
letter-spacing: 1px
|
||||
|
||||
// ── Modifier line ──
|
||||
.mgt2-roll-modifier
|
||||
background: #f5eeee
|
||||
border-bottom: 1px solid #e8dada
|
||||
padding: 3px 14px
|
||||
color: #664444
|
||||
font-size: 0.8rem
|
||||
|
||||
.mgt2-roll-modifiers
|
||||
display: flex
|
||||
gap: 6px
|
||||
flex-wrap: wrap
|
||||
background: #f5eeee
|
||||
border-bottom: 1px solid #e8dada
|
||||
padding: 3px 14px
|
||||
|
||||
.mgt2-roll-mod-tag
|
||||
color: #664444
|
||||
font-size: 0.8rem
|
||||
|
||||
// ── Dice block (Foundry .dice-roll structure preserved for tooltip click) ──
|
||||
.dice-roll
|
||||
background: #fdf8f8
|
||||
padding: 4px 10px
|
||||
cursor: pointer
|
||||
|
||||
.dice-flavor
|
||||
color: #888
|
||||
font-size: 0.73rem
|
||||
text-align: center
|
||||
margin-bottom: 3px
|
||||
|
||||
.dice-result
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
gap: 3px
|
||||
|
||||
.dice-formula
|
||||
background: #f5eeee
|
||||
border: 1px solid #ddc8c8
|
||||
border-radius: 3px
|
||||
color: #664444
|
||||
font-size: 0.78rem
|
||||
padding: 2px 14px
|
||||
font-family: 'DM Sans', sans-serif
|
||||
text-align: center
|
||||
|
||||
// Tooltip (individual dice — shown on click by Foundry)
|
||||
.dice-tooltip
|
||||
width: 100%
|
||||
|
||||
.wrapper
|
||||
background: transparent
|
||||
|
||||
.tooltip-part
|
||||
background: #fdf8f8
|
||||
border: 1px solid #e8dada
|
||||
border-radius: 4px
|
||||
margin-bottom: 6px
|
||||
overflow: hidden
|
||||
|
||||
.part-header
|
||||
background: #f5eeee
|
||||
padding: 3px 8px
|
||||
|
||||
.part-formula, .part-total
|
||||
color: #EE4050
|
||||
font-weight: 700
|
||||
font-size: 0.8rem
|
||||
|
||||
.dice-rolls
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
justify-content: center
|
||||
gap: 5px
|
||||
padding: 8px 10px
|
||||
list-style: none
|
||||
margin: 0
|
||||
|
||||
.roll.die
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
width: 30px
|
||||
height: 30px
|
||||
background: #f5eeee
|
||||
border: 1.5px solid #ddc8c8
|
||||
border-radius: 5px
|
||||
color: #3a2020
|
||||
font-weight: 700
|
||||
font-size: 1rem
|
||||
font-family: 'Barlow Condensed', sans-serif
|
||||
|
||||
&.max
|
||||
border-color: #EE4050
|
||||
color: #EE4050
|
||||
background: #fde8ea
|
||||
box-shadow: 0 0 8px rgba(238,64,80,0.2)
|
||||
|
||||
&.min
|
||||
border-color: #ccbbbb
|
||||
color: #999999
|
||||
|
||||
// ── Total: prominent number ──
|
||||
h4.dice-total
|
||||
font-size: 1.6rem !important
|
||||
font-weight: 900
|
||||
color: #0A0405 !important
|
||||
margin: 3px 0 2px 0
|
||||
line-height: 1
|
||||
font-family: 'Barlow Condensed', sans-serif
|
||||
text-align: center
|
||||
text-shadow: none
|
||||
background: #f5eeee !important
|
||||
border: 1px solid #ddc8c8 !important
|
||||
border-radius: 4px !important
|
||||
padding: 2px 14px !important
|
||||
width: auto !important
|
||||
display: inline-block !important
|
||||
min-width: 60px
|
||||
|
||||
&.success
|
||||
color: #1a8840 !important
|
||||
background: rgba(82,232,122,0.1) !important
|
||||
border-color: rgba(26,136,64,0.35) !important
|
||||
|
||||
&.failure
|
||||
color: #EE4050 !important
|
||||
background: rgba(238,64,80,0.07) !important
|
||||
border-color: rgba(238,64,80,0.3) !important
|
||||
|
||||
// ── Outcome badge ──
|
||||
.mgt2-outcome
|
||||
text-align: center
|
||||
font-size: 0.75rem
|
||||
font-weight: 700
|
||||
text-transform: uppercase
|
||||
letter-spacing: 2px
|
||||
padding: 3px 10px
|
||||
|
||||
i
|
||||
margin-right: 5px
|
||||
|
||||
&.is-success
|
||||
background: rgba(26,136,64,0.08)
|
||||
color: #1a8840
|
||||
border-top: 1px solid rgba(26,136,64,0.2)
|
||||
|
||||
&.is-failure
|
||||
background: rgba(238,64,80,0.07)
|
||||
color: #EE4050
|
||||
border-top: 1px solid rgba(238,64,80,0.2)
|
||||
|
||||
// ── Effect line ──
|
||||
.mgt2-effect
|
||||
text-align: center
|
||||
font-size: 0.72rem
|
||||
font-weight: 600
|
||||
letter-spacing: 1.5px
|
||||
text-transform: uppercase
|
||||
padding: 2px 10px 4px 10px
|
||||
border-bottom: 2px solid transparent
|
||||
|
||||
.mgt2-effect-value
|
||||
font-size: 1rem
|
||||
font-weight: 900
|
||||
margin-left: 4px
|
||||
|
||||
.mgt2-healing-amount
|
||||
font-size: 0.75rem
|
||||
font-weight: 600
|
||||
margin-left: 6px
|
||||
opacity: 0.85
|
||||
|
||||
&.is-success
|
||||
color: #1a8840
|
||||
border-bottom-color: rgba(26,136,64,0.25)
|
||||
|
||||
.mgt2-effect-value
|
||||
color: #1a8840
|
||||
|
||||
&.is-failure
|
||||
color: #EE4050
|
||||
border-bottom-color: rgba(238,64,80,0.25)
|
||||
|
||||
.mgt2-effect-value
|
||||
color: #EE4050
|
||||
|
||||
// ── AP info badge ──
|
||||
.mgt2-ap-info
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
gap: 5px
|
||||
font-size: 0.75rem
|
||||
font-weight: 600
|
||||
color: #6b4e1a
|
||||
background: rgba(180,130,40,0.12)
|
||||
border: 1px solid rgba(180,130,40,0.3)
|
||||
border-radius: 4px
|
||||
padding: 3px 8px
|
||||
margin: 4px 8px 0 8px
|
||||
i
|
||||
color: #b48228
|
||||
|
||||
// ── Auto fire mode info badge ──
|
||||
.mgt2-auto-info
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
gap: 5px
|
||||
font-size: 0.75rem
|
||||
font-weight: 600
|
||||
color: #1a3f6b
|
||||
background: rgba(40,90,180,0.10)
|
||||
border: 1px solid rgba(40,90,180,0.25)
|
||||
border-radius: 4px
|
||||
padding: 3px 8px
|
||||
margin: 4px 8px 0 8px
|
||||
i
|
||||
color: #2855b4
|
||||
|
||||
// ── Blast / explosion area info badge ──
|
||||
.mgt2-blast-info
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
gap: 5px
|
||||
font-size: 0.75rem
|
||||
font-weight: 600
|
||||
color: #7a2a00
|
||||
background: rgba(210,80,20,0.10)
|
||||
border: 1px solid rgba(210,80,20,0.30)
|
||||
border-radius: 4px
|
||||
padding: 3px 8px
|
||||
margin: 4px 8px 0 8px
|
||||
i
|
||||
color: #d24a10
|
||||
|
||||
// ── Stun / incapacitating weapon info badge ──
|
||||
.mgt2-stun-info
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
gap: 5px
|
||||
font-size: 0.75rem
|
||||
font-weight: 600
|
||||
color: #4a1a6b
|
||||
background: rgba(120,40,180,0.10)
|
||||
border: 1px solid rgba(120,40,180,0.28)
|
||||
border-radius: 4px
|
||||
padding: 3px 8px
|
||||
margin: 4px 8px 0 8px
|
||||
i
|
||||
color: #8a28c8
|
||||
|
||||
// ── Radiation weapon info badge ──
|
||||
.mgt2-radiation-info
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
gap: 5px
|
||||
font-size: 0.75rem
|
||||
font-weight: 600
|
||||
color: #1a4a1a
|
||||
background: rgba(40,160,40,0.10)
|
||||
border: 1px solid rgba(40,160,40,0.30)
|
||||
border-radius: 4px
|
||||
padding: 3px 8px
|
||||
margin: 4px 8px 0 8px
|
||||
i
|
||||
color: #28a028
|
||||
|
||||
// ── Radiation roll result card ──
|
||||
.mgt2-radiation-card
|
||||
.mgt2-radiation-label
|
||||
color: #28a028
|
||||
font-weight: 700
|
||||
i
|
||||
margin-right: 4px
|
||||
.mgt2-radiation-rules
|
||||
font-size: 0.72rem
|
||||
color: #555
|
||||
background: rgba(40,160,40,0.07)
|
||||
border: 1px solid rgba(40,160,40,0.20)
|
||||
border-radius: 4px
|
||||
padding: 4px 8px
|
||||
margin: 4px 8px 0 8px
|
||||
display: flex
|
||||
gap: 5px
|
||||
align-items: flex-start
|
||||
i
|
||||
color: #e0a020
|
||||
margin-top: 2px
|
||||
flex-shrink: 0
|
||||
|
||||
// ── Action buttons ──
|
||||
.mgt2-buttons
|
||||
display: flex
|
||||
justify-content: center
|
||||
flex-wrap: wrap
|
||||
gap: 4px
|
||||
padding: 5px 10px
|
||||
background: #f5eeee
|
||||
border-top: 1px solid #ddc8c8
|
||||
|
||||
button
|
||||
background: #ffffff
|
||||
border: 1px solid #ccbbbb
|
||||
color: #3a2020
|
||||
border-radius: 3px
|
||||
padding: 4px 14px
|
||||
font-family: 'Barlow Condensed', sans-serif
|
||||
font-size: 0.78rem
|
||||
font-weight: 700
|
||||
text-transform: uppercase
|
||||
letter-spacing: 1px
|
||||
cursor: pointer
|
||||
transition: background 0.15s ease, box-shadow 0.15s ease
|
||||
box-shadow: none
|
||||
|
||||
i
|
||||
font-size: 1rem
|
||||
padding: 0
|
||||
margin: 0
|
||||
|
||||
&:hover
|
||||
background: #EE4050
|
||||
border-color: #EE4050
|
||||
color: #fff
|
||||
box-shadow: 0 0 8px rgba(238,64,80,0.25)
|
||||
|
||||
&:disabled
|
||||
background: #e8e0e0
|
||||
border-color: #c8b8b8
|
||||
color: #a08080
|
||||
cursor: not-allowed
|
||||
box-shadow: none
|
||||
opacity: 0.6
|
||||
225
src/sass/components/_creature.sass
Normal file
225
src/sass/components/_creature.sass
Normal file
@@ -0,0 +1,225 @@
|
||||
// ─────────────────────────────────────────────────
|
||||
// Creature Sheet Styles
|
||||
// ─────────────────────────────────────────────────
|
||||
|
||||
.creature-sheet
|
||||
|
||||
// ── Header ────────────────────────────────────────
|
||||
.creature-header
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: flex-start
|
||||
gap: 0.75rem
|
||||
padding: 0.5rem 0.75rem 0.5rem
|
||||
background: var(--mgt2-bgcolor-form)
|
||||
border-bottom: 2px solid var(--mgt2-color-primary)
|
||||
|
||||
.creature-header-img
|
||||
flex: 0 0 80px
|
||||
img.profile
|
||||
width: 80px
|
||||
height: 80px
|
||||
object-fit: cover
|
||||
border: 2px solid var(--mgt2-color-primary)
|
||||
border-radius: 4px
|
||||
cursor: pointer
|
||||
|
||||
.creature-header-body
|
||||
flex: 1
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 0.4rem
|
||||
|
||||
.creature-name
|
||||
font-family: "Barlow Condensed", sans-serif
|
||||
font-size: 1.6rem
|
||||
font-weight: 700
|
||||
font-style: italic
|
||||
color: var(--mgt2-color-form)
|
||||
background: transparent
|
||||
border: none
|
||||
border-bottom: 1px solid var(--mgt2-color-primary)
|
||||
width: 100%
|
||||
padding: 0
|
||||
&:focus
|
||||
outline: none
|
||||
border-bottom-color: var(--mgt2-color-secondary)
|
||||
|
||||
// ── Stat boxes ────────────────────────────────────
|
||||
.creature-stats-row
|
||||
display: flex
|
||||
flex-direction: row
|
||||
flex-wrap: wrap
|
||||
gap: 0.5rem
|
||||
|
||||
.creature-stat
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
background: var(--mgt2-bgcolor-primary)
|
||||
border: 1px solid var(--mgt2-color-primary)
|
||||
border-radius: 4px
|
||||
padding: 2px 6px
|
||||
min-width: 4rem
|
||||
|
||||
label
|
||||
font-family: "Barlow Condensed", sans-serif
|
||||
font-size: 0.7rem
|
||||
font-weight: 700
|
||||
text-transform: uppercase
|
||||
color: var(--mgt2-color-primary)
|
||||
line-height: 1.2
|
||||
|
||||
.creature-stat-value
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: 2px
|
||||
input
|
||||
width: 2.5rem
|
||||
text-align: center
|
||||
background: transparent
|
||||
border: none
|
||||
color: var(--mgt2-bgcolor-form)
|
||||
font-size: 1rem
|
||||
font-weight: 700
|
||||
padding: 0
|
||||
&:focus
|
||||
outline: none
|
||||
border-bottom: 1px solid var(--mgt2-color-primary)
|
||||
.stat-max
|
||||
width: 2.5rem
|
||||
.stat-unit
|
||||
font-size: 0.7rem
|
||||
color: var(--mgt2-bgcolor-form)
|
||||
opacity: 0.7
|
||||
.stat-readonly
|
||||
font-size: 1rem
|
||||
font-weight: 700
|
||||
color: var(--mgt2-bgcolor-form)
|
||||
min-width: 2.5rem
|
||||
text-align: center
|
||||
|
||||
// ── Behavior row ──────────────────────────────────
|
||||
.creature-behavior-row
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: center
|
||||
gap: 0.5rem
|
||||
flex-wrap: nowrap
|
||||
|
||||
label
|
||||
flex: 0 0 auto
|
||||
font-size: 0.75rem
|
||||
text-transform: uppercase
|
||||
color: var(--mgt2-color-primary)
|
||||
font-weight: 700
|
||||
white-space: nowrap
|
||||
|
||||
.behavior-select
|
||||
flex: 1 1 auto
|
||||
min-width: 0
|
||||
background: var(--mgt2-bgcolor-form)
|
||||
color: var(--mgt2-color-form)
|
||||
border: 1px solid var(--mgt2-color-primary)
|
||||
border-radius: 3px
|
||||
font-size: 0.85rem
|
||||
padding: 1px 4px
|
||||
|
||||
.behavior-sep
|
||||
flex: 0 0 auto
|
||||
color: var(--mgt2-color-form)
|
||||
opacity: 0.5
|
||||
|
||||
.creature-size-badge
|
||||
flex: 0 0 auto
|
||||
white-space: nowrap
|
||||
font-size: 0.75rem
|
||||
font-style: italic
|
||||
color: var(--mgt2-bgcolor-form)
|
||||
background: var(--mgt2-bgcolor-primary)
|
||||
border: 1px solid var(--mgt2-color-primary)
|
||||
border-radius: 3px
|
||||
padding: 1px 6px
|
||||
|
||||
// ── Body / Tabs ────────────────────────────────────
|
||||
// min-height ensures all 4 sidebar tabs are always visible
|
||||
// (4 tabs × 54px each + 8px padding = 224px)
|
||||
.creature-body
|
||||
flex: 1
|
||||
overflow-y: auto
|
||||
padding: 0.5rem 0.75rem
|
||||
min-height: 228px
|
||||
|
||||
.tab
|
||||
display: none
|
||||
&.active
|
||||
display: block
|
||||
|
||||
// ── Info tab ──────────────────────────────────────
|
||||
.creature-info-tab
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 0.5rem
|
||||
|
||||
.creature-description
|
||||
width: 100%
|
||||
background: var(--mgt2-bgcolor-form)
|
||||
color: var(--mgt2-color-form)
|
||||
border: 1px solid var(--mgt2-color-primary)
|
||||
border-radius: 3px
|
||||
padding: 4px
|
||||
resize: vertical
|
||||
font-size: 0.9rem
|
||||
|
||||
// ── Damage formula ────────────────────────────────
|
||||
.creature-damage
|
||||
.damage-formula
|
||||
font-family: "Barlow Condensed", sans-serif
|
||||
font-weight: 700
|
||||
color: var(--mgt2-color-primary)
|
||||
font-size: 1rem
|
||||
|
||||
.trait-name
|
||||
font-weight: 600
|
||||
color: var(--mgt2-color-form)
|
||||
|
||||
.trait-value
|
||||
font-style: italic
|
||||
color: var(--mgt2-color-secondary)
|
||||
|
||||
// ─────────────────────────────────────────────────
|
||||
// Chat card – creature skill roll
|
||||
// ─────────────────────────────────────────────────
|
||||
.mgt2-creature-roll
|
||||
|
||||
.mgt2-roll-header
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: center
|
||||
gap: 0.6rem
|
||||
|
||||
.creature-chat-img
|
||||
width: 36px
|
||||
height: 36px
|
||||
object-fit: cover
|
||||
border: 1px solid var(--mgt2-color-primary)
|
||||
border-radius: 3px
|
||||
flex: 0 0 36px
|
||||
|
||||
.mgt2-roll-header-text
|
||||
flex: 1
|
||||
|
||||
// ── Section headers: use shared .header class (same as character sheet) ──────
|
||||
|
||||
// ── HTMLField editor min-height in info tab ──────────────────────────────────
|
||||
.mgt2.creature
|
||||
// Section headers (skills/attacks/traits) — margin between successive sections
|
||||
.table-container + .header
|
||||
margin-top: 8px
|
||||
|
||||
.tab[data-tab="info"]
|
||||
.editor,
|
||||
.editor-content,
|
||||
prose-mirror
|
||||
min-height: 300px !important
|
||||
height: auto !important
|
||||
275
src/sass/components/_dialog.sass
Normal file
275
src/sass/components/_dialog.sass
Normal file
@@ -0,0 +1,275 @@
|
||||
.mgt2
|
||||
.dialog-button
|
||||
color: var(--mgt2-color-primary)
|
||||
background-color: var(--mgt2-bgcolor-primary) !important
|
||||
|
||||
// ─── MGT2 Roll Dialog (DialogV2) ──────────────────────────────────────────────
|
||||
.mgt2-roll-dialog
|
||||
background: #ffffff !important
|
||||
border: 1px solid #ccbbbb !important
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.35) !important
|
||||
border-radius: 6px !important
|
||||
overflow: hidden !important
|
||||
|
||||
.window-header
|
||||
background: #0A0405 !important
|
||||
border-bottom: 3px solid #EE4050 !important
|
||||
padding: 10px 14px !important
|
||||
position: relative !important
|
||||
.window-title
|
||||
color: #ffffff !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-weight: 700 !important
|
||||
font-size: 1rem !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 2px !important
|
||||
|
||||
.window-content
|
||||
background: #ffffff !important
|
||||
color: #0A0405 !important
|
||||
padding: 0 !important
|
||||
|
||||
.dialog-content, .standard-form
|
||||
background: #ffffff !important
|
||||
padding: 8px 14px 6px !important
|
||||
|
||||
// Form group rows
|
||||
.form-group
|
||||
display: flex !important
|
||||
align-items: center !important
|
||||
gap: 8px !important
|
||||
margin-bottom: 3px !important
|
||||
padding: 2px 0 !important
|
||||
border-bottom: 1px solid #e8e0e0 !important
|
||||
|
||||
&:last-child
|
||||
border-bottom: none !important
|
||||
|
||||
label
|
||||
color: #0A0405 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-weight: 700 !important
|
||||
font-size: 0.72rem !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 1.2px !important
|
||||
flex: 0 0 110px !important
|
||||
line-height: 1.2 !important
|
||||
|
||||
select, input[type="number"], input[type="text"]
|
||||
flex: 1 !important
|
||||
background: #ffffff !important
|
||||
border: 1px solid #ccbbbb !important
|
||||
color: #0A0405 !important
|
||||
border-radius: 3px !important
|
||||
padding: 3px 8px !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.9rem !important
|
||||
transition: border-color 150ms ease !important
|
||||
|
||||
&:focus
|
||||
border-color: #EE4050 !important
|
||||
outline: none !important
|
||||
background: rgba(238,64,80,0.04) !important
|
||||
|
||||
option
|
||||
background: #ffffff !important
|
||||
color: #0A0405 !important
|
||||
|
||||
// Fieldset / ÉTATS section
|
||||
fieldset, .form-fields
|
||||
background: #fdf8f8 !important
|
||||
border: 1px solid #e0c8c8 !important
|
||||
border-radius: 5px !important
|
||||
padding: 5px 10px !important
|
||||
margin-bottom: 4px !important
|
||||
|
||||
legend
|
||||
color: #EE4050 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-weight: 700 !important
|
||||
font-size: 0.72rem !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 2px !important
|
||||
padding: 0 8px !important
|
||||
background: #ffffff !important
|
||||
|
||||
// Checkboxes inside fieldset
|
||||
.form-group
|
||||
border-bottom: none !important
|
||||
margin-bottom: 4px !important
|
||||
padding: 2px 0 !important
|
||||
|
||||
label
|
||||
color: #3a2020 !important
|
||||
flex: 1 !important
|
||||
|
||||
input[type="checkbox"]
|
||||
accent-color: #EE4050 !important
|
||||
width: 14px !important
|
||||
height: 14px !important
|
||||
|
||||
// Read-only state badges
|
||||
.roll-prompt-states
|
||||
display: flex !important
|
||||
gap: 6px !important
|
||||
flex-wrap: wrap !important
|
||||
padding: 2px 0 !important
|
||||
|
||||
.roll-prompt-state-badge
|
||||
display: inline-flex !important
|
||||
align-items: center !important
|
||||
gap: 4px !important
|
||||
padding: 2px 8px !important
|
||||
border-radius: 3px !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.75rem !important
|
||||
font-weight: 600 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 0.8px !important
|
||||
background: #ede8e8 !important
|
||||
border: 1px solid #ccc !important
|
||||
color: #999 !important
|
||||
|
||||
i
|
||||
font-size: 0.7rem !important
|
||||
|
||||
&.is-active
|
||||
background: #fdf0f0 !important
|
||||
border-color: #EE4050 !important
|
||||
color: #EE4050 !important
|
||||
|
||||
strong
|
||||
font-weight: 800 !important
|
||||
|
||||
// Footer buttons
|
||||
.dialog-buttons, .form-footer, footer
|
||||
background: #f5eeee !important
|
||||
border-top: 2px solid #EE4050 !important
|
||||
padding: 7px 14px !important
|
||||
display: flex !important
|
||||
gap: 8px !important
|
||||
justify-content: center !important
|
||||
|
||||
button
|
||||
flex: 1 !important
|
||||
max-width: 140px !important
|
||||
background: #ffffff !important
|
||||
border: 1px solid #ccbbbb !important
|
||||
color: #3a2020 !important
|
||||
border-radius: 4px !important
|
||||
padding: 5px 12px !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.82rem !important
|
||||
font-weight: 700 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 1.5px !important
|
||||
cursor: pointer !important
|
||||
transition: all 150ms ease !important
|
||||
|
||||
&:hover
|
||||
background: #fdf0f0 !important
|
||||
border-color: #EE4050 !important
|
||||
color: #EE4050 !important
|
||||
|
||||
// Primary action button (DialogV2: data-action="submit", autofocus)
|
||||
&.default, &[data-action="submit"], &[autofocus]
|
||||
background: #EE4050 !important
|
||||
border-color: #EE4050 !important
|
||||
color: #fff !important
|
||||
box-shadow: 0 2px 12px rgba(238,64,80,0.3) !important
|
||||
|
||||
&:hover
|
||||
background: #ff5060 !important
|
||||
box-shadow: 0 4px 18px rgba(238,64,80,0.45) !important
|
||||
|
||||
// Ranged modifiers fieldset
|
||||
.mgt2-ranged-modifiers
|
||||
margin-top: 4px !important
|
||||
|
||||
.mgt2-ranged-checkboxes, .mgt2-ranged-dodge
|
||||
display: flex !important
|
||||
flex-wrap: wrap !important
|
||||
gap: 6px !important
|
||||
align-items: center !important
|
||||
border-bottom: none !important
|
||||
|
||||
.mgt2-checkbox-tag
|
||||
display: inline-flex !important
|
||||
align-items: center !important
|
||||
gap: 4px !important
|
||||
padding: 2px 7px !important
|
||||
border: 1px solid #ddc8c8 !important
|
||||
border-radius: 3px !important
|
||||
background: #fdf8f8 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.73rem !important
|
||||
font-weight: 600 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 0.8px !important
|
||||
color: #3a2020 !important
|
||||
cursor: pointer !important
|
||||
transition: border-color 0.15s, background 0.15s !important
|
||||
|
||||
em
|
||||
font-style: normal !important
|
||||
color: #999 !important
|
||||
font-weight: 400 !important
|
||||
|
||||
input[type="checkbox"]
|
||||
accent-color: #EE4050 !important
|
||||
width: 13px !important
|
||||
height: 13px !important
|
||||
margin: 0 !important
|
||||
|
||||
&:has(input:checked)
|
||||
border-color: #EE4050 !important
|
||||
background: #fdf0f0 !important
|
||||
color: #EE4050 !important
|
||||
|
||||
em
|
||||
color: #EE4050 !important
|
||||
|
||||
// Auto fire mode selector
|
||||
.mgt2-auto-mode
|
||||
border-bottom: 1px solid #ddc8c8 !important
|
||||
padding-bottom: 4px !important
|
||||
margin-bottom: 2px !important
|
||||
|
||||
.mgt2-auto-hint
|
||||
font-size: 0.72rem !important
|
||||
color: #9a6520 !important
|
||||
margin: 0 0 4px 0 !important
|
||||
font-style: italic !important
|
||||
text-align: center !important
|
||||
|
||||
.mgt2-scope-badge
|
||||
display: inline-flex
|
||||
align-items: center
|
||||
gap: 5px
|
||||
font-size: 0.72rem
|
||||
font-weight: 600
|
||||
color: #1a4060
|
||||
background: rgba(30,100,180,0.10)
|
||||
border: 1px solid rgba(30,100,180,0.28)
|
||||
border-radius: 4px
|
||||
padding: 2px 8px
|
||||
margin-bottom: 4px
|
||||
cursor: help
|
||||
i
|
||||
color: #1a6090
|
||||
|
||||
.mgt2-zerog-badge
|
||||
display: inline-flex
|
||||
align-items: center
|
||||
gap: 5px
|
||||
font-size: 0.72rem
|
||||
font-weight: 600
|
||||
color: #2a3a50
|
||||
background: rgba(60,80,120,0.10)
|
||||
border: 1px solid rgba(60,80,120,0.28)
|
||||
border-radius: 4px
|
||||
padding: 2px 8px
|
||||
margin-bottom: 4px
|
||||
cursor: help
|
||||
i
|
||||
color: #506090
|
||||
86
src/sass/components/_forms.sass
Normal file
86
src/sass/components/_forms.sass
Normal file
@@ -0,0 +1,86 @@
|
||||
.mgt2
|
||||
&.sheet
|
||||
textarea
|
||||
color: var(--mgt2-input-color)
|
||||
background-color: var(--mgt2-input-bgcolor)
|
||||
font-family: "Roboto", sans-serif
|
||||
font-size: 13px
|
||||
font-stretch: 100%
|
||||
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
select:focus
|
||||
outline: none
|
||||
box-shadow: none
|
||||
|
||||
.checkbox-small
|
||||
flex: none!important
|
||||
width: auto!important
|
||||
height: auto!important
|
||||
margin: 0!important
|
||||
|
||||
.header
|
||||
color: var(--mgt2-color-primary)
|
||||
background: var(--mgt2-bgcolor-primary)
|
||||
font-size: 14px
|
||||
font-family: "Roboto Condensed", sans-serif
|
||||
font-weight: bold
|
||||
padding-left: 5px
|
||||
margin-bottom: 4px
|
||||
line-height: 30px
|
||||
text-transform: uppercase
|
||||
|
||||
.field-groups
|
||||
display: flex
|
||||
flex-direction: row
|
||||
flex-wrap: nowrap
|
||||
align-items: center
|
||||
justify-content: space-between
|
||||
|
||||
.field-group
|
||||
label
|
||||
text-transform: uppercase
|
||||
font-weight: 700
|
||||
font-size: 14px
|
||||
font-family: "Roboto Condensed", sans-serif
|
||||
font-optical-sizing: auto
|
||||
input
|
||||
&.field
|
||||
background-color: var(--mgt2-input-bgcolor)
|
||||
font-size: 13px
|
||||
&.field-name
|
||||
background-color: var(--mgt2-input-bgcolor)
|
||||
color: var(--mgt2-color-primary)
|
||||
font-size: 2rem
|
||||
border: none
|
||||
font-weight: 700
|
||||
font-family: "Roboto Condensed", sans-serif
|
||||
margin-bottom: 0.5rem
|
||||
padding: 0
|
||||
&.field-item-name
|
||||
background-color: var(--mgt2-input-bgcolor)
|
||||
color: var(--mgt2-color-primary)
|
||||
height: auto
|
||||
font-size: 2rem
|
||||
font-weight: 700
|
||||
font-family: "Roboto Condensed", sans-serif
|
||||
|
||||
.fields
|
||||
display: flex
|
||||
|
||||
.editor
|
||||
min-height: 3rem
|
||||
border: 1px solid var(--mgt2-editor-border)
|
||||
height: 100%
|
||||
|
||||
.sheet-body
|
||||
margin-left: 140px
|
||||
padding-bottom: 1.5rem
|
||||
|
||||
label
|
||||
&.mgt2-checkbox
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: center
|
||||
input
|
||||
margin: 0 0.3rem 0 0
|
||||
459
src/sass/components/_item.sass
Normal file
459
src/sass/components/_item.sass
Normal file
@@ -0,0 +1,459 @@
|
||||
// ─── MGT2 Item Sheets ─────────────────────────────────────────────────────────
|
||||
// New unified layout: type-bar → header (image+name) → stats-bar → full-width tabs
|
||||
|
||||
// ── Outer window ──────────────────────────────────────────────────────────────
|
||||
.itemsheet
|
||||
display: flex !important
|
||||
flex-direction: column !important
|
||||
height: 100% !important
|
||||
background: #ffffff !important
|
||||
overflow: hidden !important
|
||||
|
||||
// ── Type bar (replaces vertical sidebar label) ────────────────────────────────
|
||||
.item-type-bar
|
||||
background: #0A0405 !important
|
||||
border-bottom: 3px solid #EE4050 !important
|
||||
padding: 4px 12px !important
|
||||
display: flex !important
|
||||
align-items: center !important
|
||||
gap: 8px !important
|
||||
min-height: 0 !important
|
||||
flex-shrink: 0 !important
|
||||
|
||||
.item-type-label
|
||||
color: #EE4050 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.65rem !important
|
||||
font-weight: 700 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 2px !important
|
||||
|
||||
// ── Header: image + name ──────────────────────────────────────────────────────
|
||||
.item-sheet-header
|
||||
background: #0A0405 !important
|
||||
padding: 10px 14px !important
|
||||
display: flex !important
|
||||
align-items: center !important
|
||||
gap: 12px !important
|
||||
flex-shrink: 0 !important
|
||||
|
||||
.item-header-img
|
||||
width: 64px !important
|
||||
height: 64px !important
|
||||
object-fit: cover !important
|
||||
border: 2px solid #EE4050 !important
|
||||
border-radius: 4px !important
|
||||
cursor: pointer !important
|
||||
flex-shrink: 0 !important
|
||||
|
||||
.item-header-name
|
||||
flex: 1 !important
|
||||
background: transparent !important
|
||||
border: none !important
|
||||
border-bottom: 1px solid rgba(238,64,80,0.4) !important
|
||||
color: #ffffff !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 1.6rem !important
|
||||
font-weight: 800 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 1px !important
|
||||
padding: 2px 0 !important
|
||||
line-height: 1.1 !important
|
||||
width: 100% !important
|
||||
|
||||
&:focus
|
||||
outline: none !important
|
||||
border-bottom-color: #EE4050 !important
|
||||
|
||||
&::placeholder
|
||||
color: rgba(255,255,255,0.35) !important
|
||||
|
||||
// ── Stats bar (pills) ─────────────────────────────────────────────────────────
|
||||
.item-stats-bar
|
||||
background: #f5eeee !important
|
||||
border-bottom: 1px solid #ddc8c8 !important
|
||||
padding: 6px 14px !important
|
||||
display: flex !important
|
||||
flex-wrap: wrap !important
|
||||
gap: 6px !important
|
||||
flex-shrink: 0 !important
|
||||
|
||||
.item-stat-pill
|
||||
display: flex !important
|
||||
flex-direction: column !important
|
||||
align-items: center !important
|
||||
background: #ffffff !important
|
||||
border: 1px solid #ddc8c8 !important
|
||||
border-radius: 4px !important
|
||||
padding: 3px 10px !important
|
||||
min-width: 56px !important
|
||||
|
||||
.stat-label
|
||||
color: #664444 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.6rem !important
|
||||
font-weight: 700 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 1.5px !important
|
||||
line-height: 1.2 !important
|
||||
|
||||
.stat-value
|
||||
color: #0A0405 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 1rem !important
|
||||
font-weight: 800 !important
|
||||
line-height: 1.1 !important
|
||||
|
||||
input, select
|
||||
color: #0A0405 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 1rem !important
|
||||
font-weight: 800 !important
|
||||
background: transparent !important
|
||||
border: none !important
|
||||
text-align: center !important
|
||||
width: 100% !important
|
||||
padding: 0 !important
|
||||
line-height: 1.1 !important
|
||||
|
||||
&:focus
|
||||
outline: none !important
|
||||
border-bottom: 1px solid #EE4050 !important
|
||||
|
||||
.item-stat-pill-checkbox
|
||||
display: flex !important
|
||||
flex-direction: row !important
|
||||
align-items: center !important
|
||||
gap: 5px !important
|
||||
background: #ffffff !important
|
||||
border: 1px solid #ddc8c8 !important
|
||||
border-radius: 4px !important
|
||||
padding: 4px 10px !important
|
||||
|
||||
input[type="checkbox"]
|
||||
margin: 0 !important
|
||||
width: 14px !important
|
||||
height: 14px !important
|
||||
accent-color: #EE4050 !important
|
||||
|
||||
label
|
||||
color: #664444 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.65rem !important
|
||||
font-weight: 700 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 1px !important
|
||||
cursor: pointer !important
|
||||
|
||||
// ── Tabs navigation ───────────────────────────────────────────────────────────
|
||||
.itemsheet
|
||||
.horizontal-tabs.tabs
|
||||
background: #f5eeee !important
|
||||
border-bottom: 2px solid #ddc8c8 !important
|
||||
display: flex !important
|
||||
flex-wrap: wrap !important
|
||||
gap: 0 !important
|
||||
padding: 0 10px !important
|
||||
flex-shrink: 0 !important
|
||||
|
||||
.item.tab-select
|
||||
color: #664444 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.72rem !important
|
||||
font-weight: 700 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 1.5px !important
|
||||
padding: 6px 14px !important
|
||||
border: none !important
|
||||
border-bottom: 2px solid transparent !important
|
||||
margin-bottom: -2px !important
|
||||
background: transparent !important
|
||||
cursor: pointer !important
|
||||
transition: color 0.15s, border-color 0.15s !important
|
||||
|
||||
&:hover
|
||||
color: #EE4050 !important
|
||||
|
||||
&.active
|
||||
color: #EE4050 !important
|
||||
border-bottom-color: #EE4050 !important
|
||||
background: transparent !important
|
||||
|
||||
// ── Tab content area ──────────────────────────────────────────────────────────
|
||||
.itemsheet
|
||||
.tab-content-area
|
||||
flex: 1 !important
|
||||
overflow-y: auto !important
|
||||
padding: 10px 14px !important
|
||||
background: #ffffff !important
|
||||
|
||||
.tab[data-group]
|
||||
display: none !important
|
||||
|
||||
&.active
|
||||
display: block !important
|
||||
|
||||
// ── Fields inside item sheets ─────────────────────────────────────────────────
|
||||
.itemsheet
|
||||
.field-group
|
||||
margin-bottom: 8px !important
|
||||
|
||||
label
|
||||
display: block !important
|
||||
color: #664444 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.68rem !important
|
||||
font-weight: 700 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 1px !important
|
||||
margin-bottom: 2px !important
|
||||
|
||||
.field-groups
|
||||
display: flex !important
|
||||
flex-wrap: wrap !important
|
||||
gap: 10px !important
|
||||
align-items: flex-start !important
|
||||
|
||||
.field-group
|
||||
flex: 1 !important
|
||||
min-width: 80px !important
|
||||
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
select,
|
||||
textarea
|
||||
background: #fdf8f8 !important
|
||||
border: 1px solid #ddc8c8 !important
|
||||
border-radius: 3px !important
|
||||
color: #0A0405 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.85rem !important
|
||||
padding: 4px 8px !important
|
||||
width: 100% !important
|
||||
|
||||
&:focus
|
||||
outline: none !important
|
||||
border-color: #EE4050 !important
|
||||
box-shadow: 0 0 0 2px rgba(238,64,80,0.12) !important
|
||||
|
||||
textarea
|
||||
resize: vertical !important
|
||||
min-height: 80px !important
|
||||
|
||||
.mgt2-checkbox
|
||||
display: flex !important
|
||||
align-items: center !important
|
||||
gap: 6px !important
|
||||
color: #664444 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.72rem !important
|
||||
font-weight: 700 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 1px !important
|
||||
|
||||
input[type="checkbox"]
|
||||
width: 14px !important
|
||||
height: 14px !important
|
||||
margin: 0 !important
|
||||
accent-color: #EE4050 !important
|
||||
|
||||
// ── Tables inside item sheets ─────────────────────────────────────────────────
|
||||
.itemsheet
|
||||
.table-container
|
||||
border: 1px solid #ddc8c8 !important
|
||||
border-radius: 4px !important
|
||||
overflow: hidden !important
|
||||
margin-top: 8px !important
|
||||
|
||||
.table-row
|
||||
display: flex !important
|
||||
align-items: stretch !important
|
||||
border-bottom: 1px solid #e8dada !important
|
||||
|
||||
&:last-child
|
||||
border-bottom: none !important
|
||||
|
||||
&.heading
|
||||
background: #0A0405 !important
|
||||
color: #EE4050 !important
|
||||
|
||||
.row-item
|
||||
color: #EE4050 !important
|
||||
font-family: 'Barlow Condensed', sans-serif !important
|
||||
font-size: 0.68rem !important
|
||||
font-weight: 700 !important
|
||||
text-transform: uppercase !important
|
||||
letter-spacing: 1px !important
|
||||
padding: 5px 8px !important
|
||||
|
||||
&:not(.heading)
|
||||
background: #ffffff !important
|
||||
|
||||
&:nth-child(even)
|
||||
background: #fdf8f8 !important
|
||||
|
||||
.row-item
|
||||
padding: 4px 6px !important
|
||||
|
||||
input, textarea, select
|
||||
font-size: 0.8rem !important
|
||||
padding: 2px 6px !important
|
||||
|
||||
.row-item
|
||||
flex: 1 !important
|
||||
border-right: 1px solid #e8dada !important
|
||||
|
||||
&:last-child
|
||||
border-right: none !important
|
||||
|
||||
&.row-item-left
|
||||
justify-content: flex-start !important
|
||||
|
||||
&.row-item-right
|
||||
flex: 0 0 auto !important
|
||||
min-width: 36px !important
|
||||
display: flex !important
|
||||
align-items: center !important
|
||||
justify-content: center !important
|
||||
|
||||
&.row-item-30
|
||||
flex: 0 0 30% !important
|
||||
|
||||
.item-controls
|
||||
display: flex !important
|
||||
align-items: center !important
|
||||
justify-content: center !important
|
||||
gap: 6px !important
|
||||
|
||||
a
|
||||
color: #888888 !important
|
||||
cursor: pointer !important
|
||||
|
||||
&:hover
|
||||
color: #EE4050 !important
|
||||
|
||||
// ── Legacy classes kept for compatibility ─────────────────────────────────────
|
||||
// (old structure hidden, new structure used)
|
||||
.itemsheet-header
|
||||
display: none !important
|
||||
|
||||
.itemsheet-maincol
|
||||
display: none !important
|
||||
|
||||
.itemsheet-panel
|
||||
display: contents !important
|
||||
|
||||
|
||||
// ── Details tab: 2-column grid layout ────────────────────────────────────────
|
||||
.itemsheet
|
||||
.item-details-grid
|
||||
display: grid !important
|
||||
grid-template-columns: 1fr 1fr !important
|
||||
gap: 4px 16px !important
|
||||
align-items: start !important
|
||||
|
||||
// Traits table spans full width
|
||||
.table-container
|
||||
grid-column: 1 / -1 !important
|
||||
margin-top: 10px !important
|
||||
|
||||
// ── Field row: label + input on the same line ─────────────────────────────────
|
||||
.itemsheet
|
||||
.field-row
|
||||
display: flex !important
|
||||
align-items: center !important
|
||||
gap: 8px !important
|
||||
min-height: 28px !important
|
||||
|
||||
label
|
||||
flex: 0 0 100px !important
|
||||
min-width: 0 !important
|
||||
margin-bottom: 0 !important
|
||||
white-space: nowrap !important
|
||||
overflow: hidden !important
|
||||
text-overflow: ellipsis !important
|
||||
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
select
|
||||
flex: 1 !important
|
||||
width: auto !important
|
||||
height: 24px !important
|
||||
padding: 2px 6px !important
|
||||
|
||||
input.short
|
||||
flex: 0 0 56px !important
|
||||
width: 56px !important
|
||||
|
||||
.range-inputs
|
||||
display: flex !important
|
||||
gap: 4px !important
|
||||
flex: 1 !important
|
||||
|
||||
input
|
||||
flex: 0 0 52px !important
|
||||
width: 52px !important
|
||||
|
||||
select
|
||||
flex: 1 !important
|
||||
|
||||
// Full-width row (e.g. storage)
|
||||
&.full
|
||||
grid-column: 1 / -1 !important
|
||||
|
||||
// Checkbox row variant
|
||||
.field-row--check
|
||||
label
|
||||
flex: unset !important
|
||||
display: flex !important
|
||||
align-items: center !important
|
||||
gap: 6px !important
|
||||
cursor: pointer !important
|
||||
|
||||
// ── Description tab: editor min-height ───────────────────────────────────────
|
||||
.itemsheet
|
||||
.tab[data-tab="tab1"]
|
||||
.editor,
|
||||
.editor-container
|
||||
min-height: 200px !important
|
||||
|
||||
// ── Weapon traits structured grid ────────────────────────────────────────────
|
||||
.mgt2-weapon-traits
|
||||
border: 1px solid var(--color-border-light-tertiary)
|
||||
border-radius: 4px
|
||||
padding: 6px 8px
|
||||
margin-top: 4px
|
||||
|
||||
legend
|
||||
font-size: 0.85em
|
||||
font-weight: bold
|
||||
padding: 0 4px
|
||||
|
||||
.mgt2-weapon-traits-grid
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
gap: 6px 12px
|
||||
align-items: center
|
||||
|
||||
.mgt2-trait-num
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: 4px
|
||||
label
|
||||
font-size: 0.85em
|
||||
white-space: nowrap
|
||||
input[type="number"]
|
||||
width: 44px
|
||||
text-align: center
|
||||
flex: 0 0 44px
|
||||
|
||||
.mgt2-trait-bool
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: 4px
|
||||
label
|
||||
font-size: 0.85em
|
||||
white-space: nowrap
|
||||
cursor: pointer
|
||||
input[type="checkbox"]
|
||||
cursor: pointer
|
||||
111
src/sass/components/_tab-sidebar.sass
Normal file
111
src/sass/components/_tab-sidebar.sass
Normal file
@@ -0,0 +1,111 @@
|
||||
// ── Overflow fixes: allow sidebar nav to protrude OUTSIDE the window frame ──
|
||||
// All ancestors of nav.sheet-sidebar need overflow:visible so absolute-positioned
|
||||
// nav (left: 100% of character-body) can extend to the right of the window border.
|
||||
// Layered !important beats Foundry's unlayered overflow:hidden per CSS cascade spec.
|
||||
|
||||
.mgt2.character, .mgt2.creature, .mgt2.vehicule
|
||||
overflow: visible !important
|
||||
> .window-content
|
||||
overflow: visible !important
|
||||
.editable, .locked
|
||||
overflow: visible !important
|
||||
|
||||
.mgt2.character .character-body
|
||||
position: relative !important
|
||||
overflow: visible !important
|
||||
|
||||
.mgt2.creature .creature-body
|
||||
position: relative !important
|
||||
overflow: visible !important
|
||||
|
||||
.mgt2.vehicule .vehicule-content
|
||||
position: relative !important
|
||||
overflow: visible !important
|
||||
|
||||
// ── Vertical sidebar tab navigation (outside window, right side) ──
|
||||
.mgt2
|
||||
nav.sheet-sidebar.tabs
|
||||
position: absolute !important
|
||||
left: 100% !important
|
||||
top: 0 !important
|
||||
width: 62px !important
|
||||
flex: none !important
|
||||
display: flex !important
|
||||
flex-direction: column
|
||||
// Rich dark gradient matching MGT2 theme
|
||||
background: linear-gradient(180deg, #1e0507 0%, #110304 40%, #0a0202 100%)
|
||||
border-top: 1px solid rgba(238,64,80,0.35)
|
||||
border-right: 1px solid rgba(238,64,80,0.25)
|
||||
border-bottom: 1px solid rgba(238,64,80,0.2)
|
||||
border-left: 3px solid var(--mgt2-color-primary)
|
||||
border-radius: 0 10px 10px 0
|
||||
box-shadow: 6px 0 24px rgba(0,0,0,0.75), 0 0 0 0 transparent, inset 1px 0 16px rgba(238,64,80,0.05)
|
||||
z-index: 10
|
||||
overflow: hidden !important
|
||||
padding: 2px 0
|
||||
|
||||
& > .item
|
||||
position: relative
|
||||
display: flex !important
|
||||
flex-direction: column !important
|
||||
justify-content: center !important
|
||||
align-items: center !important
|
||||
gap: 4px !important
|
||||
min-height: 54px
|
||||
padding: 8px 4px
|
||||
color: rgba(238,64,80,0.45)
|
||||
border-bottom: 1px solid rgba(238,64,80,0.07)
|
||||
cursor: pointer
|
||||
transition: background 180ms ease, color 180ms ease, box-shadow 180ms ease
|
||||
user-select: none
|
||||
text-decoration: none !important
|
||||
|
||||
// Left accent bar
|
||||
&::before
|
||||
content: ''
|
||||
position: absolute
|
||||
left: -3px
|
||||
top: 18%
|
||||
bottom: 18%
|
||||
width: 3px
|
||||
background: transparent
|
||||
border-radius: 0 3px 3px 0
|
||||
transition: background 180ms ease, top 180ms ease, bottom 180ms ease, box-shadow 180ms ease
|
||||
|
||||
&:hover
|
||||
color: var(--mgt2-color-primary)
|
||||
background: rgba(238,64,80,0.07)
|
||||
&::before
|
||||
background: rgba(238,64,80,0.5)
|
||||
.tab-label
|
||||
color: rgba(238,64,80,0.7)
|
||||
|
||||
&.active
|
||||
color: var(--mgt2-color-primary)
|
||||
background: linear-gradient(90deg, rgba(238,64,80,0.16) 0%, rgba(238,64,80,0.03) 100%)
|
||||
box-shadow: inset 0 1px 0 rgba(238,64,80,0.12), inset 0 -1px 0 rgba(238,64,80,0.08)
|
||||
&::before
|
||||
background: var(--mgt2-color-primary)
|
||||
top: 10%
|
||||
bottom: 10%
|
||||
box-shadow: 0 0 10px rgba(238,64,80,0.7), 0 0 20px rgba(238,64,80,0.3)
|
||||
i
|
||||
filter: drop-shadow(0 0 5px rgba(238,64,80,0.55))
|
||||
.tab-label
|
||||
color: rgba(238,64,80,0.85)
|
||||
|
||||
i
|
||||
font-size: 1.15rem
|
||||
pointer-events: none
|
||||
line-height: 1
|
||||
|
||||
.tab-label
|
||||
font-family: 'Barlow Condensed', sans-serif
|
||||
font-size: 0.52rem
|
||||
font-weight: 700
|
||||
text-transform: uppercase
|
||||
letter-spacing: 0.8px
|
||||
color: rgba(238,64,80,0.3)
|
||||
line-height: 1
|
||||
pointer-events: none
|
||||
transition: color 180ms ease
|
||||
172
src/sass/components/_tables.sass
Normal file
172
src/sass/components/_tables.sass
Normal file
@@ -0,0 +1,172 @@
|
||||
.container-controls
|
||||
display: inline-block
|
||||
margin-left: 1rem
|
||||
a
|
||||
&:not(:last-child)
|
||||
margin-right: 0.5rem
|
||||
|
||||
.table-container
|
||||
display: flex
|
||||
flex-flow: column nowrap
|
||||
width: 100%
|
||||
margin: 0 auto
|
||||
.table-row
|
||||
display: flex
|
||||
flex-flow: row nowrap
|
||||
width: 100%
|
||||
position: relative
|
||||
align-items: flex-start
|
||||
&.heading
|
||||
background-color: var(--mgt2-bgcolor-primary)
|
||||
align-items: center
|
||||
.row-item
|
||||
text-transform: uppercase
|
||||
font-size: 12px
|
||||
&:first-child
|
||||
font-weight: bold
|
||||
font-size: 13px
|
||||
letter-spacing: 3px
|
||||
i
|
||||
margin-right: 0.5rem
|
||||
&.color-1
|
||||
.row-item
|
||||
background-color: var(--mgt2-bgcolor-primary)
|
||||
color: var(--mgt2-color-primary)
|
||||
&.color-2
|
||||
.row-item
|
||||
background-color: var(--mgt2-bgcolor-form)
|
||||
color: var(--mgt2-bgcolor-primary)
|
||||
div
|
||||
&.row-item
|
||||
padding-left: 5px
|
||||
&:last-child
|
||||
padding-right: 5px
|
||||
&:hover
|
||||
&:not(.heading)
|
||||
background-color: var(--mgt2-row-hover)
|
||||
.table-row-mb-4
|
||||
margin-bottom: 4px
|
||||
.row-item
|
||||
display: flex
|
||||
flex-grow: 1
|
||||
font-size: 14px
|
||||
line-height: 25px
|
||||
align-items: center
|
||||
transition: all 0.15s ease-in-out
|
||||
overflow: hidden !important
|
||||
text-overflow: ellipsis
|
||||
text-wrap: nowrap
|
||||
&.item-controls
|
||||
justify-content: right
|
||||
padding-right: 4px
|
||||
a
|
||||
&:not(:last-child)
|
||||
margin-right: 0.4rem
|
||||
i
|
||||
color: var(--mgt2-color-form)
|
||||
a[data-roll]
|
||||
margin-right: 0.5rem
|
||||
.heading
|
||||
&.color-1
|
||||
.row-item
|
||||
i
|
||||
color: var(--mgt2-color-primary) !important
|
||||
&.color-2
|
||||
.row-item
|
||||
i
|
||||
color: var(--mgt2-bgcolor-primary) !important
|
||||
.table-subrow
|
||||
border-left: 2px var(--mgt2-subrow-color) dashed
|
||||
color: var(--mgt2-subrow-color)
|
||||
.row-item
|
||||
font-size: 0.8em
|
||||
line-height: 20px
|
||||
&:first-child
|
||||
padding-left: 1rem
|
||||
& > i
|
||||
margin-right: 4px
|
||||
.row-item-center
|
||||
justify-content: center
|
||||
text-align: center
|
||||
.row-item-left
|
||||
justify-content: left
|
||||
.row-item-right
|
||||
justify-content: right
|
||||
.row-item-space-between
|
||||
justify-content: space-between
|
||||
.row-item-2
|
||||
flex-basis: 4rem
|
||||
.row-item-5
|
||||
flex-basis: 5%
|
||||
.row-item-10
|
||||
flex-basis: 10%
|
||||
.row-item-12
|
||||
flex-basis: 4rem
|
||||
.row-item-15
|
||||
flex-basis: 5rem
|
||||
.row-item-20
|
||||
flex-basis: 20%
|
||||
.row-item-25
|
||||
flex-basis: 25%
|
||||
.row-item-30
|
||||
flex-basis: 30%
|
||||
.row-item-35
|
||||
flex-basis: 35%
|
||||
.row-item-40
|
||||
flex-basis: 40%
|
||||
.row-item-45
|
||||
flex-basis: 45%
|
||||
.row-item-50
|
||||
flex-basis: 50%
|
||||
.row-item-65
|
||||
flex-basis: 50%
|
||||
.row-item-85
|
||||
flex-basis: 50%
|
||||
.row-item-storage
|
||||
flex-wrap: wrap
|
||||
flex-grow: 0
|
||||
flex-basis: 20%
|
||||
font-size: 0.7rem
|
||||
line-height: 0.8rem
|
||||
.item-control
|
||||
&.item-equip
|
||||
i
|
||||
color: var(--mgt2-row-inactive-icon)
|
||||
&.active
|
||||
i
|
||||
color: var(--mgt2-color-form)
|
||||
.row-description
|
||||
flex-basis: 100%
|
||||
font-size: 14px
|
||||
padding: 4px 0
|
||||
justify-content: left
|
||||
transition: all 0.15s ease-in-out
|
||||
.row-sub-container
|
||||
display: flex
|
||||
flex-flow: column nowrap
|
||||
flex: 1
|
||||
.row-item
|
||||
padding: 8px 0
|
||||
border-bottom: 1px solid var(--mgt2-bgcolor-primary)
|
||||
.table-row:last-child,
|
||||
.row-sub-container .row-item:last-child
|
||||
border-bottom: 0
|
||||
.table-container
|
||||
&.editable
|
||||
.table-row
|
||||
margin-top: 4px
|
||||
.table-container
|
||||
&.editable
|
||||
.table-row:last-child
|
||||
margin-bottom: 4px
|
||||
.item-options
|
||||
position: absolute
|
||||
top: 0.7rem
|
||||
font-size: 0.7em
|
||||
left: 1.6rem
|
||||
text-transform: uppercase
|
||||
font-family: "DM Sans", sans-serif
|
||||
font-optical-sizing: auto
|
||||
font-weight: 600
|
||||
font-style: normal
|
||||
color: var(--mgt2-subrow-color)
|
||||
58
src/sass/components/_tabs.sass
Normal file
58
src/sass/components/_tabs.sass
Normal file
@@ -0,0 +1,58 @@
|
||||
.mgt2
|
||||
nav
|
||||
&.horizontal-tabs
|
||||
color: var(--mgt2-color-primary)
|
||||
background: var(--mgt2-bgcolor-primary)
|
||||
font-style: normal
|
||||
font-weight: 700
|
||||
font-size: 14px
|
||||
line-height: 30px
|
||||
text-transform: uppercase
|
||||
justify-content: space-around
|
||||
align-items: center
|
||||
font-family: "Roboto Condensed", sans-serif
|
||||
a
|
||||
&.item
|
||||
position: relative
|
||||
flex: 1 1 auto
|
||||
i
|
||||
margin-right: 0.5rem
|
||||
& > a
|
||||
&.item
|
||||
&::after
|
||||
content: ""
|
||||
position: absolute
|
||||
inset: 0.25rem 0.25rem 0.25rem 0.25rem
|
||||
border: 1px solid var( --mgt2-color-primary-active)
|
||||
pointer-events: none
|
||||
&.active
|
||||
&::after
|
||||
border-bottom: none
|
||||
border-top: 2px solid var( --mgt2-color-primary-active)
|
||||
border-left: 2px solid var( --mgt2-color-primary-active)
|
||||
border-right: 2px solid var( --mgt2-color-primary-active)
|
||||
inset: 0.25rem 0.25rem 0 0.25rem
|
||||
.active
|
||||
color: var(--mgt2-color-primary)
|
||||
text-decoration: none
|
||||
text-shadow: none
|
||||
border-bottom: none
|
||||
|
||||
|
||||
.tab[data-tab].fullsize
|
||||
height: calc(100% - 3rem)
|
||||
|
||||
.subTab
|
||||
flex-flow: column
|
||||
height: 100%
|
||||
display: flex
|
||||
justify-content: flex-start
|
||||
align-items: stretch
|
||||
.tab-scroll
|
||||
overflow-y: auto
|
||||
height: 100%
|
||||
.subTabs
|
||||
height: 100%
|
||||
flex-direction: column
|
||||
&.active
|
||||
display: flex !important
|
||||
203
src/sass/components/_vehicule.sass
Normal file
203
src/sass/components/_vehicule.sass
Normal file
@@ -0,0 +1,203 @@
|
||||
// ─────────────────────────────────────────────────
|
||||
// Vehicule Sheet Styles
|
||||
// ─────────────────────────────────────────────────
|
||||
|
||||
.vehicule-sheet
|
||||
|
||||
// ── Header ────────────────────────────────────────
|
||||
.vehicule-header
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: flex-start
|
||||
gap: 0.75rem
|
||||
padding: 0.5rem 0.75rem
|
||||
background: var(--mgt2-bgcolor-form)
|
||||
border-bottom: 2px solid var(--mgt2-color-primary)
|
||||
flex-shrink: 0
|
||||
|
||||
.vehicule-header-img
|
||||
flex: 0 0 90px
|
||||
img.profile
|
||||
width: 90px
|
||||
height: 90px
|
||||
object-fit: cover
|
||||
border: 2px solid var(--mgt2-color-primary)
|
||||
border-radius: 4px
|
||||
cursor: pointer
|
||||
|
||||
.vehicule-header-body
|
||||
flex: 1
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 0.4rem
|
||||
min-width: 0
|
||||
|
||||
.vehicule-name
|
||||
font-family: "Barlow Condensed", sans-serif
|
||||
font-size: 1.6rem
|
||||
font-weight: 700
|
||||
font-style: italic
|
||||
color: var(--mgt2-color-form)
|
||||
background: transparent
|
||||
border: none
|
||||
border-bottom: 1px solid var(--mgt2-color-primary)
|
||||
width: 100%
|
||||
padding: 0
|
||||
&:focus
|
||||
outline: none
|
||||
border-bottom-color: var(--mgt2-color-secondary)
|
||||
|
||||
.vehicule-header-stats
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: flex-start
|
||||
gap: 0.75rem
|
||||
flex-wrap: wrap
|
||||
|
||||
// ── Stat boxes (hull, armor) ────────────────────────
|
||||
.vehicule-stat-box
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
background: var(--mgt2-bgcolor-primary)
|
||||
border: 1px solid var(--mgt2-color-primary)
|
||||
border-radius: 4px
|
||||
padding: 3px 8px
|
||||
min-width: 4rem
|
||||
|
||||
label
|
||||
font-family: "Barlow Condensed", sans-serif
|
||||
font-size: 0.65rem
|
||||
font-weight: 700
|
||||
text-transform: uppercase
|
||||
color: var(--mgt2-color-primary)
|
||||
line-height: 1.2
|
||||
white-space: nowrap
|
||||
|
||||
.vehicule-stat-value
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: 2px
|
||||
|
||||
span
|
||||
color: var(--mgt2-color-primary)
|
||||
font-weight: 700
|
||||
|
||||
input[type="number"]
|
||||
width: 2.8rem
|
||||
text-align: center
|
||||
background: transparent
|
||||
border: none
|
||||
color: var(--mgt2-color-form)
|
||||
font-family: "Rubik", monospace
|
||||
font-size: 1rem
|
||||
font-weight: 600
|
||||
padding: 0
|
||||
&:focus
|
||||
outline: none
|
||||
border-bottom: 1px solid var(--mgt2-color-secondary)
|
||||
|
||||
.vehicule-hull
|
||||
min-width: 6rem
|
||||
.vehicule-stat-value input[type="number"]
|
||||
width: 2.5rem
|
||||
|
||||
// ── Armor group ────────────────────────────────────
|
||||
.vehicule-armor-group
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 2px
|
||||
|
||||
.vehicule-armor-label
|
||||
font-family: "Barlow Condensed", sans-serif
|
||||
font-size: 0.65rem
|
||||
font-weight: 700
|
||||
text-transform: uppercase
|
||||
color: var(--mgt2-color-primary)
|
||||
text-align: center
|
||||
|
||||
.vehicule-armor-row
|
||||
display: flex
|
||||
flex-direction: row
|
||||
gap: 4px
|
||||
|
||||
.vehicule-armor-box
|
||||
min-width: 3.5rem
|
||||
|
||||
// ── Body wrapper (contains tabs + sidebar nav) ─────
|
||||
// min-height ensures both tabs in the sidebar are always visible
|
||||
// (2 tabs × 54px each + 8px padding = 116px)
|
||||
.vehicule-content
|
||||
flex: 1
|
||||
display: flex
|
||||
flex-direction: column
|
||||
overflow: hidden
|
||||
min-height: 320px
|
||||
|
||||
// ── Tab panels ─────────────────────────────────────
|
||||
.vehicule-tab
|
||||
flex: 1
|
||||
overflow-y: auto
|
||||
padding: 0.75rem
|
||||
display: none
|
||||
|
||||
&.active
|
||||
display: block
|
||||
|
||||
// ── Stats grid ─────────────────────────────────────
|
||||
.vehicule-stats-grid
|
||||
display: grid
|
||||
grid-template-columns: 1fr 1fr
|
||||
gap: 4px 12px
|
||||
|
||||
.vehicule-field
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: center
|
||||
gap: 8px
|
||||
padding: 3px 0
|
||||
border-bottom: 1px solid rgba(0,0,0,0.08)
|
||||
|
||||
&:last-child
|
||||
border-bottom: none
|
||||
|
||||
label
|
||||
font-family: "Barlow Condensed", sans-serif
|
||||
font-size: 0.72rem
|
||||
font-weight: 700
|
||||
text-transform: uppercase
|
||||
color: var(--mgt2-color-primary)
|
||||
flex: 0 0 120px
|
||||
white-space: nowrap
|
||||
|
||||
input[type="number"],
|
||||
select
|
||||
flex: 1
|
||||
background: transparent
|
||||
border: 1px solid transparent
|
||||
border-radius: 3px
|
||||
color: var(--mgt2-color-form)
|
||||
font-family: "Barlow Condensed", sans-serif
|
||||
font-size: 0.9rem
|
||||
padding: 2px 4px
|
||||
&:focus
|
||||
outline: none
|
||||
border-color: var(--mgt2-color-primary)
|
||||
background: rgba(255,255,255,0.1)
|
||||
|
||||
input[type="number"]
|
||||
text-align: center
|
||||
width: 4rem
|
||||
|
||||
select
|
||||
cursor: pointer
|
||||
|
||||
|
||||
// ── HTMLField editor min-height in description tab ──────────────────────────
|
||||
.mgt2.vehicule
|
||||
.vehicule-tab[data-tab="description"]
|
||||
.editor,
|
||||
.editor-content,
|
||||
prose-mirror
|
||||
min-height: 300px !important
|
||||
height: auto !important
|
||||
17
src/sass/mgt2.sass
Normal file
17
src/sass/mgt2.sass
Normal file
@@ -0,0 +1,17 @@
|
||||
@import 'utils/typography'
|
||||
@import 'utils/colors'
|
||||
@import 'utils/global'
|
||||
@import 'utils/window'
|
||||
@import 'utils/flex'
|
||||
|
||||
@import 'components/_forms'
|
||||
@import 'components/_dialog'
|
||||
@import 'components/_character'
|
||||
@import 'components/_item'
|
||||
@import 'components/_chat-sidebar'
|
||||
|
||||
@import 'components/_tabs'
|
||||
@import 'components/_tab-sidebar'
|
||||
@import 'components/_tables'
|
||||
@import 'components/_creature'
|
||||
@import 'components/_vehicule'
|
||||
63
src/sass/utils/_colors.sass
Normal file
63
src/sass/utils/_colors.sass
Normal file
@@ -0,0 +1,63 @@
|
||||
$primary-color: #3498db
|
||||
$secondary-color: #2ecc71
|
||||
$background-color: #ecf0f1
|
||||
$text-color: #34495e
|
||||
|
||||
.black-and-red
|
||||
--mgt2-color-form: #0A0405
|
||||
--mgt2-bgcolor-form: #fff
|
||||
--mgt2-color-primary: #EE4050
|
||||
--mgt2-color-primary-active: #AF2F3C
|
||||
--mgt2-bgcolor-primary: #0A0405
|
||||
--mgt2-color-primary-light: #4b4a44
|
||||
--mgt2-color-warning: #EE4050
|
||||
--mgt2-color-dm: #fff
|
||||
--mgt2-bgcolor-dm: #0A0405
|
||||
--mgt2-color-software: #fff
|
||||
--mgt2-bgcolor-software: #0A0405
|
||||
--mgt2-input-color: #0A0405
|
||||
--mgt2-input-bgcolor: #fff
|
||||
--mgt2-editor-border: #C6C6C6
|
||||
--mgt2-row-hover: #F2F2F2
|
||||
--mgt2-subrow-color: #727272
|
||||
--mgt2-row-inactive-icon: #b5b3a4
|
||||
--mgt2-encumbrance-normal: #D94826
|
||||
--mgt2-encumbrance-heavy: #D82727
|
||||
|
||||
.mwamba
|
||||
--mgt2-color-form: #0A0405
|
||||
--mgt2-bgcolor-form: #fff
|
||||
--mgt2-color-primary: #2A9932
|
||||
--mgt2-color-primary-active: #40ED4E
|
||||
--mgt2-bgcolor-primary: #0A0405
|
||||
--mgt2-color-primary-light: #4b4a44
|
||||
--mgt2-color-warning: #EE4050
|
||||
--mgt2-color-dm: #fff
|
||||
--mgt2-bgcolor-dm: #0A0405
|
||||
--mgt2-color-software: #fff
|
||||
--mgt2-bgcolor-software: #0A0405
|
||||
--mgt2-input-color: #0A0405
|
||||
--mgt2-input-bgcolor: #fff
|
||||
--mgt2-editor-border: #C6C6C6
|
||||
--mgt2-row-hover: #F2F2F2
|
||||
--mgt2-subrow-color: #727272
|
||||
--mgt2-row-inactive-icon: #b5b3a4
|
||||
|
||||
.blue
|
||||
--mgt2-color-form: #0A0405
|
||||
--mgt2-bgcolor-form: #fff
|
||||
--mgt2-color-primary: #91AAC8
|
||||
--mgt2-color-primary-active: #BCDCFF
|
||||
--mgt2-bgcolor-primary: #0A0405
|
||||
--mgt2-color-primary-light: #4b4a44
|
||||
--mgt2-color-warning: #EE4050
|
||||
--mgt2-color-dm: #fff
|
||||
--mgt2-bgcolor-dm: #0A0405
|
||||
--mgt2-color-software: #fff
|
||||
--mgt2-bgcolor-software: #0A0405
|
||||
--mgt2-input-color: #0A0405
|
||||
--mgt2-input-bgcolor: #fff
|
||||
--mgt2-editor-border: #C6C6C6
|
||||
--mgt2-row-hover: #F2F2F2
|
||||
--mgt2-subrow-color: #727272
|
||||
--mgt2-row-inactive-icon: #b5b3a4
|
||||
18
src/sass/utils/_flex.sass
Normal file
18
src/sass/utils/_flex.sass
Normal file
@@ -0,0 +1,18 @@
|
||||
.mgt2
|
||||
.flex-fix
|
||||
flex-grow: 0 !important
|
||||
flex-shrink: 0 !important
|
||||
.flex-basis-10
|
||||
flex-basis: 10%
|
||||
.flex-basis-20
|
||||
flex-basis: 20%
|
||||
.flex-basis-30
|
||||
flex-basis: 30%
|
||||
.flex-basis-40
|
||||
flex-basis: 40%
|
||||
.flex-basis-50
|
||||
flex-basis: 50%
|
||||
.flex-basis-60
|
||||
flex-basis: 60%
|
||||
.flex-basis-70
|
||||
flex-basis: 70%
|
||||
39
src/sass/utils/_global.sass
Normal file
39
src/sass/utils/_global.sass
Normal file
@@ -0,0 +1,39 @@
|
||||
.upcase
|
||||
text-transform: uppercase
|
||||
|
||||
.w1-10
|
||||
width: calc(100% / 10)
|
||||
|
||||
.w2-10
|
||||
width: calc(100% / 10 * 2)
|
||||
|
||||
.w3-10
|
||||
width: calc(100% / 10 * 3)
|
||||
|
||||
.w4-10
|
||||
width: calc(100% / 10 * 4)
|
||||
|
||||
.w5-10
|
||||
width: calc(100% / 10 * 5)
|
||||
|
||||
.h100
|
||||
height: 100%
|
||||
|
||||
.w100
|
||||
width: 100%
|
||||
|
||||
.mgt2
|
||||
a:hover
|
||||
text-shadow: none
|
||||
|
||||
.w-100
|
||||
width: 100%
|
||||
|
||||
.mb-1
|
||||
margin-bottom: 8px
|
||||
|
||||
.mt-1, .mt-05
|
||||
margin-top: 8px
|
||||
|
||||
.mt-2
|
||||
margin-top: 14px
|
||||
3
src/sass/utils/_typography.sass
Normal file
3
src/sass/utils/_typography.sass
Normal file
@@ -0,0 +1,3 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Barlow+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap')
|
||||
@import url('https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&display=swap')
|
||||
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap')
|
||||
4
src/sass/utils/_variables.sass
Normal file
4
src/sass/utils/_variables.sass
Normal file
@@ -0,0 +1,4 @@
|
||||
$myFont: Helvetica, sans-serif
|
||||
$myColor: red
|
||||
$myFontSize: 18px
|
||||
$myWidth: 680px
|
||||
19
src/sass/utils/_window.sass
Normal file
19
src/sass/utils/_window.sass
Normal file
@@ -0,0 +1,19 @@
|
||||
.mgt2
|
||||
&.sheet
|
||||
header
|
||||
&.window-header
|
||||
color: var(--mgt2-color-primary)
|
||||
background-color: var(--mgt2-bgcolor-primary)
|
||||
h4
|
||||
&.window-title
|
||||
font-weight: bold
|
||||
text-transform: uppercase
|
||||
&.sheet
|
||||
.window-content
|
||||
background: var(--mgt2-bgcolor-form)
|
||||
color: var(--mgt2-color-form)
|
||||
padding: 0
|
||||
|
||||
.nopad
|
||||
.window-content
|
||||
padding: 0
|
||||
11
src/todo.md
Normal file
11
src/todo.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# BUGS
|
||||
|
||||
|
||||
# Chose à faire
|
||||
|
||||
- Enlever les styles inlines
|
||||
|
||||
Actors
|
||||
- NPC
|
||||
- Creature
|
||||
- Container
|
||||
1026
styles/mgt2.min.css
vendored
1026
styles/mgt2.min.css
vendored
File diff suppressed because one or more lines are too long
1
styles/mgt2.min.css.map
Normal file
1
styles/mgt2.min.css.map
Normal file
File diff suppressed because one or more lines are too long
147
system.json
147
system.json
@@ -1,45 +1,106 @@
|
||||
{
|
||||
"id": "mgt2",
|
||||
"version": "0.1.2",
|
||||
"title": "MGT2 - Mongoose Traveller (Unofficial)",
|
||||
"description": "An unofficial implementation of Mongoose Publishing Traveller (VO/VF). Traveller is the property of Mongoose Publishing, and can be purchased at https://www.mongoosepublishing.com",
|
||||
"background": "systems/mgt2/assets/screens/rosette-nebula-ngc2239-hoo.webp",
|
||||
"url": "https://github.com/JDR-Ninja/foundryvtt-mgt2",
|
||||
"manifest": "https://github.com/JDR-Ninja/foundryvtt-mgt2/releases/latest/download/system.json",
|
||||
"readme": "https://raw.githubusercontent.com/JDR-Ninja/foundryvtt-mgt2/main/README.md",
|
||||
"download": "https://github.com/JDR-Ninja/foundryvtt-mgt2/releases/download/v0.1.2/mgt2.zip",
|
||||
"changelog": "https://raw.githubusercontent.com/JDR-Ninja/foundryvtt-mgt2/main/CHANGELOG.md",
|
||||
"authors": [
|
||||
{
|
||||
"name": "JdR Ninja",
|
||||
"url": "https://www.jdr.ninja/",
|
||||
"discord": "jdr.ninja"
|
||||
}
|
||||
],
|
||||
"esmodules": [
|
||||
"mgt2.bundle.js"
|
||||
],
|
||||
"styles": [
|
||||
"styles/mgt2.min.css"
|
||||
],
|
||||
"packs": [],
|
||||
"languages": [
|
||||
{
|
||||
"lang": "en",
|
||||
"name": "English",
|
||||
"path": "lang/en.json"
|
||||
},
|
||||
{
|
||||
"lang": "fr",
|
||||
"name": "Français",
|
||||
"path": "lang/fr.json"
|
||||
}
|
||||
],
|
||||
"compatibility": {
|
||||
"minimum": "11",
|
||||
"verified": "11.315"
|
||||
},
|
||||
"gridDistance": 1.5,
|
||||
"gridUnits": "m",
|
||||
"primaryTokenAttribute": "life"
|
||||
"id": "mgt2",
|
||||
"version": "14.0.0",
|
||||
"title": "MGT2 - Mongoose Traveller (Unofficial)",
|
||||
"description": "An unofficial implementation of Mongoose Publishing Traveller (VO/VF). Traveller is the property of Mongoose Publishing, and can be purchased at https://www.mongoosepublishing.com",
|
||||
"background": "systems/mgt2/assets/screens/rosette-nebula-ngc2239-hoo.webp",
|
||||
"url": "https://github.com/JDR-Ninja/foundryvtt-mgt2",
|
||||
"manifest": "https://github.com/JDR-Ninja/foundryvtt-mgt2/releases/latest/download/system.json",
|
||||
"readme": "https://raw.githubusercontent.com/JDR-Ninja/foundryvtt-mgt2/main/README.md",
|
||||
"download": "https://github.com/JDR-Ninja/foundryvtt-mgt2/releases/download/v0.1.4/mgt2.zip",
|
||||
"changelog": "https://raw.githubusercontent.com/JDR-Ninja/foundryvtt-mgt2/main/CHANGELOG.md",
|
||||
"authors": [
|
||||
{
|
||||
"name": "JdR Ninja",
|
||||
"url": "https://www.jdr.ninja/",
|
||||
"discord": "jdr.ninja"
|
||||
}
|
||||
],
|
||||
"esmodules": [
|
||||
"mgt2.bundle.js"
|
||||
],
|
||||
"styles": [
|
||||
"styles/mgt2.min.css"
|
||||
],
|
||||
"packs": [],
|
||||
"languages": [
|
||||
{
|
||||
"lang": "en",
|
||||
"name": "English",
|
||||
"path": "lang/en.json"
|
||||
},
|
||||
{
|
||||
"lang": "fr",
|
||||
"name": "Français",
|
||||
"path": "lang/fr.json"
|
||||
}
|
||||
],
|
||||
"documentTypes": {
|
||||
"Actor": {
|
||||
"character": {
|
||||
"htmlFields": [
|
||||
"personal.speciesText.descriptionLong",
|
||||
"biography",
|
||||
"finance.notes",
|
||||
"notes"
|
||||
]
|
||||
},
|
||||
"vehicule": {
|
||||
"htmlFields": [
|
||||
"description",
|
||||
"notes"
|
||||
]
|
||||
},
|
||||
"creature": {
|
||||
"htmlFields": [
|
||||
"biography",
|
||||
"notes"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Item": {
|
||||
"armor": {
|
||||
"htmlFields": [ "description" ]
|
||||
},
|
||||
"career": {
|
||||
"htmlFields": [ "description" ]
|
||||
},
|
||||
"computer": {
|
||||
"htmlFields": [ "description" ]
|
||||
},
|
||||
"contact": {
|
||||
"htmlFields": [ "description", "notes" ]
|
||||
},
|
||||
"container": {
|
||||
"htmlFields": [ "description", "lockedDescription" ]
|
||||
},
|
||||
"disease": {
|
||||
"htmlFields": [ "description" ]
|
||||
},
|
||||
"equipment": {
|
||||
"htmlFields": [ "description" ]
|
||||
},
|
||||
"item": {
|
||||
"htmlFields": [ "description" ]
|
||||
},
|
||||
"species": {
|
||||
"htmlFields": [ "description", "descriptionLong" ]
|
||||
},
|
||||
"talent": {
|
||||
"htmlFields": [ "description" ]
|
||||
},
|
||||
"weapon": {
|
||||
"htmlFields": [ "description" ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
"minimum": "13",
|
||||
"verified": "14"
|
||||
},
|
||||
"grid": {
|
||||
"distance": 1.5,
|
||||
"units": "m"
|
||||
},
|
||||
"primaryTokenAttribute": "life"
|
||||
}
|
||||
@@ -2,11 +2,13 @@
|
||||
"Actor": {
|
||||
"types": [
|
||||
"character",
|
||||
"vehicule"
|
||||
"vehicule",
|
||||
"creature"
|
||||
],
|
||||
"htmlFields": ["notes"],
|
||||
"htmlFields": ["notes", "biography"],
|
||||
"character": {},
|
||||
"vehicule": {}
|
||||
"vehicule": {},
|
||||
"creature": {}
|
||||
},
|
||||
"Item": {
|
||||
"types": [
|
||||
@@ -1,6 +1,6 @@
|
||||
<form class="{{cssClass}} flexcol" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.config.psionic" data-dtype="Boolean" {{checked system.config.psionic}} />{{ localize 'MGT2.Actor.ShowPsionicTalents' }}</label>
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="psionic" data-dtype="Boolean" {{checked system.config.psionic}} />{{ localize 'MGT2.Actor.ShowPsionicTalents' }}</label>
|
||||
</div>
|
||||
<fieldset>
|
||||
<legend>{{ localize 'MGT2.Actor.Initiative' }}</legend>
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
<form class="{{cssClass}} flexcol" autocomplete="off" style="align-content: flex-start;align-items: baseline;overflow: hidden;height: 100%;">
|
||||
<nav class="sheet-sidebar tabs" data-group="sidebar">
|
||||
<!-- <a class="item tab-select" data-tab="personal" title="Personal"><i class="fa-solid fa-id-card"></i></a> -->
|
||||
<a class="item tab-select" data-tab="health" title="{{ localize 'MGT2.Actor.Health' }}"><i class="fa-solid fa-heart-pulse"></i></a>
|
||||
<a class="item tab-select" data-tab="skills" title="{{ localize 'MGT2.Actor.TabSkills' }}"><i class="fa-solid fa-head-side"></i></a>
|
||||
<a class="item tab-select" data-tab="inventory" title="{{ localize 'MGT2.Actor.Inventory' }}"><i class="fa-solid fa-briefcase-blank"></i></a>
|
||||
<a class="item tab-select" data-tab="relations" title="{{ localize 'MGT2.Actor.Contacts' }}"><i class="fa-solid fa-users"></i></a>
|
||||
<a class="item tab-select" data-tab="notes" title="{{ localize 'MGT2.Actor.Notes' }}"><i class="fa-solid fa-books"></i></a>
|
||||
<a class="item tab-select" data-tab="biography" title="{{ localize 'MGT2.Actor.Biography' }}"><i class="fa-solid fa-book-user"></i></a>
|
||||
</nav>
|
||||
<div class="{{cssClass}} flexcol" style="align-content: flex-start;align-items: baseline;overflow: hidden;height: 100%;">
|
||||
<section class="character-header">
|
||||
<div class="character-header-img">
|
||||
<img class="profile" src="{{img}}" data-edit="img" title="{{name}}" height="130" width="100" />
|
||||
@@ -281,6 +272,22 @@
|
||||
<label class="upcase">{{ localize 'MGT2.Actor.Rads' }}</label>
|
||||
<input class="field" name="system.health.radiations" type="text" value="{{system.health.radiations}}" />
|
||||
</div>
|
||||
<!-- HEALING SECTION -->
|
||||
<div class="header upcase">{{ localize 'MGT2.Healing.Title' }}</div>
|
||||
<div class="healing-buttons" style="display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px;">
|
||||
<button type="button" data-action="heal" data-heal-type="firstaid" class="button sm" title="{{ localize 'MGT2.Healing.FirstAid' }}">
|
||||
<i class="fas fa-bandage"></i> {{ localize 'MGT2.Healing.FirstAid' }}
|
||||
</button>
|
||||
<button type="button" data-action="heal" data-heal-type="surgery" class="button sm" title="{{ localize 'MGT2.Healing.Surgery' }}">
|
||||
<i class="fas fa-flask-vial"></i> {{ localize 'MGT2.Healing.Surgery' }}
|
||||
</button>
|
||||
<button type="button" data-action="heal" data-heal-type="medical" class="button sm" title="{{ localize 'MGT2.Healing.MedicalCare' }}">
|
||||
<i class="fas fa-hospital"></i> {{ localize 'MGT2.Healing.MedicalCare' }}
|
||||
</button>
|
||||
<button type="button" data-action="heal" data-heal-type="natural" class="button sm" title="{{ localize 'MGT2.Healing.NaturalHealing' }}">
|
||||
<i class="fas fa-leaf"></i> {{ localize 'MGT2.Healing.NaturalHealing' }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<div class="table-row heading color-1">
|
||||
<div class="row-item row-item-30 row-item-left upcase">{{ localize 'MGT2.Actor.Wounds' }}</div>
|
||||
@@ -387,7 +394,7 @@
|
||||
{{#each skills as |skill id|}}
|
||||
<div class="table-row" data-item-id="{{skill._id}}" role="rowgroup">
|
||||
<div class="row-item row-item-30 row-item-left flex-fix">
|
||||
<a data-roll="skill" data-roll-skill="{{skill._id}}"><i class="fa-solid fa-dice"></i></a>{{skill.name}}{{#if skill.system.skill.speciality}} ({{skill.system.skill.speciality}}){{/if}}
|
||||
<a class="roll" data-roll="skill" data-roll-skill="{{skill._id}}"><i class="fa-solid fa-dice"></i></a>{{skill.name}}{{#if skill.system.skill.speciality}} ({{skill.system.skill.speciality}}){{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-center">{{skill.system.level}}</div>
|
||||
<div class="row-item item-controls">
|
||||
@@ -410,7 +417,7 @@
|
||||
{{#each psionics as |psionic id|}}
|
||||
<div class="table-row" data-item-id="{{psionic._id}}" role="rowgroup">
|
||||
<div class="row-item row-item-left">
|
||||
<a data-roll="psionic" data-item-id="{{psionic._id}}"><i class="fa-solid fa-dice"></i></a>{{psionic.name}}
|
||||
<a class="roll" data-roll="psionic" data-item-id="{{psionic._id}}"><i class="fa-solid fa-dice"></i></a>{{psionic.name}}
|
||||
</div>
|
||||
<div class="row-item row-item-10 row-item-center flex-fix">{{psionic.system.level}}</div>
|
||||
<div class="row-item row-item-10 row-item-center flex-fix">{{psionic.system.psionic.cost}}</div>
|
||||
@@ -446,13 +453,13 @@
|
||||
{{#each weapons as |weapon id|}}
|
||||
<div class="table-row{{#if weapon.subInfo}} table-row-mb-4{{/if}} drag-item-list" data-item-id="{{weapon._id}}" role="rowgroup">
|
||||
<div class="row-item row-item-30 row-item-left flex-3 row-large">
|
||||
<a data-roll="item" data-item-id="{{weapon._id}}"><i class="fa-solid fa-dice"></i></a>{{weapon.name}}{{#if weapon.subInfo}}<div class="item-options">{{weapon.subInfo}}</div>{{/if}}
|
||||
<a class="roll" data-roll="item" data-item-id="{{weapon._id}}"><i class="fa-solid fa-dice"></i></a>{{weapon.name}}{{#if weapon.subInfo}}<div class="item-options">{{weapon.subInfo}}</div>{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-2 row-item-center">{{weapon.range}}</div>
|
||||
<div class="row-item row-item-20 row-item-center">{{weapon.system.damage}}</div>
|
||||
<div class="row-item row-item-2 row-item-right flex-fix">{{weapon.weight}}</div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix">
|
||||
<a class="item-control item-equip {{weapon.toggleClass}}" title="Equip"><i class="fa-solid fa-shield-halved"></i></a>
|
||||
<a class="item-control item-equip {{weapon.toggleClass}}" data-action="equipItem" title="Equip"><i class="fa-solid fa-shield-halved"></i></a>
|
||||
<a class="item-control item-storage-in" title="{{ localize 'MGT2.Actor.StoreItem' }}"><i class="fa-solid fa-inbox-in"></i></a>
|
||||
<a class="item-control item-edit" title="{{ localize 'MGT2.Actor.EditWeapon' }}"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-control item-delete" title="{{ localize 'MGT2.Actor.DeleteWeapon' }}"><i class="fas fa-trash"></i></a>
|
||||
@@ -471,13 +478,13 @@
|
||||
{{#each armors as |armor id|}}
|
||||
<div class="table-row{{#if armor.subInfo}} table-row-mb-4{{/if}} drag-item-list" data-item-id="{{armor._id}}" role="rowgroup">
|
||||
<div class="row-item row-item-40 row-item-left">
|
||||
<a data-roll="item" data-item-id="{{armor._id}}"><i class="fa-solid fa-dice"></i></a>{{armor.name}}{{#if armor.subInfo}}<div class="item-options">{{armor.subInfo}}</div>{{/if}}
|
||||
<a class="roll" data-roll="item" data-item-id="{{armor._id}}"><i class="fa-solid fa-dice"></i></a>{{armor.name}}{{#if armor.subInfo}}<div class="item-options">{{armor.subInfo}}</div>{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-10 row-item-center">{{armor.system.radiations}}</div>
|
||||
<div class="row-item row-item-10 row-item-center">{{armor.system.protection}}</div>
|
||||
<div class="row-item row-item-2 row-item-right flex-fix">{{armor.weight}}</div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix">
|
||||
<a class="item-control item-equip {{armor.toggleClass}}" title="Equip"><i class="fa-solid fa-shield-halved"></i></a>
|
||||
<a class="item-control item-equip {{armor.toggleClass}}" data-action="equipItem" title="Equip"><i class="fa-solid fa-shield-halved"></i></a>
|
||||
<a class="item-control item-storage-in" title="{{ localize 'MGT2.Actor.StoreItem' }}"><i class="fa-solid fa-inbox-in"></i></a>
|
||||
<a class="item-control item-edit" title="{{ localize 'MGT2.Actor.EditArmor' }}"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-control item-delete" title="{{ localize 'MGT2.Actor.DeleteArmor' }}"><i class="fas fa-trash"></i></a>
|
||||
@@ -496,7 +503,7 @@
|
||||
<div class="row-item row-item-30 row-item-left">{{augment.name}}</div>
|
||||
<div class="row-item row-item-40 row-item-left">{{augment.system.improvement}}</div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix">
|
||||
<a class="item-control item-equip {{augment.toggleClass}}" title="Equip"><i class="fa-solid fa-shield-halved"></i></a>
|
||||
<a class="item-control item-equip {{augment.toggleClass}}" data-action="equipItem" title="Equip"><i class="fa-solid fa-shield-halved"></i></a>
|
||||
<a class="item-control item-storage-in" title="{{ localize 'MGT2.Actor.StoreItem' }}"><i class="fa-solid fa-inbox-in"></i></a>
|
||||
<a class="item-control item-edit" title="{{ localize 'MGT2.Actor.EditAugment' }}"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-control item-delete" title="{{ localize 'MGT2.Actor.DeleteAugment' }}"><i class="fas fa-trash"></i></a>
|
||||
@@ -514,14 +521,14 @@
|
||||
{{#each computers as |computer id|}}
|
||||
<div class="table-row drop-item-list" data-item-id="{{computer._id}}" role="rowgroup">
|
||||
<div class="row-item row-item-50 row-item-left">
|
||||
<a data-roll="item" data-item-id="{{computer._id}}"><i class="fa-solid fa-dice"></i></a>{{computer.name}}{{#if computer.subInfo}}<div class="item-options">{{computer.subInfo}}</div>{{/if}}
|
||||
<a class="roll" data-roll="item" data-item-id="{{computer._id}}"><i class="fa-solid fa-dice"></i></a>{{computer.name}}{{#if computer.subInfo}}<div class="item-options">{{computer.subInfo}}</div>{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-2 row-item-center {{overloadClass}}">{{computer.system.processingUsed}}/{{computer.system.processing}}
|
||||
{{#if computer.system.overload}}<a title="Overload"><i class="fa-solid fa-triangle-exclamation"></i></a>{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-2 row-item-right flex-fix">{{computer.weight}}</div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix">
|
||||
<a class="item-control item-equip {{computer.toggleClass}}" title="Equip"><i class="fa-solid fa-shield-halved"></i></a>
|
||||
<a class="item-control item-equip {{computer.toggleClass}}" data-action="equipItem" title="Equip"><i class="fa-solid fa-shield-halved"></i></a>
|
||||
<a class="item-control item-storage-in" title="{{ localize 'MGT2.Actor.StoreItem' }}"><i class="fa-solid fa-inbox-in"></i></a>
|
||||
<a class="item-control item-edit" title="{{ localize 'MGT2.Actor.EditComputer' }}"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-control item-delete" title="{{ localize 'MGT2.Actor.DeleteComputer' }}"><i class="fas fa-trash"></i></a>
|
||||
@@ -558,7 +565,7 @@
|
||||
<div class="row-item row-item-85 row-item-left upcase"><i class="fa-solid fa-toolbox"></i>{{ localize 'MGT2.Items.Equipments' }}</div>
|
||||
<div class="row-item row-item-2 row-item-center">{{ localize 'MGT2.Items.Qty' }}</div>
|
||||
<div class="row-item row-item-2 row-item-right flex-fix">{{ localize 'MGT2.Items.Weight' }}</div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix"><a class="item-create" data-type-item="equipment" data-create-name="{{ localize 'MGT2.Actor.Equipment' }}" title="{{ localize 'MGT2.Actor.AddEquipment' }}"><i class="fas fa-plus"></i></a></div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix"><a class="item-create" data-type-item="equipment" data-create-name="{{ localize 'MGT2.Actor.NewEquipment' }}" title="{{ localize 'MGT2.Actor.AddEquipment' }}"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each equipments as |equipment id|}}
|
||||
<div class="table-row drag-item-list" data-item-id="{{equipment._id}}" role="rowgroup">
|
||||
@@ -566,7 +573,7 @@
|
||||
<div class="row-item row-item-2 row-item-center">{{equipment.system.quantity}}</div>
|
||||
<div class="row-item row-item-2 row-item-right flex-fix">{{equipment.weight}}</div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix">
|
||||
<a class="item-control item-equip {{equipment.toggleClass}}" title="{{ localize 'MGT2.Actor.EquipUnequip' }}"><i class="fa-solid fa-shield-halved"></i></a>
|
||||
<a class="item-control item-equip {{equipment.toggleClass}}" data-action="equipItem" title="{{ localize 'MGT2.Actor.EquipUnequip' }}"><i class="fa-solid fa-shield-halved"></i></a>
|
||||
<a class="item-control item-storage-in" title="{{ localize 'MGT2.Actor.StoreEquipment' }}"><i class="fa-solid fa-inbox-in"></i></a>
|
||||
<a class="item-control item-edit" title="{{ localize 'MGT2.Actor.EditEquipment' }}"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-control item-delete" title="{{ localize 'MGT2.Actor.DeleteEquipment' }}"><i class="fas fa-trash"></i></a>
|
||||
@@ -582,11 +589,11 @@
|
||||
<div class="row-item row-item-15 item-controls flex-fix"><a class="item-create" data-type-item="item" data-create-name="{{ localize 'MGT2.Actor.NewItem' }}" title="{{ localize 'MGT2.Actor.AddItem' }}"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each items as |item|}}
|
||||
{{#if (eq item.type "container")}}
|
||||
<div class="table-row drop-item-list" data-item-id="{{item._id}}">
|
||||
{{#if (eq item.type "container")}}
|
||||
<div class="table-row drop-item-list" data-item-id="{{item._id}}">
|
||||
{{else}}
|
||||
<div class="table-row drag-item-list" data-item-id="{{item._id}}">
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<div class="row-item row-item-85 row-item-left">{{item.name}}</div>
|
||||
<div class="row-item row-item-2 row-item-center">{{item.system.quantity}}</div>
|
||||
<div class="row-item row-item-2 row-item-right flex-fix">{{item.weight}}</div>
|
||||
@@ -627,7 +634,7 @@
|
||||
<div>
|
||||
<label>{{ localize 'MGT2.Items.Containers' }}</label>
|
||||
<select name="system.containerView">
|
||||
{{selectOptions containers selected=system.containerView nameAttr="_id" labelAttr="display"}}
|
||||
{{selectOptions containers selected=system.containerView valueAttr="_id" labelAttr="display"}}
|
||||
</select>
|
||||
<div class="container-controls">
|
||||
{{#unless containerShowAll}}<a class="container-edit"><i class="fas fa-edit"></i></a>{{/unless}}
|
||||
@@ -638,7 +645,7 @@
|
||||
<div>
|
||||
<label>DropIn</label>
|
||||
<select name="system.containerDropIn">
|
||||
{{selectOptions actorContainers selected=system.containerDropIn nameAttr="_id" labelAttr="display"}}
|
||||
{{selectOptions actorContainers selected=system.containerDropIn valueAttr="_id" labelAttr="display"}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -696,8 +703,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-group mt-1">
|
||||
<label class="upcase">{{ localize 'MGT2.Actor.Notes' }}</label>
|
||||
<textarea name="system.finance.description" rows="3">{{system.finance.description}}</textarea>
|
||||
<label class="upcase">{{ localize 'MGT2.Actor.Notes' }}</label>
|
||||
{{formInput systemFields.finance.fields.notes enriched=enrichedFinanceNotes value=system.finance.notes name="system.finance.notes" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -729,20 +736,27 @@
|
||||
</div>
|
||||
<div class="tab w100 h100" data-group="sidebar" data-tab="notes">
|
||||
<div class="header upcase">{{ localize 'MGT2.Actor.Notes' }}</div>
|
||||
{{editor system.notes target="system.notes" button=true editable=true}}
|
||||
{{formInput systemFields.notes enriched=enrichedNotes value=system.notes name="system.notes" toggled=true}}
|
||||
</div>
|
||||
<div class="tab w100 h100" data-group="sidebar" data-tab="biography">
|
||||
<div class="header upcase">{{ localize 'MGT2.Actor.Biography' }}</div>
|
||||
{{editor system.biography target="system.biography" button=true editable=true}}
|
||||
{{formInput systemFields.biography enriched=enrichedBiography value=system.biography name="system.biography" toggled=true}}
|
||||
</div>
|
||||
{{#if showTrash}}
|
||||
<!-- <div class="tab" data-group="inventory" data-tab="trash">
|
||||
<p>À FAIRE</p>
|
||||
</div> -->
|
||||
{{/if}}
|
||||
<nav class="sheet-sidebar tabs" data-group="sidebar">
|
||||
<a class="item tab-select" data-tab="health" title="{{ localize 'MGT2.Actor.Health' }}"><i class="fa-solid fa-heart-pulse"></i><span class="tab-label">SANTÉ</span></a>
|
||||
<a class="item tab-select" data-tab="skills" title="{{ localize 'MGT2.Actor.TabSkills' }}"><i class="fa-solid fa-head-side"></i><span class="tab-label">COMP.</span></a>
|
||||
<a class="item tab-select" data-tab="inventory" title="{{ localize 'MGT2.Actor.Inventory' }}"><i class="fa-solid fa-briefcase-blank"></i><span class="tab-label">ÉQUIP.</span></a>
|
||||
<a class="item tab-select" data-tab="relations" title="{{ localize 'MGT2.Actor.Contacts' }}"><i class="fa-solid fa-users"></i><span class="tab-label">CONT.</span></a>
|
||||
<a class="item tab-select" data-tab="notes" title="{{ localize 'MGT2.Actor.Notes' }}"><i class="fa-solid fa-books"></i><span class="tab-label">NOTES</span></a>
|
||||
<a class="item tab-select" data-tab="biography" title="{{ localize 'MGT2.Actor.Biography' }}"><i class="fa-solid fa-book-user"></i><span class="tab-label">BIO</span></a>
|
||||
</nav>
|
||||
</div>
|
||||
<section class="actor-footer">
|
||||
<div><a name="config" title="Config" style="margin-right: 0.5rem;"><i class="fa-solid fa-gear"></i></a></div>
|
||||
</section>
|
||||
</div>
|
||||
</form>
|
||||
<section class="actor-footer">
|
||||
<div><a name="config" title="Config" style="margin-right: 0.5rem;"><i class="fa-solid fa-gear"></i></a></div>
|
||||
</section>
|
||||
</div>
|
||||
26
templates/actors/creature-roll-prompt.html
Normal file
26
templates/actors/creature-roll-prompt.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<form class="flexcol" autocomplete="off" style="padding: 0 6px;">
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.Creature.SkillLabel' }}: <strong>{{skillName}}</strong>
|
||||
({{ localize 'MGT2.Creature.SkillLevel' }} {{skillLevel}})</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.CustomDM' }}</label>
|
||||
<input type="number" name="dm" value="0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.Difficulty' }}</label>
|
||||
<select name="difficulty">
|
||||
{{selectOptions config.Difficulty selected="Average" localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.RollMode' }}</label>
|
||||
<select name="rollMode">
|
||||
<option value="publicroll">{{ localize 'MGT2.RollPrompt.PublicRoll' }}</option>
|
||||
<option value="gmroll">{{ localize 'MGT2.RollPrompt.PrivateGMRoll' }}</option>
|
||||
<option value="blindroll">{{ localize 'MGT2.RollPrompt.BlindGMRoll' }}</option>
|
||||
<option value="selfroll">{{ localize 'MGT2.RollPrompt.SelfRoll' }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="hidden" name="difficultyLabel" value="" />
|
||||
</form>
|
||||
273
templates/actors/creature-sheet.html
Normal file
273
templates/actors/creature-sheet.html
Normal file
@@ -0,0 +1,273 @@
|
||||
<div class="{{cssClass}} flexcol creature-sheet">
|
||||
|
||||
{{!-- ── HEADER ── --}}
|
||||
<header class="creature-header">
|
||||
<div class="creature-header-img">
|
||||
<img class="profile" src="{{img}}" data-edit="img" title="{{name}}" />
|
||||
</div>
|
||||
|
||||
<div class="creature-header-body">
|
||||
<input class="creature-name" name="name" type="text" value="{{name}}" placeholder="{{ localize 'MGT2.Creature.Name' }}" />
|
||||
|
||||
<div class="creature-stats-row">
|
||||
{{!-- PdV --}}
|
||||
<div class="creature-stat">
|
||||
<label class="upcase">{{ localize 'MGT2.Creature.Life' }}</label>
|
||||
<div class="creature-stat-value">
|
||||
<input type="number" name="system.life.value" value="{{system.life.value}}" min="0" class="stat-current" />
|
||||
<span>/</span>
|
||||
<input type="number" name="system.life.max" value="{{system.life.max}}" min="0" class="stat-max" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Vitesse --}}
|
||||
<div class="creature-stat">
|
||||
<label class="upcase">{{ localize 'MGT2.Creature.Speed' }}</label>
|
||||
<div class="creature-stat-value">
|
||||
<input type="number" name="system.speed" value="{{system.speed}}" min="0" class="stat-current" />
|
||||
<span class="stat-unit">m</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Armure --}}
|
||||
<div class="creature-stat">
|
||||
<label class="upcase">{{ localize 'MGT2.Creature.Armor' }}</label>
|
||||
<div class="creature-stat-value">
|
||||
<input type="number" name="system.armor" value="{{system.armor}}" min="0" class="stat-current" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Initiative (calculated) --}}
|
||||
<div class="creature-stat">
|
||||
<label class="upcase">{{ localize 'MGT2.Creature.Initiative' }}</label>
|
||||
<div class="creature-stat-value">
|
||||
<span class="stat-readonly">{{showDM system.initiativeBonus}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- PSI (shown only if > 0) --}}
|
||||
{{#if system.psi}}
|
||||
<div class="creature-stat">
|
||||
<label class="upcase">{{ localize 'MGT2.Creature.Psi' }}</label>
|
||||
<div class="creature-stat-value">
|
||||
<input type="number" name="system.psi" value="{{system.psi}}" min="0" class="stat-current" />
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{!-- Comportement --}}
|
||||
<div class="creature-behavior-row">
|
||||
<label class="upcase">{{ localize 'MGT2.Creature.Behavior' }}</label>
|
||||
<select name="system.behavior.type" class="behavior-select">
|
||||
<option value="">—</option>
|
||||
{{selectOptions config.CreatureBehaviorType selected=system.behavior.type localize=true}}
|
||||
</select>
|
||||
<span class="behavior-sep">,</span>
|
||||
<select name="system.behavior.subtype" class="behavior-select">
|
||||
<option value="">—</option>
|
||||
{{selectOptions config.CreatureBehaviorSubType selected=system.behavior.subtype localize=true}}
|
||||
</select>
|
||||
|
||||
{{!-- Taille indicative --}}
|
||||
<span class="creature-size-badge" title="{{ localize 'MGT2.Creature.SizeHint' }}">
|
||||
{{sizeTraitLabel}} — {{sizeLabel}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{!-- ── TAB CONTENT ── --}}
|
||||
<div class="creature-body">
|
||||
|
||||
{{!-- ── TAB : COMBAT (compétences + attaques + traits) ── --}}
|
||||
<div class="tab" data-group="primary" data-tab="combat">
|
||||
|
||||
{{!-- Compétences --}}
|
||||
<div class="header upcase">{{ localize 'MGT2.Creature.TabSkills' }}</div>
|
||||
<div class="table-container">
|
||||
<div class="table-row heading color-1">
|
||||
<div class="row-item row-item-left upcase" style="flex: 3">{{ localize 'MGT2.Creature.SkillName' }}</div>
|
||||
<div class="row-item row-item-center upcase" style="flex: 1">{{ localize 'MGT2.Creature.SkillLevel' }}</div>
|
||||
<div class="row-item row-item-left upcase" style="flex: 3">{{ localize 'MGT2.Creature.SkillNote' }}</div>
|
||||
<div class="row-item row-item-right item-controls" style="flex: 0 0 3rem">
|
||||
{{#if isEditable}}<a data-action="addSkill" data-prop="skills" title="{{ localize 'MGT2.Creature.AddSkill' }}"><i class="fas fa-plus"></i></a>{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{#each system.skills as |skill i|}}
|
||||
<div class="table-row">
|
||||
<div class="row-item row-item-left" style="flex: 3">
|
||||
{{#if ../isEditable}}
|
||||
<input type="text" name="system.skills.{{i}}.name" value="{{skill.name}}" placeholder="{{ localize 'MGT2.Creature.SkillName' }}" />
|
||||
{{else}}
|
||||
<span>{{skill.name}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-center" style="flex: 1">
|
||||
{{#if ../isEditable}}
|
||||
<input type="number" name="system.skills.{{i}}.level" value="{{skill.level}}" min="-3" max="6" class="text-center" />
|
||||
{{else}}
|
||||
<span>{{showDM skill.level}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-left" style="flex: 3">
|
||||
{{#if ../isEditable}}
|
||||
<input type="text" name="system.skills.{{i}}.note" value="{{skill.note}}" placeholder="{{ localize 'MGT2.Creature.SkillNote' }}" />
|
||||
{{else}}
|
||||
<span class="text-muted">{{skill.note}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-right item-controls" style="flex: 0 0 3rem">
|
||||
<a data-action="rollSkill" data-index="{{i}}" title="{{ localize 'MGT2.Creature.RollSkill' }}"><i class="fas fa-dice-d6"></i></a>
|
||||
{{#if ../isEditable}}
|
||||
<a data-action="deleteSkill" data-prop="skills" data-index="{{i}}" title="{{ localize 'MGT2.Creature.Delete' }}"><i class="fas fa-trash"></i></a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{#unless system.skills.length}}
|
||||
<div class="table-row">
|
||||
<div class="row-item row-item-center text-muted" style="flex:1">{{ localize 'MGT2.Creature.NoSkills' }}</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
{{!-- Attaques --}}
|
||||
<div class="header upcase">{{ localize 'MGT2.Creature.TabAttacks' }}</div>
|
||||
<div class="table-container">
|
||||
<div class="table-row heading color-1">
|
||||
<div class="row-item row-item-left upcase" style="flex: 3">{{ localize 'MGT2.Creature.AttackName' }}</div>
|
||||
<div class="row-item row-item-center upcase" style="flex: 2">{{ localize 'MGT2.Creature.AttackDamage' }}</div>
|
||||
<div class="row-item row-item-left upcase" style="flex: 3">{{ localize 'MGT2.Creature.AttackSkill' }}</div>
|
||||
<div class="row-item row-item-left upcase" style="flex: 3">{{ localize 'MGT2.Items.Description' }}</div>
|
||||
<div class="row-item row-item-right item-controls" style="flex: 0 0 4rem">
|
||||
{{#if isEditable}}<a data-action="addAttack" data-prop="attacks" title="{{ localize 'MGT2.Creature.AddAttack' }}"><i class="fas fa-plus"></i></a>{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{#each system.attacks as |atk i|}}
|
||||
<div class="table-row">
|
||||
<div class="row-item row-item-left" style="flex: 3">
|
||||
{{#if ../isEditable}}
|
||||
<input type="text" name="system.attacks.{{i}}.name" value="{{atk.name}}" placeholder="{{ localize 'MGT2.Creature.AttackName' }}" />
|
||||
{{else}}
|
||||
<span>{{atk.name}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-center creature-damage" style="flex: 2">
|
||||
{{#if ../isEditable}}
|
||||
<input type="text" name="system.attacks.{{i}}.damage" value="{{atk.damage}}" class="text-center" />
|
||||
{{else}}
|
||||
<span class="damage-formula">{{atk.damage}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-left" style="flex: 3">
|
||||
{{#if ../isEditable}}
|
||||
<select name="system.attacks.{{i}}.skill">
|
||||
<option value="-1">— {{ localize 'MGT2.RollPrompt.NoSkill' }} —</option>
|
||||
{{#each ../system.skills as |sk si|}}
|
||||
<option value="{{si}}" {{#if (eq si ../skill)}}selected{{/if}}>{{sk.name}} ({{showDM sk.level}})</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
{{else}}
|
||||
{{#if (gte atk.skill 0)}}
|
||||
<span>{{lookup (lookup ../system.skills atk.skill) "name"}}</span>
|
||||
{{else}}
|
||||
<span class="text-muted">—</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-left" style="flex: 3">
|
||||
{{#if ../isEditable}}
|
||||
<input type="text" name="system.attacks.{{i}}.description" value="{{atk.description}}" />
|
||||
{{else}}
|
||||
<span class="text-muted">{{atk.description}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-right item-controls" style="flex: 0 0 4rem">
|
||||
<a data-action="rollAttack" data-index="{{i}}" title="{{ localize 'MGT2.Creature.RollAttack' }}"><i class="fas fa-dice-d6 color-primary"></i></a>
|
||||
{{#if ../isEditable}}
|
||||
<a data-action="deleteAttack" data-prop="attacks" data-index="{{i}}" title="{{ localize 'MGT2.Creature.Delete' }}"><i class="fas fa-trash"></i></a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{#unless system.attacks.length}}
|
||||
<div class="table-row">
|
||||
<div class="row-item row-item-center text-muted" style="flex:1">{{ localize 'MGT2.Creature.NoAttacks' }}</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
{{!-- Traits --}}
|
||||
<div class="header upcase">{{ localize 'MGT2.Creature.TabTraits' }}</div>
|
||||
<div class="table-container">
|
||||
<div class="table-row heading color-1">
|
||||
<div class="row-item row-item-left upcase" style="flex: 3">{{ localize 'MGT2.Creature.TraitName' }}</div>
|
||||
<div class="row-item row-item-center upcase" style="flex: 1">{{ localize 'MGT2.Creature.TraitValue' }}</div>
|
||||
<div class="row-item row-item-left upcase" style="flex: 4">{{ localize 'MGT2.Items.Description' }}</div>
|
||||
<div class="row-item row-item-right item-controls" style="flex: 0 0 3rem">
|
||||
{{#if isEditable}}<a data-action="addTrait" data-prop="traits" title="{{ localize 'MGT2.Creature.AddTrait' }}"><i class="fas fa-plus"></i></a>{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{#each system.traits as |trait i|}}
|
||||
<div class="table-row">
|
||||
<div class="row-item row-item-left" style="flex: 3">
|
||||
{{#if ../isEditable}}
|
||||
<input type="text" name="system.traits.{{i}}.name" value="{{trait.name}}" placeholder="{{ localize 'MGT2.Creature.TraitName' }}" />
|
||||
{{else}}
|
||||
<span class="trait-name">{{trait.name}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-center" style="flex: 1">
|
||||
{{#if ../isEditable}}
|
||||
<input type="text" name="system.traits.{{i}}.value" value="{{trait.value}}" class="text-center" />
|
||||
{{else}}
|
||||
<span class="trait-value">{{trait.value}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-left" style="flex: 4">
|
||||
{{#if ../isEditable}}
|
||||
<input type="text" name="system.traits.{{i}}.description" value="{{trait.description}}" />
|
||||
{{else}}
|
||||
<span class="text-muted">{{trait.description}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row-item row-item-right item-controls" style="flex: 0 0 3rem">
|
||||
{{#if ../isEditable}}
|
||||
<a data-action="deleteTrait" data-prop="traits" data-index="{{i}}" title="{{ localize 'MGT2.Creature.Delete' }}"><i class="fas fa-trash"></i></a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{#unless system.traits.length}}
|
||||
<div class="table-row">
|
||||
<div class="row-item row-item-center text-muted" style="flex:1">{{ localize 'MGT2.Creature.NoTraits' }}</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
</div>{{!-- /tab combat --}}
|
||||
|
||||
{{!-- ── TAB : INFORMATIONS ── --}}
|
||||
<div class="tab" data-group="primary" data-tab="info">
|
||||
<div class="creature-info-tab">
|
||||
<div class="field-group mt-1">
|
||||
<label class="upcase">{{ localize 'MGT2.Items.Description' }}</label>
|
||||
{{formInput systemFields.biography enriched=enrichedBiography value=system.biography name="system.biography" toggled=true}}
|
||||
</div>
|
||||
<div class="field-group mt-1">
|
||||
<label class="upcase">{{ localize 'MGT2.Items.Notes' }}</label>
|
||||
{{formInput systemFields.notes enriched=enrichedNotes value=system.notes name="system.notes" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- ── VERTICAL SIDEBAR TABS (outside window, right side) ── --}}
|
||||
<nav class="sheet-sidebar tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="combat" title="{{ localize 'MGT2.Creature.TabCombat' }}"><i class="fa-solid fa-swords"></i><span class="tab-label">COMBAT</span></a>
|
||||
<a class="item tab-select" data-tab="info" title="{{ localize 'MGT2.Creature.TabInfo' }}"><i class="fa-solid fa-circle-info"></i><span class="tab-label">INFO</span></a>
|
||||
</nav>
|
||||
|
||||
</div>{{!-- .creature-body --}}
|
||||
|
||||
</div>
|
||||
@@ -1,16 +1,124 @@
|
||||
<form class="{{cssClass}} flexcol" autocomplete="off" style="align-content: flex-start;align-items: baseline;overflow: hidden;height: 100%;">
|
||||
<div class="{{cssClass}} vehicule-sheet flexcol" style="overflow: hidden; height: 100%;">
|
||||
|
||||
<!-- ── Header ─────────────────────────────────────── -->
|
||||
<section class="vehicule-header">
|
||||
<div class="vehicule-header-img">
|
||||
<img class="profile" src="{{img}}" data-edit="img" title="{{name}}" height="130" width="100" />
|
||||
</div>
|
||||
<div class="vehicule-header-img">
|
||||
<img class="profile" src="{{img}}" data-edit="img" title="{{name}}" />
|
||||
</div>
|
||||
<div class="vehicule-header-body">
|
||||
<input class="field-name" name="name" type="text" value="{{name}}" />
|
||||
<ul class="character-summary">
|
||||
<li class="w5-10"><input name="system.personal.title" type="text" value="{{system.personal.title}}" placeholder="{{ localize 'MGT2.Actor.PlaceholderTITLE' }}" /></li>
|
||||
<li class="w2-10"><input name="system.personal.species" type="text" value="{{system.personal.species}}" placeholder="{{ localize 'MGT2.Actor.PlaceholderSPECIES' }}" /></li>
|
||||
<li class="w1-10"><input name="system.personal.age" type="text" value="{{system.personal.age}}" placeholder="{{ localize 'MGT2.Actor.PlaceholderAGE' }}" /></li>
|
||||
<li class="w2-10"><input name="system.personal.wup" type="text" value="{{system.personal.wup}}" placeholder="{{ localize 'MGT2.Actor.PlaceholderUCP' }}" /></li>
|
||||
</ul>
|
||||
<input class="vehicule-name" name="name" type="text" value="{{name}}" />
|
||||
<div class="vehicule-header-stats">
|
||||
<div class="vehicule-stat-box vehicule-hull">
|
||||
<label>{{ localize 'MGT2.Vehicule.Hull' }}</label>
|
||||
<div class="vehicule-stat-value">
|
||||
<input type="number" name="system.life.value" value="{{system.life.value}}" />
|
||||
<span>/</span>
|
||||
<input type="number" name="system.life.max" value="{{system.life.max}}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="vehicule-armor-group">
|
||||
<div class="vehicule-armor-label">{{ localize 'MGT2.Vehicule.Armor' }}</div>
|
||||
<div class="vehicule-armor-row">
|
||||
<div class="vehicule-stat-box vehicule-armor-box">
|
||||
<label>{{ localize 'MGT2.Vehicule.ArmorFront' }}</label>
|
||||
<div class="vehicule-stat-value">
|
||||
<input type="number" name="system.armor.front" value="{{system.armor.front}}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="vehicule-stat-box vehicule-armor-box">
|
||||
<label>{{ localize 'MGT2.Vehicule.ArmorSides' }}</label>
|
||||
<div class="vehicule-stat-value">
|
||||
<input type="number" name="system.armor.sides" value="{{system.armor.sides}}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="vehicule-stat-box vehicule-armor-box">
|
||||
<label>{{ localize 'MGT2.Vehicule.ArmorRear' }}</label>
|
||||
<div class="vehicule-stat-value">
|
||||
<input type="number" name="system.armor.rear" value="{{system.armor.rear}}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<!-- ── Body: tab content + vertical sidebar nav ──── -->
|
||||
<div class="vehicule-content">
|
||||
|
||||
<!-- Tab: Stats -->
|
||||
<div class="tab vehicule-tab" data-group="primary" data-tab="stats">
|
||||
<div class="vehicule-stats-grid">
|
||||
|
||||
<div class="vehicule-field">
|
||||
<label>{{ localize 'MGT2.Vehicule.SpeedCruise' }}</label>
|
||||
<select name="system.speed.cruise">
|
||||
{{selectOptions config.SpeedBands selected=system.speed.cruise localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="vehicule-field">
|
||||
<label>{{ localize 'MGT2.Vehicule.SpeedMax' }}</label>
|
||||
<select name="system.speed.maximum">
|
||||
{{selectOptions config.SpeedBands selected=system.speed.maximum localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="vehicule-field">
|
||||
<label>{{ localize 'MGT2.Vehicule.Agility' }}</label>
|
||||
<input type="number" name="system.agility" value="{{system.agility}}" />
|
||||
</div>
|
||||
|
||||
<div class="vehicule-field">
|
||||
<label>{{ localize 'MGT2.Vehicule.Crew' }}</label>
|
||||
<input type="number" name="system.crew" value="{{system.crew}}" />
|
||||
</div>
|
||||
|
||||
<div class="vehicule-field">
|
||||
<label>{{ localize 'MGT2.Vehicule.Passengers' }}</label>
|
||||
<input type="number" name="system.passengers" value="{{system.passengers}}" />
|
||||
</div>
|
||||
|
||||
<div class="vehicule-field">
|
||||
<label>{{ localize 'MGT2.Vehicule.Cargo' }}</label>
|
||||
<input type="number" name="system.cargo" value="{{system.cargo}}" />
|
||||
</div>
|
||||
|
||||
<div class="vehicule-field">
|
||||
<label>{{ localize 'MGT2.Vehicule.Shipping' }}</label>
|
||||
<input type="number" name="system.shipping" value="{{system.shipping}}" />
|
||||
</div>
|
||||
|
||||
<div class="vehicule-field">
|
||||
<label>{{ localize 'MGT2.Vehicule.Cost' }}</label>
|
||||
<input type="number" name="system.cost" value="{{system.cost}}" />
|
||||
</div>
|
||||
|
||||
<div class="vehicule-field">
|
||||
<label>{{ localize 'MGT2.Vehicule.Autopilot' }}</label>
|
||||
<input type="number" name="system.skills.autopilot" value="{{system.skills.autopilot}}" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab: Description -->
|
||||
<div class="tab vehicule-tab" data-group="primary" data-tab="description">
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
|
||||
<!-- Vertical sidebar nav (positioned absolutely right of window) -->
|
||||
<nav class="sheet-sidebar tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="stats" title="{{ localize 'MGT2.Vehicule.TabStats' }}">
|
||||
<i class="fa-solid fa-gauge-high"></i>
|
||||
<span class="tab-label">STATS</span>
|
||||
</a>
|
||||
<a class="item tab-select" data-tab="description" title="{{ localize 'MGT2.Vehicule.TabDescription' }}">
|
||||
<i class="fa-solid fa-circle-info"></i>
|
||||
<span class="tab-label">INFO</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
49
templates/chat/creature-roll.html
Normal file
49
templates/chat/creature-roll.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<div class="mgt2-chat-roll mgt2-creature-roll">
|
||||
<div class="mgt2-roll-header">
|
||||
<img class="creature-chat-img" src="{{creatureImg}}" title="{{creatureName}}" />
|
||||
<div class="mgt2-roll-header-text">
|
||||
<span class="mgt2-roll-char-name">{{creatureName}}</span>
|
||||
<div class="mgt2-roll-meta">
|
||||
<span class="mgt2-roll-type">{{rollLabel}}</span>
|
||||
{{#if difficulty}}
|
||||
<span class="mgt2-roll-sep">•</span>
|
||||
<span class="mgt2-roll-difficulty">{{difficultyLabel}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if modifiers}}
|
||||
<div class="mgt2-roll-modifiers">
|
||||
{{#each modifiers as |mod|}}
|
||||
<span class="mgt2-roll-mod-tag">{{mod}}</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="dice-roll">
|
||||
<div class="dice-result">
|
||||
<div class="dice-formula">{{formula}}</div>
|
||||
{{{tooltip}}}
|
||||
<h4 class="dice-total {{#if success}}success{{else if failure}}failure{{/if}}" {{#if rollBreakdown}}data-tooltip="{{rollBreakdown}}"{{/if}}>{{total}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if success}}
|
||||
<div class="mgt2-outcome is-success"><i class="fa-solid fa-check"></i> {{ localize 'MGT2.Chat.Roll.Success' }}</div>
|
||||
{{else if failure}}
|
||||
<div class="mgt2-outcome is-failure"><i class="fa-solid fa-xmark"></i> {{ localize 'MGT2.Chat.Roll.Failure' }}</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if effectStr}}
|
||||
<div class="mgt2-effect {{#if success}}is-success{{else}}is-failure{{/if}}">
|
||||
{{ localize 'MGT2.Chat.Roll.Effect' }} <span class="mgt2-effect-value">{{effectStr}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showRollDamage}}
|
||||
<div class="mgt2-buttons">
|
||||
<button data-action="rollDamage">{{ localize 'MGT2.Chat.Roll.Damages' }}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
24
templates/chat/radiation.html
Normal file
24
templates/chat/radiation.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<div class="mgt2-chat-roll mgt2-radiation-card">
|
||||
<div class="mgt2-roll-header">
|
||||
<span class="mgt2-roll-char-name">{{rollObjectName}}</span>
|
||||
<div class="mgt2-roll-meta">
|
||||
<span class="mgt2-roll-type mgt2-radiation-label">
|
||||
<i class="fa-solid fa-radiation"></i>
|
||||
{{ localize 'MGT2.Chat.Radiation.Title' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dice-roll">
|
||||
<div class="dice-result">
|
||||
<div class="dice-formula">{{formula}}</div>
|
||||
{{{tooltip}}}
|
||||
<h4 class="dice-total">{{total}} {{ localize 'MGT2.Chat.Radiation.Rads' }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mgt2-radiation-rules">
|
||||
<i class="fa-solid fa-triangle-exclamation"></i>
|
||||
{{ localize 'MGT2.Chat.Radiation.Rules' }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,46 +1,103 @@
|
||||
<div class="roll-info">
|
||||
<div class="roll-object-name">{{rollObjectName}}</div>
|
||||
{{#if rollTypeName}}
|
||||
{{#if rollDifficulty}}
|
||||
<div class="roll-type-group"><div class="roll-type-name">{{rollTypeName}}</div><div class="roll-type-name">{{ localize rollDifficultyLabel }}</div></div>
|
||||
{{else}}
|
||||
<div class="roll-type-name">{{rollTypeName}}</div>
|
||||
<div class="mgt2-chat-roll">
|
||||
<div class="mgt2-roll-header">
|
||||
<span class="mgt2-roll-char-name">{{rollObjectName}}</span>
|
||||
{{#if rollTypeName}}
|
||||
<div class="mgt2-roll-meta">
|
||||
<span class="mgt2-roll-type">{{rollTypeName}}</span>
|
||||
{{#if rollDifficulty}}
|
||||
<span class="mgt2-roll-sep">•</span>
|
||||
<span class="mgt2-roll-difficulty">{{rollDifficultyLabel}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if rollMessage}}
|
||||
<div>{{rollMessage}}</div>
|
||||
<div class="mgt2-roll-modifier">{{rollMessage}}</div>
|
||||
{{/if}}
|
||||
{{#if rollModifiers}}
|
||||
{{#each rollModifiers as |rollModifier i| }}
|
||||
<div>{{rollModifier}}</div>
|
||||
{{/each}}
|
||||
<div class="mgt2-roll-modifiers">
|
||||
{{#each rollModifiers as |mod i|}}
|
||||
<span class="mgt2-roll-mod-tag">{{mod}}</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="dice-roll">
|
||||
{{#if flavor}}
|
||||
<div class="dice-flavor">{{flavor}}</div>
|
||||
|
||||
<div class="dice-roll">
|
||||
{{#if flavor}}
|
||||
<div class="dice-flavor">{{flavor}}</div>
|
||||
{{/if}}
|
||||
<div class="dice-result">
|
||||
<div class="dice-formula">{{formula}}</div>
|
||||
{{{tooltip}}}
|
||||
<h4 class="dice-total {{#if rollSuccess}}success{{else if rollFailure}}failure{{/if}}" {{#if rollBreakdown}}data-tooltip="{{rollBreakdown}}"{{/if}}>{{total}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if apValue}}
|
||||
<div class="mgt2-ap-info" data-tooltip="{{ localize 'MGT2.Chat.Roll.APIgnoreHint' }}">
|
||||
<i class="fa-solid fa-shield-halved"></i>
|
||||
{{ localize 'MGT2.Chat.Roll.APIgnore' }} {{apValue}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="dice-result"><div class="dice-formula">{{formula}}</div>{{{tooltip}}}<h4 class="dice-total">{{total}}</h4></div>
|
||||
</div>
|
||||
{{#if rollSuccess}}
|
||||
<div class="roll-success">{{ localize 'MGT2.Chat.Roll.Success' }}</div>
|
||||
{{else if rollFailure}}
|
||||
<div class="roll-success">{{ localize 'MGT2.Chat.Roll.Failure' }}</div>
|
||||
{{/if}}
|
||||
{{#if showButtons}}
|
||||
<div class="mgt2-buttons">
|
||||
{{#if hasDamage}}
|
||||
<button data-action="damage" title="{{ localize 'MGT2.Chat.Roll.ApplyDamages' }}"><i class="fa-regular fa-heart-circle-minus"></i></button>
|
||||
<!-- <button data-action="healing" data-multiplier="1" title="Apply Healing"><i class="fa-regular fa-heart-circle-plus"></i></button> -->
|
||||
|
||||
{{#if blastRadius}}
|
||||
<div class="mgt2-blast-info" data-tooltip="{{ localize 'MGT2.Chat.Roll.BlastHint' }}">
|
||||
<i class="fa-solid fa-burst"></i>
|
||||
{{ localize 'MGT2.Chat.Roll.BlastArea' }} {{blastRadius}}m — {{ localize 'MGT2.Chat.Roll.BlastRules' }}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if showRollRequest}}
|
||||
<button data-action="requestRoll" data-roll="characteristic" data-roll-characteristic="strength" title="Roll!"><i class="fa-solid fa-dice"></i></button>
|
||||
|
||||
{{#if stunWeapon}}
|
||||
<div class="mgt2-stun-info" data-tooltip="{{ localize 'MGT2.Chat.Roll.StunHint' }}">
|
||||
<i class="fa-solid fa-bolt"></i>
|
||||
{{ localize 'MGT2.Chat.Roll.StunWeapon' }}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if showRollDamage}}
|
||||
<button data-action="rollDamage" title="Roll">{{ localize 'MGT2.Chat.Roll.Damages' }}</button>
|
||||
|
||||
{{#if radiationWeapon}}
|
||||
<div class="mgt2-radiation-info" data-tooltip="{{ localize 'MGT2.Chat.Radiation.Hint' }}">
|
||||
<i class="fa-solid fa-radiation"></i>
|
||||
{{ localize 'MGT2.Chat.Radiation.Badge' }}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#each cardButtons as |cardButton i| }}
|
||||
<button data-index="{{i}}" title="{{cardButton.label}}">{{cardButton.label}}</button>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if autoInfo}}
|
||||
<div class="mgt2-auto-info">
|
||||
<i class="fa-solid fa-gun"></i> {{autoInfo}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if rollSuccess}}
|
||||
<div class="mgt2-outcome is-success"><i class="fa-solid fa-check"></i> {{ localize 'MGT2.Chat.Roll.Success' }}</div>
|
||||
{{else if rollFailure}}
|
||||
<div class="mgt2-outcome is-failure"><i class="fa-solid fa-xmark"></i> {{ localize 'MGT2.Chat.Roll.Failure' }}</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if rollEffectStr}}
|
||||
<div class="mgt2-effect {{#if rollSuccess}}is-success{{else}}is-failure{{/if}}">
|
||||
{{ localize 'MGT2.Chat.Roll.Effect' }} <span class="mgt2-effect-value">{{rollEffectStr}}</span>
|
||||
{{#if healingAmount}}<span class="mgt2-healing-amount">({{ localize 'MGT2.Healing.Heals' }} {{healingAmount}})</span>{{/if}}
|
||||
{{#if surgeryDamageAmount}}<span class="mgt2-healing-amount">({{ localize 'MGT2.Healing.SurgeryDamage' }} {{surgeryDamageAmount}})</span>{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showButtons}}
|
||||
<div class="mgt2-buttons">
|
||||
{{#if hasDamage}}
|
||||
<button data-action="damage" title="{{ localize 'MGT2.Chat.Roll.ApplyDamages' }}"><i class="fa-regular fa-heart-circle-minus"></i></button>
|
||||
{{/if}}
|
||||
{{#if showRollDamage}}
|
||||
<button data-action="rollDamage">{{ localize 'MGT2.Chat.Roll.Damages' }}</button>
|
||||
{{/if}}
|
||||
{{#if radiationWeapon}}
|
||||
<button data-action="rollRadiation" title="{{ localize 'MGT2.Chat.Radiation.RollButton' }}">
|
||||
<i class="fa-solid fa-radiation"></i> {{ localize 'MGT2.Chat.Radiation.RollButton' }}
|
||||
</button>
|
||||
{{/if}}
|
||||
{{#each cardButtons as |cardButton|}}
|
||||
<button data-action="{{cardButton.action}}" title="{{cardButton.label}}">{{cardButton.label}}</button>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
@@ -1,74 +1,71 @@
|
||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header">
|
||||
<label>{{localize 'MGT2.TYPES.Item.armor'}}</label>
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
<span class="item-type-label"><i class="fas fa-shield-halved"></i> {{localize 'MGT2.TYPES.Item.armor'}}</span>
|
||||
</div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
{{> systems/mgt2/templates/items/parts/sheet-physical-item.html }}
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" />
|
||||
</div>
|
||||
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-100">
|
||||
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" />
|
||||
<nav class="horizontal-tabs tabs mt-1" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
<textarea name="system.description" rows="6">{{system.description}}</textarea>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="item-details-grid">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-physical-item-tab.html }}
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Protection' }}</label>
|
||||
<input type="text" name="system.protection" value="{{system.protection}}" data-dtype="String" class="short" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Radiations' }}</label>
|
||||
<input type="number" name="system.radiations" value="{{system.radiations}}" data-dtype="Number" class="short" />
|
||||
</div>
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />{{ localize 'MGT2.Items.Equipped' }}</label>
|
||||
</div>
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.powered" data-dtype="Boolean" {{checked system.powered}} />{{ localize 'MGT2.Items.Powered' }}</label>
|
||||
</div>
|
||||
{{#if hadContainer}}
|
||||
<div class="field-row full">
|
||||
<label>{{ localize 'MGT2.Items.Storage' }}</label>
|
||||
<select name="system.container.id">
|
||||
{{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group w3-10">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />{{ localize 'MGT2.Items.Equipped' }}</label>
|
||||
</div>
|
||||
<div class="field-group w3-10">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.powered" data-dtype="Boolean" {{checked system.powered}} />{{ localize 'MGT2.Items.Powered' }}</label>
|
||||
</div>
|
||||
{{#if hadContainer}}
|
||||
<div class="field-group w3-10">
|
||||
<label>{{ localize 'MGT2.Items.Storage' }}</label>
|
||||
<select name="system.container.id">
|
||||
{{selectOptions containers selected=system.container.id nameAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="table-container">
|
||||
<div class="table-row heading">
|
||||
<div class="row-item row-item-left row-item-30">{{ localize 'MGT2.Items.Options' }}</div>
|
||||
<div class="row-item row-item-left">Description</div>
|
||||
<div class="row-item row-item-right"><a class="options-create" data-property="options"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Radiations' }}</label>
|
||||
<input type="number" name="system.radiations" value="{{system.radiations}}" data-dtype="Number" />
|
||||
{{#each system.options as |option i| }}
|
||||
<div class="table-row dropitem options-part" data-options-part="{{i}}" data-property="options" role="rowgroup">
|
||||
<div class="row-item row-item-left row-item-30"><input type="text" name="system.options.{{i}}.name" value="{{option.name}}" /></div>
|
||||
<div class="row-item row-item-left">
|
||||
<textarea name="system.options.{{i}}.description" rows="2">{{option.description}}</textarea>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Protection' }}</label>
|
||||
<input type="text" name="system.protection" value="{{system.protection}}" data-dtype="String" />
|
||||
<div class="row-item row-item-right item-controls">
|
||||
<a class="item-control options-delete" title="Delete Option"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-container mt-1">
|
||||
<div class="table-row heading color-2">
|
||||
<div class="row-item row-item-left row-item-30 flex-fix upcase">{{ localize 'MGT2.Items.Options' }}</div>
|
||||
<div class="row-item row-item-left">Description</div>
|
||||
<div class="row-item row-item-right row-item-5 flex-fix"><a class="options-create" data-property="options"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each system.options as |option i| }}
|
||||
<div class="table-row dropitem options-part" data-options-part="{{i}}" data-property="options" role="rowgroup">
|
||||
<div class="row-item row-item-left row-item-30 flex-fix"><input type="text" name="system.options.{{i}}.name" value="{{option.name}}" /></div>
|
||||
<div class="row-item row-item-left">
|
||||
<textarea name="system.options.{{i}}.description" rows="3">{{option.description}}</textarea>
|
||||
</div>
|
||||
<div class="row-item row-item-right row-item-5 flex-fix item-controls">
|
||||
<a class="item-control options-delete" title="Delete Option"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,50 +1,55 @@
|
||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Career' }}</label></div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<div class="field-group mt-1">
|
||||
<label class="upcase">{{ localize 'MGT2.Items.Terms' }}</label>
|
||||
<input type="number" name="system.terms" value="{{system.terms}}" data-dtype="Number" />
|
||||
</div>
|
||||
<div class="field-group mt-1">
|
||||
<label class="upcase">{{ localize 'MGT2.Items.Rank' }}</label>
|
||||
<input type="number" name="system.rank" value="{{system.rank}}" data-dtype="Number" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-100">
|
||||
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" data-dtype="String" />
|
||||
<div class="field-group mt-1">
|
||||
<label class="upcase">{{ localize 'MGT2.Items.Assignment' }}</label>
|
||||
<input type="text" name="system.assignment" value="{{system.assignment}}" data-dtype="String" />
|
||||
</div>
|
||||
<nav class="horizontal-tabs tabs mt-1" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="events">{{ localize 'MGT2.Items.EventsMishaps' }}</a>
|
||||
</nav>
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<textarea name="system.description" rows="6">{{system.description}}</textarea>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="events">
|
||||
<div class="table-container color-2 mt-1">
|
||||
<div class="table-row heading color-2">
|
||||
<div class="row-item row-item-left upcase">{{ localize 'MGT2.Items.Age' }}</div>
|
||||
<div class="row-item row-item-left upcase">{{ localize 'MGT2.Items.Details' }}</div>
|
||||
<div class="row-item item-controls"><a class="event-create"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each system.events as |event i| }}
|
||||
<div class="table-row dropitem events-part" data-events-part="{{i}}" role="rowgroup">
|
||||
<div class="row-item row-item-left"><input type="number" name="system.events.{{i}}.age" value="{{event.age}}" data-dtype="Number" /></div>
|
||||
<div class="row-item row-item-left">
|
||||
<textarea name="system.events.{{i}}.description" rows="3">{{event.description}}</textarea>
|
||||
</div>
|
||||
<div class="row-item item-controls">
|
||||
<a class="item-control event-delete" title="Delete Event"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
<span class="item-type-label"><i class="fas fa-briefcase"></i> {{ localize 'MGT2.Items.Career' }}</span>
|
||||
</div>
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" data-dtype="String" />
|
||||
</div>
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="events">{{ localize 'MGT2.Items.EventsMishaps' }}</a>
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
</form>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="item-details-grid">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Terms' }}</label>
|
||||
<input type="number" name="system.terms" value="{{system.terms}}" data-dtype="Number" class="short" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Rank' }}</label>
|
||||
<input type="number" name="system.rank" value="{{system.rank}}" data-dtype="Number" class="short" />
|
||||
</div>
|
||||
<div class="field-row full">
|
||||
<label>{{ localize 'MGT2.Items.Assignment' }}</label>
|
||||
<input type="text" name="system.assignment" value="{{system.assignment}}" data-dtype="String" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="events">
|
||||
<div class="table-container">
|
||||
<div class="table-row heading">
|
||||
<div class="row-item row-item-left">{{ localize 'MGT2.Items.Age' }}</div>
|
||||
<div class="row-item row-item-left">{{ localize 'MGT2.Items.Details' }}</div>
|
||||
<div class="row-item row-item-right item-controls"><a class="event-create"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each system.events as |event i| }}
|
||||
<div class="table-row dropitem events-part" data-events-part="{{i}}" role="rowgroup">
|
||||
<div class="row-item row-item-left"><input type="number" name="system.events.{{i}}.age" value="{{event.age}}" data-dtype="Number" /></div>
|
||||
<div class="row-item row-item-left">
|
||||
<textarea name="system.events.{{i}}.description" rows="2">{{event.description}}</textarea>
|
||||
</div>
|
||||
<div class="row-item row-item-right item-controls">
|
||||
<a class="item-control event-delete" title="Delete Event"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,65 +1,64 @@
|
||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header">
|
||||
<label>{{localize 'MGT2.EquipmentSubType.computer'}}</label>
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
<span class="item-type-label"><i class="fas fa-desktop"></i> {{localize 'MGT2.EquipmentSubType.computer'}}</span>
|
||||
</div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
{{> systems/mgt2/templates/items/parts/sheet-physical-item.html }}
|
||||
</div>
|
||||
<div class="w-100">
|
||||
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" />
|
||||
<nav class="horizontal-tabs tabs mt-1" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
<textarea name="system.description" rows="6">{{system.description}}</textarea>
|
||||
</div>
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" />
|
||||
</div>
|
||||
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />{{ localize 'MGT2.Items.Equipped' }}</label>
|
||||
</div>
|
||||
{{#if hadContainer}}
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Storage' }}</label>
|
||||
<select name="system.container.id">
|
||||
{{selectOptions containers selected=system.container.id nameAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="field-group mt-1">
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="item-details-grid">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-physical-item-tab.html }}
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Processing' }}</label>
|
||||
<input type="number" name="system.processing" value="{{system.processing}}" data-dtype="Number" />
|
||||
<input type="number" name="system.processing" value="{{system.processing}}" data-dtype="Number" class="short" />
|
||||
</div>
|
||||
<div class="table-container mt-1">
|
||||
<div class="table-row heading color-2">
|
||||
<div class="row-item row-item-left flex-grow-2 upcase">{{ localize 'MGT2.Items.Options' }}</div>
|
||||
<div class="row-item row-item-left flex-grow-3">{{ localize 'MGT2.Items.Description' }}</div>
|
||||
<div class="row-item row-item-right"><a class="options-create" data-property="options"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each system.options as |option i| }}
|
||||
<div class="table-row dropitem options-part" data-options-part="{{i}}" data-property="options" role="rowgroup">
|
||||
<div class="row-item row-item-left flex-grow-2"><input type="text" name="system.options.{{i}}.name" value="{{option.name}}" /></div>
|
||||
<div class="row-item row-item-left flex-grow-3">
|
||||
<textarea name="system.options.{{i}}.description" rows="3">{{option.description}}</textarea>
|
||||
</div>
|
||||
<div class="row-item row-item-right item-controls">
|
||||
<a class="item-control options-delete" title="Delete Option"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />{{ localize 'MGT2.Items.Equipped' }}</label>
|
||||
</div>
|
||||
{{#if hadContainer}}
|
||||
<div class="field-row full">
|
||||
<label>{{ localize 'MGT2.Items.Storage' }}</label>
|
||||
<select name="system.container.id">
|
||||
{{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
|
||||
<div class="table-container" style="margin-top:8px;">
|
||||
<div class="table-row heading">
|
||||
<div class="row-item row-item-left">{{ localize 'MGT2.Items.Options' }}</div>
|
||||
<div class="row-item row-item-left">{{ localize 'MGT2.Items.Description' }}</div>
|
||||
<div class="row-item row-item-right"><a class="options-create" data-property="options"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each system.options as |option i| }}
|
||||
<div class="table-row dropitem options-part" data-options-part="{{i}}" data-property="options" role="rowgroup">
|
||||
<div class="row-item row-item-left"><input type="text" name="system.options.{{i}}.name" value="{{option.name}}" /></div>
|
||||
<div class="row-item row-item-left">
|
||||
<textarea name="system.options.{{i}}.description" rows="2">{{option.description}}</textarea>
|
||||
</div>
|
||||
<div class="row-item row-item-right item-controls">
|
||||
<a class="item-control options-delete" title="Delete Option"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,84 +1,80 @@
|
||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Contact' }}</label></div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Relation' }}</label>
|
||||
<select name="system.relation">
|
||||
{{selectOptions config.ContactRelations selected = system.relation localize = true}}
|
||||
</select>
|
||||
|
||||
</div>
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Attitude' }}</label>
|
||||
<select name="system.attitude">
|
||||
{{selectOptions config.Attitudes selected = system.attitude localize = true}}
|
||||
</select>
|
||||
|
||||
</div>
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Status' }}</label>
|
||||
<select name="system.status">
|
||||
{{selectOptions config.ContactStatus selected = system.status localize = true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
<span class="item-type-label"><i class="fas fa-address-card"></i> {{ localize 'MGT2.Items.Contact' }}</span>
|
||||
</div>
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" />
|
||||
</div>
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Informations' }}</a>
|
||||
<a class="item tab-select" data-tab="description">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="notes">{{ localize 'MGT2.Items.Notes' }}</a>
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="item-details-grid">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Relation' }}</label>
|
||||
<select name="system.relation">
|
||||
{{selectOptions config.ContactRelations selected=system.relation localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-100 h100 flexcol">
|
||||
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" style="flex-grow: 0;" />
|
||||
<div class="field-groups mt-1" style="flex-grow: 0;">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Species' }}</label>
|
||||
<input type="text" name="system.species" value="{{system.species}}" data-dtype="String" />
|
||||
</div>
|
||||
{{#if settings.useGender}}
|
||||
<div class="field-group" style="margin:0 1rem">
|
||||
<label>{{ localize 'MGT2.Gender' }}</label>
|
||||
<input type="text" name="system.gender" value="{{system.gender}}" data-dtype="String" />
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if settings.usePronouns}}
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Pronouns' }}</label>
|
||||
<input type="text" name="system.pronouns" value="{{system.pronouns}}" data-dtype="String" />
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<nav class="horizontal-tabs tabs mt-1" data-group="primary" style="flex-grow: 0;">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Informations' }}</a>
|
||||
<a class="item tab-select" data-tab="description">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="notes">{{ localize 'MGT2.Items.Notes' }}</a>
|
||||
</nav>
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Title' }}</label>
|
||||
<input type="text" name="system.title" value="{{system.title}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Nickname' }}</label>
|
||||
<input type="text" name="system.nickname" value="{{system.nickname}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group flex-1">
|
||||
<label>{{ localize 'MGT2.Items.Homeworld' }}</label>
|
||||
<input type="text" name="system.homeworld" value="{{system.homeworld}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-group flex-1">
|
||||
<label>{{ localize 'MGT2.Items.Location' }}</label>
|
||||
<input type="text" name="system.location" value="{{system.location}}" data-dtype="String" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Occupation' }}</label>
|
||||
<input type="text" name="system.occupation" value="{{system.occupation}}" data-dtype="String" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab w100 h100" data-group="primary" data-tab="description">
|
||||
{{editor system.description.value target="system.description" button=true editable=true}}
|
||||
</div>
|
||||
<div class="tab w100 h100" data-group="primary" data-tab="notes">
|
||||
{{editor system.notes target="system.notes" button=true editable=true}}
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Attitude' }}</label>
|
||||
<select name="system.attitude">
|
||||
{{selectOptions config.Attitudes selected=system.attitude localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Status' }}</label>
|
||||
<select name="system.status">
|
||||
{{selectOptions config.ContactStatus selected=system.status localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
{{#if settings.useGender}}
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Gender' }}</label>
|
||||
<input type="text" name="system.gender" value="{{system.gender}}" data-dtype="String" />
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Species' }}</label>
|
||||
<input type="text" name="system.species" value="{{system.species}}" data-dtype="String" />
|
||||
</div>
|
||||
{{#if settings.usePronouns}}
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Pronouns' }}</label>
|
||||
<input type="text" name="system.pronouns" value="{{system.pronouns}}" data-dtype="String" />
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Title' }}</label>
|
||||
<input type="text" name="system.title" value="{{system.title}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Nickname' }}</label>
|
||||
<input type="text" name="system.nickname" value="{{system.nickname}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Homeworld' }}</label>
|
||||
<input type="text" name="system.homeworld" value="{{system.homeworld}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Location' }}</label>
|
||||
<input type="text" name="system.location" value="{{system.location}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-row full">
|
||||
<label>{{ localize 'MGT2.Items.Occupation' }}</label>
|
||||
<input type="text" name="system.occupation" value="{{system.occupation}}" data-dtype="String" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="tab" data-group="primary" data-tab="description">
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="notes">
|
||||
{{formInput systemFields.notes enriched=enrichedNotes value=system.notes name="system.notes" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,55 +1,59 @@
|
||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Container' }}</label></div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
{{#if system.locked}}
|
||||
<div class="field-group mt-05">
|
||||
<i class="fa-solid fa-lock"></i><label class="upcase">{{ localize 'MGT2.Items.Locked' }}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="field-group mt-05">
|
||||
<label>{{ localize 'MGT2.Items.Weight' }} ({{unitlabels.weight}})</label>
|
||||
<input type="number" value="{{weight}}" readonly />
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
<span class="item-type-label"><i class="fas fa-box"></i> {{ localize 'MGT2.Items.Container' }}</span>
|
||||
{{#if system.locked}}<span class="item-type-label" style="margin-left:8px;"><i class="fa-solid fa-lock"></i></span>{{/if}}
|
||||
</div>
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" data-dtype="String" />
|
||||
</div>
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
{{#if isGM}}
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
{{/if}}
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex: 1">
|
||||
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" data-dtype="String" />
|
||||
<nav class="horizontal-tabs tabs mt-1" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
<textarea name="system.description" rows="6">{{system.description}}</textarea>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="item-details-grid">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Weight' }} ({{unitlabels.weight}})</label>
|
||||
<input type="number" value="{{weight}}" readonly style="opacity:0.7;" class="short" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="field-groups mt-05">
|
||||
<div class="field-group flex-1">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.onHand" data-dtype="Boolean" {{checked system.onHand}} />{{ localize 'MGT2.Items.OnHand' }}</label>
|
||||
</div>
|
||||
{{#if isGM}}
|
||||
<div class="field-group flex-1">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.locked" data-dtype="Boolean" {{checked system.locked}} />{{ localize 'MGT2.Items.Locked' }}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="field-group flex-1">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Location' }}</label>
|
||||
<input type="text" name="system.location" value="{{system.location}}" data-dtype="String" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox">
|
||||
<input type="checkbox" name="system.onHand" id="onhand-{{item.id}}" data-dtype="Boolean" {{checked system.onHand}} />
|
||||
{{ localize 'MGT2.Items.OnHand' }}
|
||||
</label>
|
||||
</div>
|
||||
{{#if isGM}}
|
||||
<div class="field-group mt-05">
|
||||
<label>{{ localize 'MGT2.Items.LockedDescription' }}</label>
|
||||
<textarea name="system.lockedDescription" rows="6">{{system.lockedDescription}}</textarea>
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox">
|
||||
<input type="checkbox" name="system.locked" id="locked-{{item.id}}" data-dtype="Boolean" {{checked system.locked}} />
|
||||
{{ localize 'MGT2.Items.Locked' }}
|
||||
</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{#if isGM}}
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.LockedDescription' }}</label>
|
||||
{{formInput systemFields.lockedDescription enriched=enrichedLockedDescription value=system.lockedDescription name="system.lockedDescription" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,45 +1,51 @@
|
||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header">
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
{{#if (eq system.subType "disease")}}
|
||||
<label>{{localize 'MGT2.DiseaseSubType.disease'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-bacterium"></i> {{localize 'MGT2.DiseaseSubType.disease'}}</span>
|
||||
{{else if (eq system.subType "poison")}}
|
||||
<label>{{localize 'MGT2.DiseaseSubType.poison'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-skull-crossbones"></i> {{localize 'MGT2.DiseaseSubType.poison'}}</span>
|
||||
{{else}}
|
||||
<label>{{localize 'MGT2.DiseaseSubType.wound'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-heart-crack"></i> {{localize 'MGT2.DiseaseSubType.wound'}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Type' }}</label>
|
||||
<select name="system.subType">
|
||||
{{selectOptions config.DiseaseSubType selected = system.subType localize = true}}
|
||||
</select>
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" />
|
||||
</div>
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-100">
|
||||
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" />
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group w3-10">
|
||||
<label>{{ localize 'MGT2.Items.Difficulty' }}</label>
|
||||
<select name="system.difficulty">
|
||||
{{selectOptions config.Difficulty selected = system.difficulty localize = true}}
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="item-details-grid">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Type' }}</label>
|
||||
<select name="system.subType">
|
||||
{{selectOptions config.DiseaseSubType selected=system.subType localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-group w3-10" style="margin: 0 1rem;">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Difficulty' }}</label>
|
||||
<select name="system.difficulty">
|
||||
{{selectOptions config.Difficulty selected=system.difficulty localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Damage' }}</label>
|
||||
<input type="text" name="system.damage" value="{{system.damage}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-group w3-10">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Interval' }}</label>
|
||||
<input type="text" name="system.interval" value="{{system.interval}}" data-dtype="String" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-group mt-1 w-100">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
<textarea name="system.description" rows="6">{{system.description}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,57 +1,56 @@
|
||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header">
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
{{#if (eq system.subType "augment")}}
|
||||
<label>{{localize 'MGT2.EquipmentSubType.augment'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-dna"></i> {{localize 'MGT2.EquipmentSubType.augment'}}</span>
|
||||
{{else if (eq system.subType "trinket")}}
|
||||
<label>{{localize 'MGT2.EquipmentSubType.trinket'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-gem"></i> {{localize 'MGT2.EquipmentSubType.trinket'}}</span>
|
||||
{{else if (eq system.subType "clothing")}}
|
||||
<label>{{localize 'MGT2.EquipmentSubType.clothing'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-shirt"></i> {{localize 'MGT2.EquipmentSubType.clothing'}}</span>
|
||||
{{else}}
|
||||
<label>{{localize 'MGT2.EquipmentSubType.equipment'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-toolbox"></i> {{localize 'MGT2.EquipmentSubType.equipment'}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Type' }}</label>
|
||||
<select name="system.subType">
|
||||
{{selectOptions config.EquipmentSubType selected = system.subType localize = true}}
|
||||
</select>
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" />
|
||||
</div>
|
||||
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
{{> systems/mgt2/templates/items/parts/sheet-physical-item.html }}
|
||||
</div>
|
||||
<div class="w-100">
|
||||
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" />
|
||||
<nav class="horizontal-tabs tabs mt-1" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
<textarea name="system.description" rows="6">{{system.description}}</textarea>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="item-details-grid">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Type' }}</label>
|
||||
<select name="system.subType">
|
||||
{{selectOptions config.EquipmentSubType selected=system.subType localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />Equipped</label>
|
||||
</div>
|
||||
{{#if hadContainer}}
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Storage' }}</label>
|
||||
<select name="system.container.id">
|
||||
{{selectOptions containers selected=system.container.id nameAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{> systems/mgt2/templates/items/parts/sheet-physical-item-tab.html }}
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />{{ localize 'MGT2.Items.Equipped' }}</label>
|
||||
</div>
|
||||
{{#if hadContainer}}
|
||||
<div class="field-row full">
|
||||
<label>{{ localize 'MGT2.Items.Storage' }}</label>
|
||||
<select name="system.container.id">
|
||||
{{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,60 +1,59 @@
|
||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header">
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
{{#if (eq system.subType "loot")}}
|
||||
<label>{{localize 'MGT2.ItemSubType.loot'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-coins"></i> {{localize 'MGT2.ItemSubType.loot'}}</span>
|
||||
{{else}}
|
||||
<label>{{localize 'MGT2.ItemSubType.software'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-floppy-disk"></i> {{localize 'MGT2.ItemSubType.software'}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Type' }}</label>
|
||||
<select name="system.subType">
|
||||
{{selectOptions config.ItemSubType selected = system.subType localize = true}}
|
||||
</select>
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" />
|
||||
</div>
|
||||
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
{{> systems/mgt2/templates/items/parts/sheet-physical-item.html }}
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" />
|
||||
<nav class="horizontal-tabs tabs mt-1" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
</nav>
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
<textarea name="system.description" rows="6">{{system.description}}</textarea>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="item-details-grid">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Type' }}</label>
|
||||
<select name="system.subType">
|
||||
{{selectOptions config.ItemSubType selected=system.subType localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-physical-item-tab.html }}
|
||||
{{#if hadContainer}}
|
||||
<div class="field-groups mt-1">
|
||||
{{#if (eq system.subType "software")}}
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Computer' }}</label>
|
||||
<select name="system.software.computerId">
|
||||
{{selectOptions computers selected=system.software.computerId nameAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Storage' }}</label>
|
||||
<select name="system.container.id">
|
||||
{{selectOptions containers selected=system.container.id nameAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-row full">
|
||||
<label>{{ localize 'MGT2.Items.Storage' }}</label>
|
||||
<select name="system.container.id">
|
||||
{{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq system.subType "software")}}
|
||||
<div class="field-group mt-1">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Bandwidth' }}</label>
|
||||
<input type="number" name="system.software.bandwidth" value="{{system.software.bandwidth}}" data-dtype="Number" />
|
||||
<input type="number" name="system.software.bandwidth" value="{{system.software.bandwidth}}" data-dtype="Number" class="short" />
|
||||
</div>
|
||||
{{#if hadContainer}}
|
||||
<div class="field-row full">
|
||||
<label>{{ localize 'MGT2.Items.Computer' }}</label>
|
||||
<select name="system.software.computerId">
|
||||
{{selectOptions computers selected=system.software.computerId valueAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -10,7 +10,7 @@
|
||||
<label>{{ localize 'MGT2.Items.Skill' }}</label>
|
||||
<select name="system.roll.skill">
|
||||
<option></option>
|
||||
{{selectOptions skills selected=system.roll.skill nameAttr="_id" labelAttr="name"}}
|
||||
{{selectOptions skills selected=system.roll.skill valueAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-group w3-10">
|
||||
|
||||
24
templates/items/parts/sheet-physical-item-tab.html
Normal file
24
templates/items/parts/sheet-physical-item-tab.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Quantity' }}</label>
|
||||
<input type="number" name="system.quantity" value="{{system.quantity}}" data-dtype="Number" integer="true" positive="true" class="short" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Weight' }} ({{unitlabels.weight}})</label>
|
||||
<input type="number" name="weight" value="{{weight}}" data-dtype="Number" step="0.5" class="short" />
|
||||
</div>
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox">
|
||||
<input type="checkbox" name="system.weightless" data-dtype="Boolean" {{checked system.weightless}} />
|
||||
{{ localize 'MGT2.Items.Weightless' }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Cost' }}</label>
|
||||
<input type="number" name="system.cost" value="{{system.cost}}" data-dtype="Number" step="1" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.TL' }}</label>
|
||||
<select name="system.tl">
|
||||
{{selectOptions config.TL selected=system.tl localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
@@ -1,21 +1,22 @@
|
||||
<div class="field-group mt-05">
|
||||
<label>{{ localize 'MGT2.Items.Quantity' }}</label>
|
||||
<div class="item-stat-pill">
|
||||
<span class="stat-label">{{ localize 'MGT2.Items.Quantity' }}</span>
|
||||
<input type="number" name="system.quantity" value="{{system.quantity}}" data-dtype="Number" integer="true" positive="true" />
|
||||
</div>
|
||||
<div class="field-group mt-05">
|
||||
<label>{{ localize 'MGT2.Items.Weight' }} ({{unitlabels.weight}})</label>
|
||||
<div class="item-stat-pill">
|
||||
<span class="stat-label">{{ localize 'MGT2.Items.Weight' }} ({{unitlabels.weight}})</span>
|
||||
<input type="number" name="weight" value="{{weight}}" data-dtype="Number" step="0.5" />
|
||||
</div>
|
||||
<div class="field-group mt-05">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.weightless" data-dtype="Boolean" {{checked system.weightless}} />{{ localize 'MGT2.Items.Weightless' }}</label>
|
||||
<div class="item-stat-pill-checkbox">
|
||||
<input type="checkbox" name="system.weightless" id="weightless-{{item.id}}" data-dtype="Boolean" {{checked system.weightless}} />
|
||||
<label for="weightless-{{item.id}}">{{ localize 'MGT2.Items.Weightless' }}</label>
|
||||
</div>
|
||||
<div class="field-group mt-05">
|
||||
<label>{{ localize 'MGT2.Items.Cost' }}</label>
|
||||
<div class="item-stat-pill">
|
||||
<span class="stat-label">{{ localize 'MGT2.Items.Cost' }}</span>
|
||||
<input type="number" name="system.cost" value="{{system.cost}}" data-dtype="Number" step="1" />
|
||||
</div>
|
||||
<div class="field-group mt-05">
|
||||
<label>{{ localize 'MGT2.Items.TL' }}</label>
|
||||
<div class="item-stat-pill">
|
||||
<span class="stat-label">{{ localize 'MGT2.Items.TL' }}</span>
|
||||
<select name="system.tl">
|
||||
{{selectOptions config.TL selected = system.tl localize = true}}
|
||||
</select>
|
||||
</select>
|
||||
</div>
|
||||
@@ -1,70 +1,67 @@
|
||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header"><label>{{ localize 'MGT2.Specie' }}</label></div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
</div>
|
||||
<div class="w-100" style="display: flex;flex-direction: column;justify-content: flex-start;align-items: stretch;">
|
||||
<input class="field item-name" name="name" type="text" value="{{item.name}}" />
|
||||
<div class="field-group mt-1">
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
<span class="item-type-label"><i class="fas fa-dna"></i> {{ localize 'MGT2.Specie' }}</span>
|
||||
</div>
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" />
|
||||
</div>
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.DetailedDescription' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
<nav class="horizontal-tabs tabs mt-1" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.DetailedDescription' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
</nav>
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
<textarea name="system.description" rows="6">{{system.description}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab w100 h100" data-group="primary" data-tab="tab2">
|
||||
{{editor system.descriptionLong target="system.descriptionLong" button=true editable=true}}
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
<div class="table-container">
|
||||
<div class="table-row heading color-2">
|
||||
<div class="row-item row-item-left upcase">{{ localize 'MGT2.Items.Characteristics' }}</div>
|
||||
<div class="row-item row-item-center row-item-15 flex-fix">{{ localize 'MGT2.Items.Modifiers' }}</div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix"><a class="modifiers-create"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each system.modifiers as |modifier i| }}
|
||||
<div class="table-row modifiers-part" data-modifiers-part="{{i}}" role="rowgroup">
|
||||
<div class="row-item row-item-left">
|
||||
<select name="system.modifiers.{{i}}.characteristic">
|
||||
{{selectOptions ../config.Characteristics selected=modifier.characteristic localize = true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="row-item row-item-left row-item-15 flex-fix">
|
||||
<input type="number" name="system.modifiers.{{i}}.value" value="{{modifier.value}}" />
|
||||
</div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix">
|
||||
<a class="item-control modifiers-delete" title="Delete Trait"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="table-container mt-1">
|
||||
<div class="table-row heading color-2">
|
||||
<div class="row-item row-item-30 row-item-left upcase">{{ localize 'MGT2.Items.Traits' }}</div>
|
||||
<div class="row-item row-item-left">{{ localize 'MGT2.Items.Description' }}</div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix"><a class="options-create" data-property="traits"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each system.traits as |trait i| }}
|
||||
<div class="table-row options-part" data-options-part="{{i}}" data-property="traits" role="rowgroup">
|
||||
<div class="row-item row-item-30 row-item-left"><input type="text" name="system.traits.{{i}}.name" value="{{trait.name}}" /></div>
|
||||
<div class="row-item row-item-left">
|
||||
<textarea name="system.traits.{{i}}.description" rows="3">{{trait.description}}</textarea>
|
||||
</div>
|
||||
<div class="row-item row-item-15 item-controls flex-fix">
|
||||
<a class="item-control options-delete" title="Delete Trait"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
{{formInput systemFields.descriptionLong enriched=enrichedDescriptionLong value=system.descriptionLong name="system.descriptionLong" toggled=true}}
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
<div class="table-container">
|
||||
<div class="table-row heading">
|
||||
<div class="row-item row-item-left">{{ localize 'MGT2.Items.Characteristics' }}</div>
|
||||
<div class="row-item row-item-left" style="flex:0 0 120px;">{{ localize 'MGT2.Items.Modifiers' }}</div>
|
||||
<div class="row-item row-item-right item-controls"><a class="modifiers-create"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each system.modifiers as |modifier i| }}
|
||||
<div class="table-row modifiers-part" data-modifiers-part="{{i}}" role="rowgroup">
|
||||
<div class="row-item row-item-left">
|
||||
<select name="system.modifiers.{{i}}.characteristic">
|
||||
{{selectOptions ../config.Characteristics selected=modifier.characteristic localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="row-item row-item-left" style="flex:0 0 120px;">
|
||||
<input type="number" name="system.modifiers.{{i}}.value" value="{{modifier.value}}" />
|
||||
</div>
|
||||
<div class="row-item row-item-right item-controls">
|
||||
<a class="item-control modifiers-delete" title="Delete Trait"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="table-container" style="margin-top:8px;">
|
||||
<div class="table-row heading">
|
||||
<div class="row-item row-item-left" style="flex:0 0 30%;">{{ localize 'MGT2.Items.Traits' }}</div>
|
||||
<div class="row-item row-item-left">{{ localize 'MGT2.Items.Description' }}</div>
|
||||
<div class="row-item row-item-right item-controls"><a class="options-create" data-property="traits"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each system.traits as |trait i| }}
|
||||
<div class="table-row options-part" data-options-part="{{i}}" data-property="traits" role="rowgroup">
|
||||
<div class="row-item row-item-left" style="flex:0 0 30%;"><input type="text" name="system.traits.{{i}}.name" value="{{trait.name}}" /></div>
|
||||
<div class="row-item row-item-left">
|
||||
<textarea name="system.traits.{{i}}.description" rows="2">{{trait.description}}</textarea>
|
||||
</div>
|
||||
<div class="row-item row-item-right item-controls">
|
||||
<a class="item-control options-delete" title="Delete Trait"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,79 +1,84 @@
|
||||
<form class="{{cssClass}} flexrow itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header">
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
{{#if (eq system.subType "skill")}}
|
||||
<label>{{localize 'MGT2.TalentSubType.skill'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-star"></i> {{localize 'MGT2.TalentSubType.skill'}}</span>
|
||||
{{else if (eq system.subType "psionic")}}
|
||||
<label>{{localize 'MGT2.TalentSubType.psionic'}}</label>
|
||||
<span class="item-type-label"><i class="fas fa-brain"></i> {{localize 'MGT2.TalentSubType.psionic'}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" />
|
||||
</div>
|
||||
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
|
||||
{{!-- Tab 1 : Description --}}
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Type' }}</label>
|
||||
<select name="system.subType">
|
||||
{{selectOptions config.TalentSubType selected = system.subType localize = true}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{!-- Tab 2 : Détails --}}
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="item-details-grid">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Type' }}</label>
|
||||
<select name="system.subType">
|
||||
{{selectOptions config.TalentSubType selected=system.subType localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-100">
|
||||
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" />
|
||||
{{#if (eq system.subType "skill")}}
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Speciality' }}</label>
|
||||
<input type="text" name="system.skill.speciality" value="{{system.skill.speciality}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Level' }}</label>
|
||||
<input type="text" name="system.level" value="{{system.level}}" data-dtype="Number" />
|
||||
</div>
|
||||
</div>
|
||||
{{else if (eq system.subType "psionic")}}
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Level' }}</label>
|
||||
<input type="text" name="system.level" value="{{system.level}}" data-dtype="Number" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Reach' }}</label>
|
||||
<select name="system.psionic.reach">
|
||||
<option></option>
|
||||
{{selectOptions config.PsionicReach selected = system.psionic.reach localize = true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.PSICost' }}</label>
|
||||
<input type="number" name="system.psionic.cost" value="{{system.psionic.cost}}" data-dtype="Number" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Duration' }}</label>
|
||||
<div class="flexrow">
|
||||
<input type="text" name="system.psionic.duration" value="{{system.psionic.duration}}" data-dtype="String" class="flex-basis-50" />
|
||||
<select name="system.psionic.durationUnit" class="flex-basis-50 flex-fix">
|
||||
{{selectOptions config.Durations selected=system.psionic.durationUnit localize = true}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<nav class="horizontal-tabs tabs mt-1" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="config">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<textarea name="system.description" rows="6">{{system.description}}</textarea>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="config">
|
||||
{{#if (eq system.subType "skill")}}
|
||||
<div class="field-group mt-05">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.skill.reduceEncumbrance" data-dtype="Boolean" {{checked system.skill.reduceEncumbrance}} />{{ localize 'MGT2.Items.ReduceEncumbrance' }}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Level' }}</label>
|
||||
<input type="number" name="system.level" value="{{system.level}}" data-dtype="Number" class="short" />
|
||||
</div>
|
||||
{{#if (eq system.subType "skill")}}
|
||||
<div class="field-row full">
|
||||
<label>{{ localize 'MGT2.Items.Speciality' }}</label>
|
||||
<input type="text" name="system.skill.speciality" value="{{system.skill.speciality}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox">
|
||||
<input type="checkbox" name="system.skill.reduceEncumbrance" data-dtype="Boolean" {{checked system.skill.reduceEncumbrance}} />
|
||||
{{ localize 'MGT2.Items.ReduceEncumbrance' }}
|
||||
</label>
|
||||
</div>
|
||||
{{else if (eq system.subType "psionic")}}
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.PSICost' }}</label>
|
||||
<input type="number" name="system.psionic.cost" value="{{system.psionic.cost}}" data-dtype="Number" class="short" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Reach' }}</label>
|
||||
<select name="system.psionic.reach">
|
||||
<option value=""></option>
|
||||
{{selectOptions config.PsionicReach selected=system.psionic.reach localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-row full">
|
||||
<label>{{ localize 'MGT2.Items.Duration' }}</label>
|
||||
<div class="range-inputs">
|
||||
<input type="text" name="system.psionic.duration" value="{{system.psionic.duration}}" data-dtype="String" />
|
||||
<select name="system.psionic.durationUnit">
|
||||
{{selectOptions config.Durations selected=system.psionic.durationUnit localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{!-- Tab 3 : Configuration (jet de dé) --}}
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,89 +1,124 @@
|
||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
||||
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Weapon' }}</label></div>
|
||||
<div class="itemsheet-panel">
|
||||
<div class="itemsheet-maincol">
|
||||
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
{{> systems/mgt2/templates/items/parts/sheet-physical-item.html }}
|
||||
<div class="{{cssClass}} itemsheet">
|
||||
<div class="item-type-bar">
|
||||
<span class="item-type-label"><i class="fas fa-gun"></i> {{ localize 'MGT2.Items.Weapon' }}</span>
|
||||
</div>
|
||||
<div class="item-sheet-header">
|
||||
<img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
|
||||
<input class="item-header-name" name="name" type="text" value="{{item.name}}" />
|
||||
</div>
|
||||
|
||||
<nav class="horizontal-tabs tabs" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab-content-area">
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-100">
|
||||
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" />
|
||||
<nav class="horizontal-tabs tabs mt-1" data-group="primary">
|
||||
<a class="item tab-select" data-tab="tab1">{{ localize 'MGT2.Items.Description' }}</a>
|
||||
<a class="item tab-select" data-tab="tab2">{{ localize 'MGT2.Items.Details' }}</a>
|
||||
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
|
||||
</nav>
|
||||
<div class="tab" data-group="primary" data-tab="tab1">
|
||||
<div class="field-group mt-1">
|
||||
<label>{{ localize 'MGT2.Items.Description' }}</label>
|
||||
<textarea name="system.description" rows="6">{{system.description}}</textarea>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="item-details-grid">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Quantity' }}</label>
|
||||
<input type="number" name="system.quantity" value="{{system.quantity}}" data-dtype="Number" integer="true" positive="true" class="short" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab2">
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group flex-1">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />{{ localize 'MGT2.Items.Equipped' }}</label>
|
||||
</div>
|
||||
{{#if hadContainer}}
|
||||
<div class="field-group">
|
||||
<label>{{ localize 'MGT2.Items.Storage' }}</label>
|
||||
<select name="system.container.id">
|
||||
{{selectOptions containers selected=system.container.id nameAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.TL' }}</label>
|
||||
<select name="system.tl">{{selectOptions config.TL selected=system.tl localize=true}}</select>
|
||||
</div>
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group w3-10">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.range.isMelee" data-dtype="Boolean" {{checked system.range.isMelee}} />{{ localize 'MGT2.Items.IsMelee' }}</label>
|
||||
</div>
|
||||
{{#unless system.range.isMelee}}
|
||||
<div class="field-group w3-10">
|
||||
<label>{{ localize 'MGT2.Items.Range' }}</label>
|
||||
<div class="flexrow">
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Weight' }} ({{unitlabels.weight}})</label>
|
||||
<input type="number" name="weight" value="{{weight}}" data-dtype="Number" step="0.5" class="short" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Cost' }}</label>
|
||||
<input type="number" name="system.cost" value="{{system.cost}}" data-dtype="Number" step="1" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Damage' }}</label>
|
||||
<input type="text" name="system.damage" value="{{system.damage}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Magazine' }}</label>
|
||||
<input type="number" name="system.magazine" value="{{system.magazine}}" data-dtype="Number" class="short" />
|
||||
</div>
|
||||
{{#unless system.range.isMelee}}
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.Range' }}</label>
|
||||
<div class="range-inputs">
|
||||
<input type="text" name="system.range.value" value="{{system.range.value}}" data-dtype="String" />
|
||||
<select name="system.range.unit" class="flex-fix" style="width: 5rem;">
|
||||
{{selectOptions config.MetricRange selected=system.range.unit localize = true}}
|
||||
<select name="system.range.unit">
|
||||
{{selectOptions config.MetricRange selected=system.range.unit localize=true}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</div>
|
||||
<div class="field-groups mt-1">
|
||||
<div class="field-group w3-10">
|
||||
<label>{{ localize 'MGT2.Items.Damage' }}</label>
|
||||
<input type="text" name="system.damage" value="{{system.damage}}" data-dtype="String" />
|
||||
</div>
|
||||
<div class="field-group w3-10" style="margin: 0 1rem;">
|
||||
<label>{{ localize 'MGT2.Items.Magazine' }}</label>
|
||||
<input type="number" name="system.magazine" value="{{system.magazine}}" data-dtype="Number" />
|
||||
</div>
|
||||
<div class="field-group w3-10">
|
||||
<label>{{ localize 'MGT2.Items.MagazineCost' }}</label>
|
||||
<input type="number" name="system.magazineCost" value="{{system.magazineCost}}" data-dtype="Number" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<div class="table-row heading color-2">
|
||||
<div class="row-item row-item-left upcase">{{ localize 'MGT2.Items.Trait' }}</div>
|
||||
<div class="row-item row-item-left">{{ localize 'MGT2.Items.Description' }}</div>
|
||||
<div class="row-item row-item-right"><a class="options-create" data-property="traits"><i class="fas fa-plus"></i></a></div>
|
||||
</div>
|
||||
{{#each system.traits as |trait i| }}
|
||||
<div class="table-row dropitem options-part" data-options-part="{{i}}" data-property="traits" role="rowgroup">
|
||||
<div class="row-item row-item-left"><input type="text" name="system.traits.{{i}}.name" value="{{trait.name}}" /></div>
|
||||
<div class="row-item row-item-left">
|
||||
<textarea name="system.traits.{{i}}.description" rows="3">{{trait.description}}</textarea>
|
||||
</div>
|
||||
<div class="row-item row-item-right item-controls">
|
||||
<a class="item-control options-delete" title="Delete Trait"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/unless}}
|
||||
<div class="field-row">
|
||||
<label>{{ localize 'MGT2.Items.MagazineCost' }}</label>
|
||||
<input type="number" name="system.magazineCost" value="{{system.magazineCost}}" data-dtype="Number" class="short" />
|
||||
</div>
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.weightless" data-dtype="Boolean" {{checked system.weightless}} />{{ localize 'MGT2.Items.Weightless' }}</label>
|
||||
</div>
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.range.isMelee" data-dtype="Boolean" {{checked system.range.isMelee}} />{{ localize 'MGT2.Items.IsMelee' }}</label>
|
||||
</div>
|
||||
<div class="field-row field-row--check">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />{{ localize 'MGT2.Items.Equipped' }}</label>
|
||||
</div>
|
||||
{{#if hadContainer}}
|
||||
<div class="field-row full">
|
||||
<label>{{ localize 'MGT2.Items.Storage' }}</label>
|
||||
<select name="system.container.id">
|
||||
{{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
|
||||
</div>
|
||||
<fieldset class="mgt2-weapon-traits">
|
||||
<legend>{{ localize 'MGT2.WeaponTraits.SectionTitle' }}</legend>
|
||||
<div class="mgt2-weapon-traits-grid">
|
||||
<div class="mgt2-trait-num" data-tooltip="{{ localize 'MGT2.WeaponTraits.APHint' }}">
|
||||
<label>{{ localize 'MGT2.WeaponTraits.AP' }}</label>
|
||||
<input type="number" name="system.traits.ap" value="{{system.traits.ap}}" data-dtype="Number" min="0" class="short" />
|
||||
</div>
|
||||
<div class="mgt2-trait-num" data-tooltip="{{ localize 'MGT2.WeaponTraits.AutoHint' }}">
|
||||
<label>{{ localize 'MGT2.WeaponTraits.Auto' }}</label>
|
||||
<input type="number" name="system.traits.auto" value="{{system.traits.auto}}" data-dtype="Number" min="0" class="short" />
|
||||
</div>
|
||||
<div class="mgt2-trait-num" data-tooltip="{{ localize 'MGT2.WeaponTraits.BlastHint' }}">
|
||||
<label>{{ localize 'MGT2.WeaponTraits.Blast' }}</label>
|
||||
<input type="number" name="system.traits.blast" value="{{system.traits.blast}}" data-dtype="Number" min="0" class="short" />
|
||||
</div>
|
||||
<label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.BulkyHint' }}">
|
||||
<input type="checkbox" name="system.traits.bulky" data-dtype="Boolean" {{checked system.traits.bulky}} />{{ localize 'MGT2.WeaponTraits.Bulky' }}
|
||||
</label>
|
||||
<label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.VeryBulkyHint' }}">
|
||||
<input type="checkbox" name="system.traits.veryBulky" data-dtype="Boolean" {{checked system.traits.veryBulky}} />{{ localize 'MGT2.WeaponTraits.VeryBulky' }}
|
||||
</label>
|
||||
<label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.StunHint' }}">
|
||||
<input type="checkbox" name="system.traits.stun" data-dtype="Boolean" {{checked system.traits.stun}} />{{ localize 'MGT2.WeaponTraits.Stun' }}
|
||||
</label>
|
||||
<label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.SmartHint' }}">
|
||||
<input type="checkbox" name="system.traits.smart" data-dtype="Boolean" {{checked system.traits.smart}} />{{ localize 'MGT2.WeaponTraits.Smart' }}
|
||||
</label>
|
||||
<label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.RadiationHint' }}">
|
||||
<input type="checkbox" name="system.traits.radiation" data-dtype="Boolean" {{checked system.traits.radiation}} />{{ localize 'MGT2.WeaponTraits.Radiation' }}
|
||||
</label>
|
||||
<label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.ScopeHint' }}">
|
||||
<input type="checkbox" name="system.traits.scope" data-dtype="Boolean" {{checked system.traits.scope}} />{{ localize 'MGT2.WeaponTraits.Scope' }}
|
||||
</label>
|
||||
<label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.ZeroGHint' }}">
|
||||
<input type="checkbox" name="system.traits.zeroG" data-dtype="Boolean" {{checked system.traits.zeroG}} />{{ localize 'MGT2.WeaponTraits.ZeroG' }}
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="tab" data-group="primary" data-tab="tab3">
|
||||
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,38 +1,205 @@
|
||||
<form class="{{cssClass}} flexcol" autocomplete="off" style="padding: 0 6px;">
|
||||
|
||||
{{!-- Hidden fields for healing --}}
|
||||
{{#if showHeal}}
|
||||
<input type="hidden" name="healType" value="{{healType}}" />
|
||||
{{/if}}
|
||||
|
||||
{{!-- ── Mode CRÉATURE : sélecteur de compétence de la créature ── --}}
|
||||
{{#if isCreature}}
|
||||
|
||||
{{#if showSkillSelector}}
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.CreatureSkill' }}</label>
|
||||
<select name="creatureSkillIndex">
|
||||
<option value="-1">— {{ localize 'MGT2.RollPrompt.NoSkill' }} —</option>
|
||||
{{#each creatureSkills}}
|
||||
<option value="{{@index}}" {{#if (eq @index ../selectedSkillIndex)}}selected{{/if}}>{{name}} ({{showDM level}})</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="form-group roll-prompt-info">
|
||||
<span class="roll-prompt-skill-name">{{skillName}}</span>
|
||||
<span class="roll-prompt-skill-level">{{showDM skillLevel}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{!-- ── Mode PERSONNAGE : caractéristique + compétence + états ── --}}
|
||||
{{else}}
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.CharacteristicDM' }}</label>
|
||||
<select name="characteristic">
|
||||
{{selectOptions characteristics selected=characteristic nameAttr="_id" labelAttr="name"}}
|
||||
{{selectOptions characteristics selected=characteristic valueAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.SkillDM' }}</label>
|
||||
<select name="skill">
|
||||
<option></option>
|
||||
{{selectOptions skills selected=skill nameAttr="_id" labelAttr="name"}}
|
||||
{{selectOptions skills selected=skill valueAttr="_id" labelAttr="name"}}
|
||||
</select>
|
||||
</div>
|
||||
{{#unless isAttack}}
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.Timeframes' }}</label>
|
||||
<select name="timeframes">
|
||||
{{selectOptions config.Timeframes selected = timeframe localize = true}}
|
||||
</select>
|
||||
</div>
|
||||
{{/unless}}
|
||||
<fieldset>
|
||||
<legend>{{ localize 'MGT2.RollPrompt.States' }}</legend>
|
||||
<div class="form-group">
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="encumbrance" data-dtype="Boolean" {{checked encumbrance}} />{{ localize 'MGT2.RollPrompt.EncumbranceDM' }}</label>
|
||||
<label class="mgt2-checkbox"><input type="checkbox" name="fatigue" data-dtype="Boolean" {{checked fatigue}} />{{ localize 'MGT2.RollPrompt.FatigueDM' }}</label>
|
||||
{{!-- Hidden checkboxes preserve form values for the roll calculation --}}
|
||||
<input type="checkbox" name="encumbrance" data-dtype="Boolean" {{checked encumbrance}} style="display:none" />
|
||||
<input type="checkbox" name="fatigue" data-dtype="Boolean" {{checked fatigue}} style="display:none" />
|
||||
{{!-- Read-only state badges --}}
|
||||
<div class="roll-prompt-states">
|
||||
<span class="roll-prompt-state-badge {{#if encumbrance}}is-active{{/if}}">
|
||||
<i class="fa-solid fa-weight-hanging"></i>
|
||||
{{ localize 'MGT2.RollPrompt.EncumbranceDM' }}
|
||||
{{#if encumbrance}}<strong>−2</strong>{{/if}}
|
||||
</span>
|
||||
<span class="roll-prompt-state-badge {{#if fatigue}}is-active{{/if}}">
|
||||
<i class="fa-solid fa-person-dots-from-line"></i>
|
||||
{{ localize 'MGT2.RollPrompt.FatigueDM' }}
|
||||
{{#if fatigue}}<strong>−2</strong>{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{#if isRanged}}
|
||||
<fieldset class="mgt2-ranged-modifiers">
|
||||
<legend>{{ localize 'MGT2.RollPrompt.RangedModifiers' }}</legend>
|
||||
{{#if hasScope}}
|
||||
<div class="mgt2-scope-badge" data-tooltip="{{ localize 'MGT2.RollPrompt.ScopeHint' }}">
|
||||
<i class="fa-solid fa-crosshairs"></i>
|
||||
{{ localize 'MGT2.RollPrompt.ScopeActive' }}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if hasZeroG}}
|
||||
<div class="mgt2-zerog-badge" data-tooltip="{{ localize 'MGT2.RollPrompt.ZeroGHint' }}">
|
||||
<i class="fa-solid fa-satellite"></i>
|
||||
{{ localize 'MGT2.RollPrompt.ZeroGActive' }}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if autoLevel}}
|
||||
<div class="form-group mgt2-auto-mode">
|
||||
<label>{{ localize 'MGT2.RollPrompt.FireMode' }}</label>
|
||||
<select name="autoMode">
|
||||
<option value="single">{{ localize 'MGT2.RollPrompt.AutoSingle' }}</option>
|
||||
<option value="burst">{{ localize 'MGT2.RollPrompt.AutoBurst' }} (+{{autoLevel}})</option>
|
||||
<option value="fullAuto">{{ localize 'MGT2.RollPrompt.AutoFull' }} (×{{autoLevel}})</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="mgt2-auto-hint">⚠ {{ localize 'MGT2.RollPrompt.AutoNoAim' }}</p>
|
||||
{{/if}}
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.Range' }}</label>
|
||||
<select name="rangedRange">
|
||||
<option value="1">{{ localize 'MGT2.RollPrompt.RangeShort' }} (+1)</option>
|
||||
<option value="0" selected>{{ localize 'MGT2.RollPrompt.RangeNormal' }}</option>
|
||||
<option value="-2">{{ localize 'MGT2.RollPrompt.RangeLong' }} (−2)</option>
|
||||
<option value="-4">{{ localize 'MGT2.RollPrompt.RangeExtreme' }} (−4)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.Aim' }}</label>
|
||||
<select name="rangedAim">
|
||||
<option value="0" selected>0</option>
|
||||
<option value="1">+1</option>
|
||||
<option value="2">+2</option>
|
||||
<option value="3">+3</option>
|
||||
<option value="4">+4</option>
|
||||
<option value="5">+5</option>
|
||||
<option value="6">+6</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.FastTarget' }}</label>
|
||||
<select name="rangedFastTarget">
|
||||
<option value="0" selected>0</option>
|
||||
<option value="-1">−1</option>
|
||||
<option value="-2">−2</option>
|
||||
<option value="-3">−3</option>
|
||||
<option value="-4">−4</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mgt2-ranged-checkboxes">
|
||||
<label class="mgt2-checkbox-tag"><input type="checkbox" name="rangedLaserSight" data-dtype="Boolean" />{{ localize 'MGT2.RollPrompt.LaserSight' }} <em>(+1 si Viser)</em></label>
|
||||
<label class="mgt2-checkbox-tag"><input type="checkbox" name="rangedCover" data-dtype="Boolean" />{{ localize 'MGT2.RollPrompt.Cover' }} <em>(−2)</em></label>
|
||||
<label class="mgt2-checkbox-tag"><input type="checkbox" name="rangedProne" data-dtype="Boolean" />{{ localize 'MGT2.RollPrompt.Prone' }} <em>(−1)</em></label>
|
||||
</div>
|
||||
<div class="form-group mgt2-ranged-dodge">
|
||||
<label class="mgt2-checkbox-tag"><input type="checkbox" name="rangedDodge" data-dtype="Boolean" />{{ localize 'MGT2.RollPrompt.Dodge' }}</label>
|
||||
<select name="rangedDodgeDM">
|
||||
<option value="0" selected>MD 0</option>
|
||||
<option value="-1">MD −1</option>
|
||||
<option value="-2">MD −2</option>
|
||||
<option value="-3">MD −3</option>
|
||||
<option value="-4">MD −4</option>
|
||||
<option value="-5">MD −5</option>
|
||||
<option value="-6">MD −6</option>
|
||||
</select>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
{{#if isMelee}}
|
||||
<fieldset class="mgt2-ranged-modifiers">
|
||||
<legend>{{ localize 'MGT2.RollPrompt.MeleeModifiers' }}</legend>
|
||||
<div class="form-group mgt2-ranged-dodge">
|
||||
<label class="mgt2-checkbox-tag"><input type="checkbox" name="meleeDodge" data-dtype="Boolean" />{{ localize 'MGT2.RollPrompt.Dodge' }}</label>
|
||||
<select name="meleeDodgeDM">
|
||||
<option value="0" selected>MD 0</option>
|
||||
<option value="-1">MD −1</option>
|
||||
<option value="-2">MD −2</option>
|
||||
<option value="-3">MD −3</option>
|
||||
<option value="-4">MD −4</option>
|
||||
<option value="-5">MD −5</option>
|
||||
<option value="-6">MD −6</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mgt2-ranged-dodge">
|
||||
<label class="mgt2-checkbox-tag"><input type="checkbox" name="meleeParry" data-dtype="Boolean" />{{ localize 'MGT2.RollPrompt.Parry' }}</label>
|
||||
<select name="meleeParryDM">
|
||||
<option value="0" selected>MD 0</option>
|
||||
<option value="-1">MD −1</option>
|
||||
<option value="-2">MD −2</option>
|
||||
<option value="-3">MD −3</option>
|
||||
<option value="-4">MD −4</option>
|
||||
<option value="-5">MD −5</option>
|
||||
<option value="-6">MD −6</option>
|
||||
</select>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.CustomDM' }}</label>
|
||||
<input type="text" name="customDM" maxlength="15" />
|
||||
<select name="customDM">
|
||||
<option value="-8">−8</option>
|
||||
<option value="-7">−7</option>
|
||||
<option value="-6">−6</option>
|
||||
<option value="-5">−5</option>
|
||||
<option value="-4">−4</option>
|
||||
<option value="-3">−3</option>
|
||||
<option value="-2">−2</option>
|
||||
<option value="-1">−1</option>
|
||||
<option value="0" selected>0</option>
|
||||
<option value="1">+1</option>
|
||||
<option value="2">+2</option>
|
||||
<option value="3">+3</option>
|
||||
<option value="4">+4</option>
|
||||
<option value="5">+5</option>
|
||||
<option value="6">+6</option>
|
||||
<option value="7">+7</option>
|
||||
<option value="8">+8</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ localize 'MGT2.RollPrompt.Difficulty' }}</label>
|
||||
<select name="difficulty">
|
||||
<option></option>
|
||||
<option value="">—</option>
|
||||
{{selectOptions config.Difficulty selected = difficulty localize = true}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user