9 Commits

Author SHA1 Message Date
c5a0a47b25 Build release script
Some checks failed
Release Creation / build (release) Failing after 1m9s
2026-04-21 19:56:22 +02:00
a48f447b99 Build release script
Some checks failed
Release Creation / build (release) Failing after 24s
2026-04-21 19:50:55 +02:00
e398b157f4 Build release script 2026-04-21 19:50:33 +02:00
a349402306 Build release script 2026-04-21 19:49:18 +02:00
bdc305abd8 Build release script 2026-04-21 18:57:53 +02:00
6ef454d533 Gestion des traits d'arme et des munitions 2026-04-21 18:50:49 +02:00
74f1b581f7 Ajout des fonctions de gestion des soins 2026-04-21 10:22:39 +02:00
df0a93d715 feat: complete visual redesign of all item sheets + compact chat roll messages
- Rewrote _item.sass with unified layout: type-bar + dark header + stats pills + tabs
 - All 11 item templates converted to new pill-based stats bar
 - Chat roll message font sizes and padding compacted
 - Physical item partial rewritten as stat pills (QTY/WEIGHT/COST/TL)
 - Legacy itemsheet-header and itemsheet-maincol hidden
2026-04-19 21:31:52 +02:00
d62d14c1da Second round de corrections et améliorations 2026-04-19 18:55:34 +02:00
65 changed files with 6831 additions and 1254 deletions

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

View File

@@ -18,6 +18,12 @@
"Save": "Save", "Save": "Save",
"Cancel": "Cancel", "Cancel": "Cancel",
"Close": "Close", "Close": "Close",
"Dialog": {
"ConfirmDeleteTitle": "Confirm Deletion",
"ConfirmDeleteContent": "Are you sure you want to delete \"{name}\"?",
"Yes": "Yes",
"No": "No"
},
"Themes": { "Themes": {
"BlackAndRed": "Classic Traveller Cover", "BlackAndRed": "Classic Traveller Cover",
"Mwamba": "Oppa Mwamba Style", "Mwamba": "Oppa Mwamba Style",
@@ -54,20 +60,21 @@
"Gender": "Gender", "Gender": "Gender",
"Pronouns": "Pronouns", "Pronouns": "Pronouns",
"Actor": { "Actor": {
"StudyPeriod":"Study Period", "StudyPeriod": "Study Period",
"TrainingInSkill":"Training In Skill", "TrainingInSkill": "Training In Skill",
"Completed":"Completed", "Completed": "Completed",
"Weeks":"Weeks", "Weeks": "Weeks",
"NewCareer":"New Career", "NewCareer": "New Career",
"AddCareer":"Add Career", "AddCareer": "Add Career",
"EditCareer":"Edit Career", "ThisTrait": "this trait",
"EditTrait":"Éditer Trait", "EditCareer": "Edit Career",
"EditTrait": "Éditer Trait",
"DeleteTrait": "Supprimer Trait", "DeleteTrait": "Supprimer Trait",
"DeleteCareer":"Delete Career", "DeleteCareer": "Delete Career",
"NewSkill":"New Skill", "NewSkill": "New Skill",
"DeleteSkill":"Delete Skill", "DeleteSkill": "Delete Skill",
"EditSkill":"EditSkill", "EditSkill": "EditSkill",
"PsionicTalents":"Psionic Talents", "PsionicTalents": "Psionic Talents",
"NewPsionicTalent": "New Psionic Talent", "NewPsionicTalent": "New Psionic Talent",
"AddPsionicTalent": "Add Psionic Talent", "AddPsionicTalent": "Add Psionic Talent",
"EditPsionic": "Edit Psionic", "EditPsionic": "Edit Psionic",
@@ -343,7 +350,36 @@
"EncumbranceDM": "Encumbrance (DM -2)", "EncumbranceDM": "Encumbrance (DM -2)",
"FatigueDM": "Fatigue (DM -2)", "FatigueDM": "Fatigue (DM -2)",
"Boon": "Boon", "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": { "Timeframes": {
"Normal": "Normal", "Normal": "Normal",
@@ -355,7 +391,26 @@
"ApplyDamages": "Apply Damages", "ApplyDamages": "Apply Damages",
"Damages": "Roll damages", "Damages": "Roll damages",
"Success": "Success", "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": { "Items": {
@@ -393,7 +448,7 @@
"Informations": "Informations", "Informations": "Informations",
"Improvement": "Improvement", "Improvement": "Improvement",
"Interval": "Interval", "Interval": "Interval",
"IsMelee": "IsMelee", "IsMelee": "Melee Weapon",
"Items": "Items", "Items": "Items",
"Level": "Level", "Level": "Level",
"Location": "Location", "Location": "Location",
@@ -410,6 +465,7 @@
"Occupation": "Occupation", "Occupation": "Occupation",
"OnHand": "On Hand", "OnHand": "On Hand",
"Options": "Options", "Options": "Options",
"PerDay": "per day",
"PSICost": "PSI Cost", "PSICost": "PSI Cost",
"Powered": "Powered", "Powered": "Powered",
"Processing": "Processing", "Processing": "Processing",
@@ -443,10 +499,150 @@
"Weightless": "Weightless", "Weightless": "Weightless",
"Quantity": "Quantity" "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": { "Durations": {
"Seconds": "Seconds", "Seconds": "Seconds",
"Minutes": "Minutes", "Minutes": "Minutes",
"Heures": "Hours" "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"
} }

View File

@@ -18,6 +18,12 @@
"Save": "Sauvegarder", "Save": "Sauvegarder",
"Cancel": "Annuler", "Cancel": "Annuler",
"Close": "Fermer", "Close": "Fermer",
"Dialog": {
"ConfirmDeleteTitle": "Confirmer la suppression",
"ConfirmDeleteContent": "Êtes-vous sûr de vouloir supprimer \"{name}\" ?",
"Yes": "Oui",
"No": "Non"
},
"Themes": { "Themes": {
"BlackAndRed": "Couverture Classique Traveller", "BlackAndRed": "Couverture Classique Traveller",
"Mwamba": "Oppa Mwamba Style", "Mwamba": "Oppa Mwamba Style",
@@ -54,20 +60,21 @@
"Gender": "Genre", "Gender": "Genre",
"Pronouns": "Pronoms", "Pronouns": "Pronoms",
"Actor": { "Actor": {
"StudyPeriod":"Période d'étude", "StudyPeriod": "Période d'étude",
"TrainingInSkill":"Compétence en formation", "TrainingInSkill": "Compétence en formation",
"Completed":"Completée", "Completed": "Completée",
"Weeks":"Semaines", "Weeks": "Semaines",
"NewCareer":"Nouvelle Carrière", "NewCareer": "Nouvelle Carrière",
"AddCareer":"Ajouter Carrière", "AddCareer": "Ajouter Carrière",
"EditCareer":"Éditer Carrière", "ThisTrait": "ce trait",
"EditTrait":"Éditer Trait", "EditCareer": "Éditer Carrière",
"EditTrait": "Éditer Trait",
"DeleteTrait": "Supprimer Trait", "DeleteTrait": "Supprimer Trait",
"DeleteCareer":"Supprimer Carrière", "DeleteCareer": "Supprimer Carrière",
"NewSkill":"Nouvelle Compétence", "NewSkill": "Nouvelle Compétence",
"DeleteSkill":"Supprimer Compétence", "DeleteSkill": "Supprimer Compétence",
"EditSkill":"Éditer Compétence", "EditSkill": "Éditer Compétence",
"PsionicTalents":"Talents Psionique", "PsionicTalents": "Talents Psionique",
"NewPsionicTalent": "Nouveau Talent Psionique", "NewPsionicTalent": "Nouveau Talent Psionique",
"AddPsionicTalent": "Ajouter Talent Psionique", "AddPsionicTalent": "Ajouter Talent Psionique",
"EditPsionic": "Éditer Talent Psionique", "EditPsionic": "Éditer Talent Psionique",
@@ -343,7 +350,36 @@
"EncumbranceDM": "Encombrement (MD -2)", "EncumbranceDM": "Encombrement (MD -2)",
"FatigueDM": "Fatigue (MD -2)", "FatigueDM": "Fatigue (MD -2)",
"Boon": "Avantage", "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": { "Timeframes": {
"Normal": "Normal", "Normal": "Normal",
@@ -355,7 +391,26 @@
"ApplyDamages": "Appliquer Dégâts", "ApplyDamages": "Appliquer Dégâts",
"Damages": "Lancer les Dégâts", "Damages": "Lancer les Dégâts",
"Success": "Succès", "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": { "Items": {
@@ -393,7 +448,7 @@
"Informations": "Informations", "Informations": "Informations",
"Improvement": "Améliorations", "Improvement": "Améliorations",
"Interval": "Intervalle", "Interval": "Intervalle",
"IsMelee": "Est Mêlée", "IsMelee": "Arme de Mêlée",
"Items": "Objets", "Items": "Objets",
"Level": "Niveau", "Level": "Niveau",
"Location": "Localisation", "Location": "Localisation",
@@ -410,6 +465,7 @@
"Occupation": "Profession", "Occupation": "Profession",
"OnHand": "Sur Soi", "OnHand": "Sur Soi",
"Options": "Options", "Options": "Options",
"PerDay": "par jour",
"PSICost": "Coût PSI", "PSICost": "Coût PSI",
"Powered": "Alimenté", "Powered": "Alimenté",
"Processing": "Capacité de Traitement", "Processing": "Capacité de Traitement",
@@ -443,10 +499,150 @@
"Weightless": "Aucun Poids", "Weightless": "Aucun Poids",
"Quantity": "Quantité" "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": { "Durations": {
"Seconds": "Secondes", "Seconds": "Secondes",
"Minutes": "Minutes", "Minutes": "Minutes",
"Heures": "Heures" "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"
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -4,5 +4,8 @@
"scripts": { "scripts": {
"build": "rollup -c rollup.config.mjs", "build": "rollup -c rollup.config.mjs",
"watch": "rollup -c rollup.config.mjs --watch" "watch": "rollup -c rollup.config.mjs --watch"
},
"devDependencies": {
"rollup": "^4"
} }
} }

View File

@@ -52,9 +52,33 @@ export class TravellerActor extends Actor {
} }
} }
applyDamage(amount) { applyDamage(amount, { ignoreArmor = false, ap = 0, stun = false } = {}) {
if (this.type === "character") { if (this.type === "character") {
ActorCharacter.applyDamage(this, amount); 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 });
}
} }
} }

View File

@@ -51,13 +51,14 @@ export class CharacterPrompts {
} }
static async openEditorFullView(title, html) { 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", { const htmlContent = await renderTemplate("systems/mgt2/templates/editor-fullview.html", {
config: CONFIG.MGT2, config: CONFIG.MGT2,
html html: html ?? ""
}); });
const theme = game.settings.get("mgt2", "theme"); const theme = game.settings.get("mgt2", "theme");
await DialogV2.wait({ await DialogV2.wait({
window: { title }, window: { title: safeTitle },
content: htmlContent, content: htmlContent,
rejectClose: false, rejectClose: false,
buttons: [ buttons: [
@@ -70,4 +71,35 @@ export class CharacterPrompts {
] ]
}); });
} }
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;
}
}
]
});
}
} }

View File

@@ -254,7 +254,10 @@ export class ActorCharacter {
} }
updateData["system.inventory.weight"] = onHandWeight; updateData["system.inventory.weight"] = onHandWeight;
updateData["system.states.encumbrance"] = onHandWeight > $this.system.inventory.encumbrance.normal; // 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); await $this.update(updateData);
@@ -275,6 +278,8 @@ export class ActorCharacter {
let heavy = normal * 2; let heavy = normal * 2;
foundry.utils.setProperty(changed, "system.inventory.encumbrance.normal", normal); foundry.utils.setProperty(changed, "system.inventory.encumbrance.normal", normal);
foundry.utils.setProperty(changed, "system.inventory.encumbrance.heavy", heavy); 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")); //console.log(foundry.utils.getProperty(changed, "system.characteristics.strength.value"));
@@ -331,19 +336,39 @@ export class ActorCharacter {
// $this.update({ system: { characteristics: data } }); // $this.update({ system: { characteristics: data } });
// } // }
static applyDamage($this, amount) { static async applyDamage($this, amount, { ignoreArmor = false, ap = 0, stun = false } = {}) {
if (isNaN(amount) || amount === 0) return; if (isNaN(amount) || amount === 0) return { incapRounds: 0 };
const rank1 = $this.system.config.damages.rank1; const rank1 = $this.system.config.damages.rank1;
const rank2 = $this.system.config.damages.rank2; const rank2 = $this.system.config.damages.rank2;
const rank3 = $this.system.config.damages.rank3; 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 = {}; const data = {};
data[rank1] = { value: $this.system.characteristics[rank1].value }; data[rank1] = { value: $this.system.characteristics[rank1].value };
data[rank2] = { value: $this.system.characteristics[rank2].value }; data[rank2] = { value: $this.system.characteristics[rank2].value };
data[rank3] = { value: $this.system.characteristics[rank3].value }; data[rank3] = { value: $this.system.characteristics[rank3].value };
if (amount < 0) amount = Math.abs(amount);
for (const [key, rank] of Object.entries(data)) { for (const [key, rank] of Object.entries(data)) {
if (rank.value > 0) { if (rank.value > 0) {
if (rank.value >= amount) { if (rank.value >= amount) {
@@ -358,7 +383,50 @@ export class ActorCharacter {
} }
} }
$this.update({ system: { characteristics: data } }); 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) { static getContainers($this) {

View File

@@ -1,4 +1,5 @@
export { default as MGT2ActorSheet } from "./base-actor-sheet.mjs"; export { default as MGT2ActorSheet } from "./base-actor-sheet.mjs";
export { default as TravellerCharacterSheet } from "./character-sheet.mjs"; export { default as TravellerCharacterSheet } from "./character-sheet.mjs";
export { default as TravellerVehiculeSheet } from "./vehicule-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"; export { default as TravellerItemSheet } from "./item-sheet.mjs";

View File

@@ -3,6 +3,7 @@ import { MGT2 } from "../../config.js";
import { MGT2Helper } from "../../helper.js"; import { MGT2Helper } from "../../helper.js";
import { RollPromptHelper } from "../../roll-prompt.js"; import { RollPromptHelper } from "../../roll-prompt.js";
import { CharacterPrompts } from "../../actors/character-prompts.js"; import { CharacterPrompts } from "../../actors/character-prompts.js";
import WeaponData from "../../models/items/weapon.mjs";
export default class TravellerCharacterSheet extends MGT2ActorSheet { export default class TravellerCharacterSheet extends MGT2ActorSheet {
@@ -33,6 +34,7 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
traitEdit: TravellerCharacterSheet.#onTraitEdit, traitEdit: TravellerCharacterSheet.#onTraitEdit,
traitDelete: TravellerCharacterSheet.#onTraitDelete, traitDelete: TravellerCharacterSheet.#onTraitDelete,
openEditor: TravellerCharacterSheet.#onOpenEditor, openEditor: TravellerCharacterSheet.#onOpenEditor,
heal: TravellerCharacterSheet.#onHeal,
}, },
} }
@@ -54,7 +56,11 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
async _prepareContext() { async _prepareContext() {
const context = await super._prepareContext(); const context = await super._prepareContext();
const actor = this.document; 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.enrichedFinanceNotes = await enrich(actor.system.finance?.notes);
context.settings = { context.settings = {
weightUnit: "kg", weightUnit: "kg",
usePronouns: game.settings.get("mgt2", "usePronouns"), usePronouns: game.settings.get("mgt2", "usePronouns"),
@@ -152,9 +158,10 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
if (item.hasOwnProperty("equipped")) { if (item.hasOwnProperty("equipped")) {
i._canEquip = true; i._canEquip = true;
i._toggleClass = item.equipped ? "active" : ""; i.toggleClass = item.equipped ? "active" : "";
} else { } else {
i._canEquip = false; i._canEquip = false;
i.toggleClass = "";
} }
switch (i.type) { switch (i.type) {
@@ -194,8 +201,7 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
i._range = i.system.range.isMelee i._range = i.system.range.isMelee
? game.i18n.localize("MGT2.Melee") ? game.i18n.localize("MGT2.Melee")
: MGT2Helper.getRangeDisplay(i.system.range); : MGT2Helper.getRangeDisplay(i.system.range);
if (i.system.traits?.length > 0) i._subInfo = WeaponData.getTraitsSummary(i.system.traits);
i._subInfo = i.system.traits.map(x => x.name).join(", ");
weapons.push(i); weapons.push(i);
break; break;
@@ -283,12 +289,11 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
const html = this.element; const html = this.element;
if (!this.isEditable) return; if (!this.isEditable) return;
this._bindClassEvent(html, ".roll", "click", TravellerCharacterSheet.#onRoll); this._bindClassEvent(html, "a[data-roll]", "click", TravellerCharacterSheet.#onRoll);
this._bindClassEvent(html, ".cfg-characteristic", "click", TravellerCharacterSheet.#onOpenCharacteristic); this._bindClassEvent(html, ".cfg-characteristic", "click", TravellerCharacterSheet.#onOpenCharacteristic);
this._bindClassEvent(html, ".item-create", "click", TravellerCharacterSheet.#onCreateItem); this._bindClassEvent(html, ".item-create", "click", TravellerCharacterSheet.#onCreateItem);
this._bindClassEvent(html, ".item-edit", "click", TravellerCharacterSheet.#onEditItem); this._bindClassEvent(html, ".item-edit", "click", TravellerCharacterSheet.#onEditItem);
this._bindClassEvent(html, ".item-delete", "click", TravellerCharacterSheet.#onDeleteItem); this._bindClassEvent(html, ".item-delete", "click", TravellerCharacterSheet.#onDeleteItem);
this._bindClassEvent(html, ".item-equip", "click", TravellerCharacterSheet.#onEquipItem);
this._bindClassEvent(html, ".item-storage-in", "click", TravellerCharacterSheet.#onItemStorageIn); this._bindClassEvent(html, ".item-storage-in", "click", TravellerCharacterSheet.#onItemStorageIn);
this._bindClassEvent(html, ".item-storage-out", "click", TravellerCharacterSheet.#onItemStorageOut); this._bindClassEvent(html, ".item-storage-out", "click", TravellerCharacterSheet.#onItemStorageOut);
this._bindClassEvent(html, ".software-eject", "click", TravellerCharacterSheet.#onSoftwareEject); this._bindClassEvent(html, ".software-eject", "click", TravellerCharacterSheet.#onSoftwareEject);
@@ -374,26 +379,22 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
let sourceItem = this.actor.getEmbeddedDocument("Item", sourceItemData.id); let sourceItem = this.actor.getEmbeddedDocument("Item", sourceItemData.id);
if (sourceItem) { if (sourceItem) {
if (!targetItem) return false; if (!targetItem) return false;
sourceItem = foundry.utils.deepClone(sourceItem); if (sourceItem.id === targetId) return false;
if (sourceItem._id === targetId) return false;
if (targetItem.type === "item" || targetItem.type === "equipment") { if (targetItem.type === "item" || targetItem.type === "equipment") {
if (targetItem.system.subType === "software") if (targetItem.system.subType === "software")
sourceItem.system.software.computerId = targetItem.system.software.computerId; await sourceItem.update({ "system.software.computerId": targetItem.system.software.computerId });
else else
sourceItem.system.container.id = targetItem.system.container.id; await sourceItem.update({ "system.container.id": targetItem.system.container.id });
this.actor.updateEmbeddedDocuments("Item", [sourceItem]);
return true; return true;
} else if (targetItem.type === "computer") { } else if (targetItem.type === "computer") {
sourceItem.system.software.computerId = targetId; await sourceItem.update({ "system.software.computerId": targetId });
this.actor.updateEmbeddedDocuments("Item", [sourceItem]);
return true; return true;
} else if (targetItem.type === "container") { } else if (targetItem.type === "container") {
if (targetItem.system.locked && !game.user.isGM) { if (targetItem.system.locked && !game.user.isGM) {
ui.notifications.error("Verrouillé"); ui.notifications.error("Verrouillé");
} else { } else {
sourceItem.system.container.id = targetId; await sourceItem.update({ "system.container.id": targetId });
this.actor.updateEmbeddedDocuments("Item", [sourceItem]);
return true; return true;
} }
} }
@@ -456,30 +457,46 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
if (item) item.sheet.render(true); if (item) item.sheet.render(true);
} }
static async #confirmDelete(name) {
return foundry.applications.api.DialogV2.confirm({
window: { title: game.i18n.localize("MGT2.Dialog.ConfirmDeleteTitle") },
content: `<p>${game.i18n.format("MGT2.Dialog.ConfirmDeleteContent", { name })}</p>`,
yes: { label: game.i18n.localize("MGT2.Dialog.Yes"), icon: "fas fa-trash" },
no: { label: game.i18n.localize("MGT2.Dialog.No"), icon: "fas fa-times" },
rejectClose: false,
modal: true
});
}
static async #onDeleteItem(event, target) { static async #onDeleteItem(event, target) {
event.preventDefault(); event.preventDefault();
const li = target.closest("[data-item-id]"); const li = target.closest("[data-item-id]");
if (!li?.dataset.itemId) return; if (!li?.dataset.itemId) return;
const item = this.actor.items.get(li.dataset.itemId);
if (!item) return;
const confirmed = await TravellerCharacterSheet.#confirmDelete(item.name);
if (!confirmed) return;
this.actor.deleteEmbeddedDocuments("Item", [li.dataset.itemId]); this.actor.deleteEmbeddedDocuments("Item", [li.dataset.itemId]);
} }
static async #onEquipItem(event, target) { static async #onEquipItem(event, target) {
event.preventDefault(); event.preventDefault();
const li = target.closest("[data-item-id]"); const li = target.closest("[data-item-id]");
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId)); const itemId = li?.dataset.itemId;
if (!itemId) return;
const item = this.actor.items.get(itemId);
if (!item) return; if (!item) return;
item.system.equipped = !item.system.equipped; await item.update({ "system.equipped": !item.system.equipped });
this.actor.updateEmbeddedDocuments("Item", [item]);
} }
static async #onItemStorageIn(event, target) { static async #onItemStorageIn(event, target) {
event.preventDefault(); event.preventDefault();
const li = target.closest("[data-item-id]"); const li = target.closest("[data-item-id]");
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId)); const item = this.actor.getEmbeddedDocument("Item", li?.dataset.itemId);
if (!item) return; if (!item) return;
if (item.type === "container") { if (item.type === "container") {
item.system.onHand = false; await item.update({ "system.onHand": false });
} else { } else {
const containers = this.actor.getContainers(); const containers = this.actor.getContainers();
let container; let container;
@@ -497,27 +514,24 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
ui.notifications.error("Objet verrouillé"); ui.notifications.error("Objet verrouillé");
return; return;
} }
item.system.container.id = container._id; await item.update({ "system.container.id": container._id });
} }
this.actor.updateEmbeddedDocuments("Item", [item]);
} }
static async #onItemStorageOut(event, target) { static async #onItemStorageOut(event, target) {
event.preventDefault(); event.preventDefault();
const li = target.closest("[data-item-id]"); const li = target.closest("[data-item-id]");
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId)); const item = this.actor.getEmbeddedDocument("Item", li?.dataset.itemId);
if (!item) return; if (!item) return;
item.system.container.id = ""; await item.update({ "system.container.id": "" });
this.actor.updateEmbeddedDocuments("Item", [item]);
} }
static async #onSoftwareEject(event, target) { static async #onSoftwareEject(event, target) {
event.preventDefault(); event.preventDefault();
const li = target.closest("[data-item-id]"); const li = target.closest("[data-item-id]");
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId)); const item = this.actor.getEmbeddedDocument("Item", li?.dataset.itemId);
if (!item) return; if (!item) return;
item.system.software.computerId = ""; await item.update({ "system.software.computerId": "" });
this.actor.updateEmbeddedDocuments("Item", [item]);
} }
static async #onContainerCreate(event) { static async #onContainerCreate(event) {
@@ -538,28 +552,27 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
const container = containers.find(x => x._id === this.actor.system.containerView); const container = containers.find(x => x._id === this.actor.system.containerView);
if (!container) return; if (!container) return;
const confirmed = await TravellerCharacterSheet.#confirmDelete(container.name);
if (!confirmed) return;
const containerItems = this.actor.items.filter( const containerItems = this.actor.items.filter(
x => x.system.hasOwnProperty("container") && x.system.container.id === container._id x => x.system.hasOwnProperty("container") && x.system.container.id === container._id
); );
if (containerItems.length > 0) { if (containerItems.length > 0) {
for (let item of containerItems) { const updates = containerItems.map(item => ({ _id: item.id, "system.container.id": "" }));
let clone = foundry.utils.deepClone(item); await this.actor.updateEmbeddedDocuments("Item", updates);
clone.system.container.id = "";
this.actor.updateEmbeddedDocuments("Item", [clone]);
}
} }
const cloneActor = foundry.utils.deepClone(this.actor); const actorUpdate = { "system.containerView": "" };
cloneActor.system.containerView = ""; if (this.actor.system.containerDropIn === container._id) {
if (cloneActor.system.containerDropIn === container._id) { actorUpdate["system.containerDropIn"] = "";
cloneActor.system.containerDropIn = "";
const remaining = containers.filter(x => x._id !== container._id); const remaining = containers.filter(x => x._id !== container._id);
if (remaining.length > 0) cloneActor.system.containerDropIn = remaining[0]._id; if (remaining.length > 0) actorUpdate["system.containerDropIn"] = remaining[0]._id;
} }
this.actor.deleteEmbeddedDocuments("Item", [container._id]); await this.actor.deleteEmbeddedDocuments("Item", [container._id]);
this.actor.update(cloneActor); await this.actor.update(actorUpdate);
} }
static async #onRoll(event, target) { static async #onRoll(event, target) {
@@ -576,6 +589,14 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
encumbrance: this.actor.system.states.encumbrance, encumbrance: this.actor.system.states.encumbrance,
difficulty: null, difficulty: null,
damageFormula: null, damageFormula: null,
damageAP: 0,
blastRadius: 0,
stun: false,
radiation: false,
isMelee: false,
isRanged: false,
bulky: false,
veryBulky: false,
}; };
const cardButtons = []; const cardButtons = [];
@@ -643,6 +664,21 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
if (itemObj?.system.hasOwnProperty("damage")) { if (itemObj?.system.hasOwnProperty("damage")) {
rollOptions.damageFormula = itemObj.system.damage; rollOptions.damageFormula = itemObj.system.damage;
if (itemObj.type === "weapon") {
rollOptions.isMelee = itemObj.system.range?.isMelee === true;
rollOptions.isRanged = itemObj.type === "weapon" && !itemObj.system.range?.isMelee;
rollOptions.damageAP = itemObj.system.traits?.ap ?? 0;
rollOptions.blastRadius = itemObj.system.traits?.blast ?? 0;
rollOptions.stun = itemObj.system.traits?.stun === true;
rollOptions.radiation = itemObj.system.traits?.radiation === true;
rollOptions.scope = itemObj.system.traits?.scope === true;
rollOptions.zeroG = itemObj.system.traits?.zeroG === true;
rollOptions.autoLevel = itemObj.system.traits?.auto ?? 0;
rollOptions.itemId = itemObj._id;
rollOptions.magazine = itemObj.system.magazine ?? -1; // -1 = not tracked
rollOptions.bulky = itemObj.system.traits?.bulky === true;
rollOptions.veryBulky = itemObj.system.traits?.veryBulky === true;
}
if (itemObj.type === "disease") { if (itemObj.type === "disease") {
if (itemObj.system.subType === "disease") if (itemObj.system.subType === "disease")
rollOptions.rollTypeName = game.i18n.localize("MGT2.DiseaseSubType.disease"); rollOptions.rollTypeName = game.i18n.localize("MGT2.DiseaseSubType.disease");
@@ -663,6 +699,10 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
const rollModifiers = []; const rollModifiers = [];
const rollFormulaParts = []; const rollFormulaParts = [];
// Auto trait — fire mode
const autoLevel = rollOptions.autoLevel ?? 0;
const autoMode = autoLevel > 0 ? (userRollData.autoMode ?? "single") : "single";
if (userRollData.diceModifier) { if (userRollData.diceModifier) {
rollFormulaParts.push("3d6", userRollData.diceModifier); rollFormulaParts.push("3d6", userRollData.diceModifier);
} else { } else {
@@ -707,13 +747,99 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
rollModifiers.push(game.i18n.localize("MGT2.Actor.Fatigue") + " -2"); rollModifiers.push(game.i18n.localize("MGT2.Actor.Fatigue") + " -2");
} }
if (userRollData.customDM) { const customDMVal = parseInt(userRollData.customDM ?? "0", 10);
const s = userRollData.customDM.trim(); if (!isNaN(customDMVal) && customDMVal !== 0) {
if (/^[0-9]/.test(s)) rollFormulaParts.push("+"); rollFormulaParts.push(customDMVal > 0 ? `+${customDMVal}` : `${customDMVal}`);
rollFormulaParts.push(s); rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.CustomDM") + " " + (customDMVal > 0 ? `+${customDMVal}` : `${customDMVal}`));
} }
if (MGT2Helper.hasValue(userRollData, "difficulty")) rollOptions.difficulty = userRollData.difficulty; if (rollOptions.isRanged) {
const rangedRange = parseInt(userRollData.rangedRange ?? "0", 10);
if (rangedRange === 1) {
rollFormulaParts.push("+1");
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.RangeShort") + " +1");
} else if (rangedRange === -2) {
rollFormulaParts.push("-2");
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.RangeLong") + " 2");
} else if (rangedRange === -4) {
rollFormulaParts.push("-4");
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.RangeExtreme") + " 4");
}
const rangedAim = parseInt(userRollData.rangedAim ?? "0", 10);
// Auto: burst/full-auto cancels all aiming advantages (rules p.75)
if (rangedAim > 0 && autoMode === "single") {
rollFormulaParts.push(`+${rangedAim}`);
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.Aim") + ` +${rangedAim}`);
if (userRollData.rangedLaserSight === true || userRollData.rangedLaserSight === "true") {
rollFormulaParts.push("+1");
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.LaserSight") + " +1");
}
}
const rangedFastTarget = parseInt(userRollData.rangedFastTarget ?? "0", 10);
if (rangedFastTarget < 0) {
rollFormulaParts.push(`${rangedFastTarget}`);
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.FastTarget") + ` ${rangedFastTarget}`);
}
if (userRollData.rangedCover === true || userRollData.rangedCover === "true") {
rollFormulaParts.push("-2");
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.Cover") + " 2");
}
if (userRollData.rangedProne === true || userRollData.rangedProne === "true") {
rollFormulaParts.push("-1");
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.Prone") + " 1");
}
if (userRollData.rangedDodge === true || userRollData.rangedDodge === "true") {
const dodgeDM = parseInt(userRollData.rangedDodgeDM ?? "0", 10);
if (dodgeDM < 0) {
rollFormulaParts.push(`${dodgeDM}`);
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.Dodge") + ` ${dodgeDM}`);
}
}
}
if (rollOptions.isMelee) {
if (userRollData.meleeDodge === true || userRollData.meleeDodge === "true") {
const dodgeDM = parseInt(userRollData.meleeDodgeDM ?? "0", 10);
if (dodgeDM < 0) {
rollFormulaParts.push(`${dodgeDM}`);
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.Dodge") + ` ${dodgeDM}`);
}
}
if (userRollData.meleeParry === true || userRollData.meleeParry === "true") {
const parryDM = parseInt(userRollData.meleeParryDM ?? "0", 10);
if (parryDM < 0) {
rollFormulaParts.push(`${parryDM}`);
rollModifiers.push(game.i18n.localize("MGT2.RollPrompt.Parry") + ` ${parryDM}`);
}
}
}
if (MGT2Helper.hasValue(userRollData, "difficulty") && userRollData.difficulty !== "") rollOptions.difficulty = userRollData.difficulty;
// ── Bulky / Very Bulky trait: STR penalty ────────────────────────────
const strDm = this.actor.system.characteristics.strength?.dm ?? 0;
if (rollOptions.veryBulky) {
// Very Bulky: requires STR DM ≥ +2
if (strDm < 2) {
const penalty = strDm - 2;
rollFormulaParts.push(`${penalty}`);
rollModifiers.push(game.i18n.localize("MGT2.WeaponTraits.VeryBulky") + ` ${penalty}`);
ui.notifications.warn(game.i18n.format("MGT2.Notifications.VeryBulkyPenalty", { penalty }));
}
} else if (rollOptions.bulky) {
// Bulky: requires STR DM ≥ +1
if (strDm < 1) {
const penalty = strDm - 1;
rollFormulaParts.push(`${penalty}`);
rollModifiers.push(game.i18n.localize("MGT2.WeaponTraits.Bulky") + ` ${penalty}`);
ui.notifications.warn(game.i18n.format("MGT2.Notifications.BulkyPenalty", { penalty }));
}
}
const rollFormula = rollFormulaParts.join(""); const rollFormula = rollFormulaParts.join("");
if (!Roll.validate(rollFormula)) { if (!Roll.validate(rollFormula)) {
@@ -721,43 +847,120 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
return; return;
} }
// ── Ammo decrement (ranged weapons with magazine tracking) ───────────
if (rollOptions.isRanged && rollOptions.itemId && rollOptions.magazine >= 0) {
let ammoUsed = 1;
if (autoMode === "burst" && autoLevel > 0) ammoUsed = autoLevel;
else if (autoMode === "fullAuto" && autoLevel > 0) ammoUsed = autoLevel * 3;
const currentMag = rollOptions.magazine;
if (currentMag <= 0) {
ui.notifications.warn(
game.i18n.format("MGT2.Notifications.NoAmmo", { weapon: rollOptions.rollObjectName })
);
} else {
const newMag = Math.max(0, currentMag - ammoUsed);
const weaponItem = this.actor.getEmbeddedDocument("Item", rollOptions.itemId);
if (weaponItem) await weaponItem.update({ "system.magazine": newMag });
if (newMag === 0) {
ui.notifications.warn(
game.i18n.format("MGT2.Notifications.AmmoEmpty", { weapon: rollOptions.rollObjectName })
);
} else {
ui.notifications.info(
game.i18n.format("MGT2.Notifications.AmmoUsed", { used: ammoUsed, remaining: newMag, weapon: rollOptions.rollObjectName })
);
}
}
}
let roll = await new Roll(rollFormula, this.actor.getRollData()).roll({ rollMode: userRollData.rollMode }); let roll = await new Roll(rollFormula, this.actor.getRollData()).roll({ rollMode: userRollData.rollMode });
if (isInitiative && this.token?.combatant) { if (isInitiative && this.token?.combatant) {
await this.token.combatant.update({ initiative: roll.total }); await this.token.combatant.update({ initiative: roll.total });
} }
// ── Compute effect and effective damage formula ──────────────────────
let rollSuccess = false;
let rollFailure = false;
let rollEffect = undefined;
let rollEffectStr = undefined;
let difficultyValue = null;
if (MGT2Helper.hasValue(rollOptions, "difficulty")) {
difficultyValue = MGT2Helper.getDifficultyValue(rollOptions.difficulty);
rollEffect = roll.total - difficultyValue;
rollEffectStr = (rollEffect >= 0 ? "+" : "") + rollEffect;
rollSuccess = rollEffect >= 0;
rollFailure = !rollSuccess;
}
// Build effective damage formula: base + effect + STR DM (melee) + Auto burst
let effectiveDamageFormula = rollOptions.damageFormula || null;
if (effectiveDamageFormula) {
if (rollEffect !== undefined && rollEffect !== 0) {
effectiveDamageFormula += (rollEffect >= 0 ? "+" : "") + rollEffect;
}
if (rollOptions.isMelee) {
const strDm = this.actor.system.characteristics.strength?.dm ?? 0;
if (strDm !== 0) effectiveDamageFormula += (strDm >= 0 ? "+" : "") + strDm;
}
// Burst: add Auto level to damage
if (autoMode === "burst" && autoLevel > 0) {
effectiveDamageFormula += `+${autoLevel}`;
}
}
// Auto fire mode chat info
let autoInfo = null;
if (autoMode === "burst" && autoLevel > 0) {
autoInfo = game.i18n.format("MGT2.RollPrompt.AutoBurstInfo", { level: autoLevel, ammo: autoLevel });
} else if (autoMode === "fullAuto" && autoLevel > 0) {
autoInfo = game.i18n.format("MGT2.RollPrompt.AutoFullInfo", { level: autoLevel, ammo: autoLevel * 3 });
}
// ── Build roll breakdown tooltip ─────────────────────────────────────
const diceRawTotal = roll.dice.reduce((s, d) => s + d.total, 0);
const breakdownParts = [game.i18n.localize("MGT2.Chat.Roll.Dice") + " " + diceRawTotal];
for (const mod of rollModifiers) breakdownParts.push(mod);
if (rollEffectStr !== undefined)
breakdownParts.push(game.i18n.localize("MGT2.Chat.Roll.Effect") + " " + rollEffectStr);
const rollBreakdown = breakdownParts.join(" | ");
const chatData = { const chatData = {
user: game.user.id, user: game.user.id,
speaker: this.actor ? ChatMessage.getSpeaker({ actor: this.actor }) : null, speaker: this.actor ? ChatMessage.getSpeaker({ actor: this.actor }) : null,
formula: roll._formula, formula: roll._formula,
tooltip: await roll.getTooltip(), tooltip: await roll.getTooltip(),
total: Math.round(roll.total * 100) / 100, total: Math.round(roll.total * 100) / 100,
rollBreakdown,
showButtons: true, showButtons: true,
showLifeButtons: false, showLifeButtons: false,
showRollRequest: false, showRollRequest: false,
rollTypeName: rollOptions.rollTypeName, rollTypeName: rollOptions.rollTypeName,
rollObjectName: rollOptions.rollObjectName, rollObjectName: rollOptions.rollObjectName,
rollModifiers: rollModifiers, rollModifiers: rollModifiers,
showRollDamage: rollOptions.damageFormula !== null && rollOptions.damageFormula !== "", // Show damage button only if there's a formula AND (no difficulty check OR roll succeeded)
showRollDamage: !!effectiveDamageFormula && (!difficultyValue || rollSuccess),
cardButtons: cardButtons, cardButtons: cardButtons,
autoInfo,
}; };
if (MGT2Helper.hasValue(rollOptions, "difficulty")) { if (MGT2Helper.hasValue(rollOptions, "difficulty")) {
chatData.rollDifficulty = rollOptions.difficulty; chatData.rollDifficulty = rollOptions.difficulty;
chatData.rollDifficultyLabel = MGT2Helper.getDifficultyDisplay(rollOptions.difficulty); chatData.rollDifficultyLabel = MGT2Helper.getDifficultyDisplay(rollOptions.difficulty);
if (roll.total >= MGT2Helper.getDifficultyValue(rollOptions.difficulty)) chatData.rollEffect = rollEffect;
chatData.rollSuccess = true; chatData.rollEffectStr = rollEffectStr;
else chatData.rollSuccess = rollSuccess || undefined;
chatData.rollFailure = true; chatData.rollFailure = rollFailure || undefined;
} }
const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData); const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
chatData.content = html; chatData.content = html;
let flags = null; let flags = null;
if (rollOptions.damageFormula) { if (effectiveDamageFormula) {
flags = { mgt2: { damage: { formula: rollOptions.damageFormula, rollObjectName: rollOptions.rollObjectName, rollTypeName: rollOptions.rollTypeName } } }; flags = { mgt2: { damage: { formula: effectiveDamageFormula, ap: rollOptions.damageAP ?? 0, blast: rollOptions.blastRadius ?? 0, stun: rollOptions.stun ?? false, radiation: rollOptions.radiation ?? false, rollObjectName: rollOptions.rollObjectName, rollTypeName: rollOptions.rollTypeName } } };
} }
if (cardButtons.length > 0) { if (cardButtons.length > 0) {
if (!flags) flags = { mgt2: {} }; if (!flags) flags = { mgt2: {} };
@@ -828,6 +1031,8 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
static async #onTraitDelete(event, target) { static async #onTraitDelete(event, target) {
event.preventDefault(); event.preventDefault();
const confirmed = await TravellerCharacterSheet.#confirmDelete(game.i18n.localize("MGT2.Actor.ThisTrait"));
if (!confirmed) return;
const element = target.closest("[data-traits-part]"); const element = target.closest("[data-traits-part]");
const index = Number(element.dataset.traitsPart); const index = Number(element.dataset.traitsPart);
const traits = foundry.utils.deepClone(this.actor.system.personal.traits); const traits = foundry.utils.deepClone(this.actor.system.personal.traits);
@@ -839,9 +1044,329 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
static async #onOpenEditor(event) { static async #onOpenEditor(event) {
event.preventDefault(); event.preventDefault();
await CharacterPrompts.openEditorFullView( const title = this.actor.system.personal.species
this.actor.system.personal.species, || game.i18n.localize("MGT2.Actor.Species")
this.actor.system.personal.speciesText.descriptionLong || "Species";
const html = this.actor.system.personal.speciesText?.descriptionLong ?? "";
await CharacterPrompts.openEditorFullView(title, html);
}
static async #onHeal(event, target) {
event.preventDefault();
const healType = target.dataset.healType;
if (game.user.targets.size === 0) {
ui.notifications.warn(game.i18n.localize("MGT2.Errors.NoTokenSelected"));
return;
}
if (healType === "firstaid") {
// Find Medicine skill to pre-select
// Use normalized string matching to handle accents
const medSkill = this.actor.items.find(i => {
if (i.type !== "talent" || i.system.subType !== "skill") return false;
const normalized = i.name.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
return normalized.includes("medecin") || normalized.includes("medicine");
});
// Only EDU characteristic available for First Aid
const characteristics = [
{ _id: "education", name: game.i18n.localize("MGT2.Characteristics.education.name") }
];
const rollOptions = {
rollTypeName: game.i18n.localize("MGT2.Healing.FirstAid"),
rollObjectName: this.actor.name,
characteristics: characteristics, // Only EDU
characteristic: "education", // Pre-selected
skill: medSkill?.id ?? "", // Medicine skill ID for pre-selection (must match _id in array)
skillName: medSkill?.name ?? game.i18n.localize("MGT2.Healing.NoMedicineSkill"), // Display name
skillLevel: medSkill?.system.level ?? -3, // -3 if not found
skills: medSkill ? [{ _id: medSkill.id, name: medSkill.name, level: medSkill.system.level }] : [],
difficulty: "Average", // First Aid difficulty is 8 (Average)
showHeal: true,
healType: MGT2.HealingType.FIRST_AID,
};
const userRollData = await RollPromptHelper.roll(rollOptions);
if (userRollData) {
// Build formula with all DMs — same pattern as standard skill roll
const rollFormulaParts = [];
const rollModifiers = [];
if (userRollData.diceModifier) {
rollFormulaParts.push("3d6");
rollFormulaParts.push(userRollData.diceModifier);
} else {
rollFormulaParts.push("2d6");
}
if (userRollData.characteristic) {
const c = this.actor.system.characteristics[userRollData.characteristic];
rollFormulaParts.push(MGT2Helper.getFormulaDM(c.dm));
rollModifiers.push(game.i18n.localize(`MGT2.Characteristics.${userRollData.characteristic}.name`) + MGT2Helper.getDisplayDM(c.dm));
}
if (userRollData.skill && userRollData.skill !== "") {
if (userRollData.skill === "NP") {
rollFormulaParts.push("-3");
rollModifiers.push(game.i18n.localize("MGT2.Items.NotProficient"));
} else {
const skillObj = this.actor.getEmbeddedDocument("Item", userRollData.skill);
rollFormulaParts.push(MGT2Helper.getFormulaDM(skillObj.system.level));
rollModifiers.push(skillObj.getRollDisplay());
}
}
if (userRollData.customDM && userRollData.customDM !== "") {
let s = userRollData.customDM.trim();
if (/^[0-9]/.test(s)) rollFormulaParts.push("+");
rollFormulaParts.push(s);
rollModifiers.push("DM " + s);
}
const rollFormula = rollFormulaParts.join("");
const roll = await new Roll(rollFormula, this.actor.getRollData()).roll();
// Difficulty for First Aid is Average (8)
const difficulty = 8;
const effect = roll.total - difficulty;
const isSuccess = effect >= 0;
const healing = isSuccess ? Math.max(1, effect) : 0;
const cardButtons = isSuccess
? [{ label: game.i18n.localize("MGT2.Healing.ApplyHealing"), action: "healing" }]
: [];
const chatData = {
user: game.user.id,
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
formula: roll._formula,
tooltip: await roll.getTooltip(),
total: Math.round(roll.total * 100) / 100,
rollTypeName: game.i18n.localize("MGT2.Healing.FirstAid"),
rollObjectName: this.actor.name,
rollModifiers: rollModifiers,
rollDifficulty: difficulty,
rollDifficultyLabel: MGT2Helper.getDifficultyDisplay("Average"),
rollEffectStr: isSuccess ? effect.toString() : undefined,
healingAmount: isSuccess ? healing : undefined,
rollSuccess: isSuccess || undefined,
rollFailure: !isSuccess || undefined,
showButtons: isSuccess,
hasDamage: false,
showRollDamage: false,
cardButtons: cardButtons,
};
const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
chatData.content = html;
chatData.flags = { mgt2: { healing: { amount: healing } } };
return roll.toMessage(chatData);
}
} else if (healType === "surgery") {
// Find Medicine skill to pre-select (same as first aid)
const medSkill = this.actor.items.find(i => {
if (i.type !== "talent" || i.system.subType !== "skill") return false;
const normalized = i.name.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
return normalized.includes("medecin") || normalized.includes("medicine");
});
const characteristics = [
{ _id: "education", name: game.i18n.localize("MGT2.Characteristics.education.name") }
];
const rollOptions = {
rollTypeName: game.i18n.localize("MGT2.Healing.Surgery"),
rollObjectName: this.actor.name,
characteristics: characteristics,
characteristic: "education",
skill: medSkill?.id ?? "",
skillName: medSkill?.name ?? game.i18n.localize("MGT2.Healing.NoMedicineSkill"),
skillLevel: medSkill?.system.level ?? -3,
skills: medSkill ? [{ _id: medSkill.id, name: medSkill.name, level: medSkill.system.level }] : [],
difficulty: "Average",
showHeal: true,
healType: MGT2.HealingType.SURGERY,
};
const userRollData = await RollPromptHelper.roll(rollOptions);
if (userRollData) {
// Build formula with all DMs — same pattern as standard skill roll
const rollFormulaParts = [];
const rollModifiers = [];
if (userRollData.diceModifier) {
rollFormulaParts.push("3d6");
rollFormulaParts.push(userRollData.diceModifier);
} else {
rollFormulaParts.push("2d6");
}
if (userRollData.characteristic) {
const c = this.actor.system.characteristics[userRollData.characteristic];
rollFormulaParts.push(MGT2Helper.getFormulaDM(c.dm));
rollModifiers.push(game.i18n.localize(`MGT2.Characteristics.${userRollData.characteristic}.name`) + MGT2Helper.getDisplayDM(c.dm));
}
if (userRollData.skill && userRollData.skill !== "") {
if (userRollData.skill === "NP") {
rollFormulaParts.push("-3");
rollModifiers.push(game.i18n.localize("MGT2.Items.NotProficient"));
} else {
const skillObj = this.actor.getEmbeddedDocument("Item", userRollData.skill);
rollFormulaParts.push(MGT2Helper.getFormulaDM(skillObj.system.level));
rollModifiers.push(skillObj.getRollDisplay());
}
}
if (userRollData.customDM && userRollData.customDM !== "") {
let s = userRollData.customDM.trim();
if (/^[0-9]/.test(s)) rollFormulaParts.push("+");
rollFormulaParts.push(s);
rollModifiers.push("DM " + s);
}
const rollFormula = rollFormulaParts.join("");
const roll = await new Roll(rollFormula, this.actor.getRollData()).roll();
// Difficulty for Surgery is Average (8)
const difficulty = 8;
const effect = roll.total - difficulty;
const isSuccess = effect >= 0;
// Success: heal Math.max(1, effect); Failure: patient takes 3 + |effect| damage
const healing = isSuccess ? Math.max(1, effect) : 0;
const surgeryDamage = isSuccess ? 0 : 3 + Math.abs(effect);
const cardButtons = [];
if (isSuccess) {
cardButtons.push({ label: game.i18n.localize("MGT2.Healing.ApplyHealing"), action: "healing" });
} else {
cardButtons.push({ label: game.i18n.localize("MGT2.Healing.ApplySurgeryDamage"), action: "surgeryDamage" });
}
const chatData = {
user: game.user.id,
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
formula: roll._formula,
tooltip: await roll.getTooltip(),
total: Math.round(roll.total * 100) / 100,
rollTypeName: game.i18n.localize("MGT2.Healing.Surgery"),
rollObjectName: this.actor.name,
rollModifiers: rollModifiers,
rollDifficulty: difficulty,
rollDifficultyLabel: MGT2Helper.getDifficultyDisplay("Average"),
rollEffectStr: effect.toString(),
healingAmount: isSuccess ? healing : undefined,
surgeryDamageAmount: isSuccess ? undefined : surgeryDamage,
rollSuccess: isSuccess || undefined,
rollFailure: !isSuccess || undefined,
showButtons: true,
hasDamage: false,
showRollDamage: false,
cardButtons: cardButtons,
};
const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
chatData.content = html;
chatData.flags = { mgt2: { surgery: { healing, surgeryDamage } } };
return roll.toMessage(chatData);
}
} else if (healType === "medical") {
const result = await CharacterPrompts.openHealingDays();
if (result) {
const endMD = this.actor.system.characteristics.endurance.dm;
const medSkill = this.actor.items.find(i =>
i.type === "talent" &&
i.system.subType === "skill" &&
(i.name.toLowerCase().includes("medecin") || i.name.toLowerCase().includes("medicine"))
);
const skillValue = medSkill ? medSkill.system.level : 0;
const days = result.days;
const healingPerDay = Math.max(1, 3 + endMD + skillValue);
const totalHealing = healingPerDay * days;
const rollModifiers = [
`3 (base)`,
`${endMD >= 0 ? "+" : ""}${endMD} END`,
`+${skillValue} ${medSkill?.name ?? "Médecine"}`,
`× ${days} ${game.i18n.localize("MGT2.RollPrompt.Days").toLowerCase()}`
];
const templateData = {
rollObjectName: this.actor.name,
rollTypeName: game.i18n.localize("MGT2.Healing.MedicalCare"),
rollModifiers,
formula: `${healingPerDay} ${game.i18n.localize("MGT2.Items.PerDay")}`,
tooltip: "",
total: totalHealing,
rollSuccess: true,
showButtons: true,
cardButtons: [
{ action: "healing", label: game.i18n.localize("MGT2.Healing.ApplyHealing") }
]
};
const content = await renderTemplate(
"systems/mgt2/templates/chat/roll.html",
templateData
);
await ChatMessage.create({
user: game.user.id,
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
content,
flags: { mgt2: { healing: { amount: totalHealing } } }
});
}
} else if (healType === "natural") {
const result = await CharacterPrompts.openHealingDays();
if (result) {
const endMD = this.actor.system.characteristics.endurance.dm;
let totalAmount = 0;
const rolls = [];
for (let i = 0; i < result.days; i++) {
const roll = await new Roll("1d6").evaluate();
const dayHealing = Math.max(1, roll.total + endMD);
rolls.push({ roll, dayHealing });
totalAmount += dayHealing;
}
// Build roll details
const rollDisplay = rolls.map((r, idx) =>
`<div><strong>${game.i18n.localize("MGT2.RollPrompt.Days")} ${idx + 1}:</strong> 1d6 = ${r.roll.total} + ${endMD > 0 ? "+" : ""}${endMD} = <strong>${r.dayHealing}</strong></div>`
).join("");
const chatData = {
user: game.user.id,
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
content: `<div class="mgt2-chat-roll">
<div class="mgt2-roll-header">
<span class="mgt2-roll-char-name">${this.actor.name}</span>
<div class="mgt2-roll-meta">
<span class="mgt2-roll-type">${game.i18n.localize("MGT2.Healing.NaturalHealing")}</span>
</div>
</div>
<div class="mgt2-roll-modifier">${result.days} ${game.i18n.localize("MGT2.RollPrompt.Days")}</div>
<div style="padding: 8px 0; font-size: 0.9em;">
${rollDisplay}
</div>
<div class="mgt2-effect is-success">
${game.i18n.localize("MGT2.Chat.Roll.Effect")} <span class="mgt2-effect-value">${totalAmount}</span>
</div>
</div>`,
flags: { mgt2: { healing: { amount: totalAmount } } }
};
await ChatMessage.create(chatData);
// Apply healing immediately
await this.actor.applyHealing(totalAmount);
ui.notifications.info(
game.i18n.format("MGT2.Notifications.HealingApplied",
{ name: this.actor.name, amount: totalAmount })
); );
} }
}
}
} }

View 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 {};
}
}
}

View File

@@ -73,6 +73,8 @@ export default class TravellerItemSheet extends HandlebarsApplicationMixin(found
skills.sort(MGT2Helper.compareByName); skills.sort(MGT2Helper.compareByName);
skills = [{ _id: "NP", name: game.i18n.localize("MGT2.Items.NotProficient") }].concat(skills); 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 { return {
item: item, item: item,
document: item, document: item,
@@ -91,6 +93,10 @@ export default class TravellerItemSheet extends HandlebarsApplicationMixin(found
weight: weight, weight: weight,
unitlabels: { weight: MGT2Helper.getWeightLabel() }, unitlabels: { weight: MGT2Helper.getWeightLabel() },
skills: skills, 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),
}; };
} }
@@ -126,6 +132,27 @@ export default class TravellerItemSheet extends HandlebarsApplicationMixin(found
bind(".options-delete", TravellerItemSheet.#onOptionDelete); bind(".options-delete", TravellerItemSheet.#onOptionDelete);
bind(".modifiers-create", TravellerItemSheet.#onModifierCreate); bind(".modifiers-create", TravellerItemSheet.#onModifierCreate);
bind(".modifiers-delete", TravellerItemSheet.#onModifierDelete); 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() { _activateTabGroups() {

View File

@@ -21,4 +21,15 @@ export default class TravellerVehiculeSheet extends MGT2ActorSheet {
/** @override */ /** @override */
tabGroups = { primary: "stats" } 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;
}
} }

View File

@@ -6,12 +6,45 @@ export class ChatHelper {
if (!message || !element) { if (!message || !element) {
return; 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 => { element.querySelectorAll('button[data-action="rollDamage"]').forEach(el => {
el.addEventListener('click', async event => { el.addEventListener('click', async event => {
await this._processRollDamageButtonEvent(message, 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 => { element.querySelectorAll('button[data-action="damage"]').forEach(el => {
el.addEventListener('click', async event => { el.addEventListener('click', async event => {
await this._applyChatCardDamage(message, event); await this._applyChatCardDamage(message, event);
@@ -20,11 +53,17 @@ export class ChatHelper {
element.querySelectorAll('button[data-action="healing"]').forEach(el => { element.querySelectorAll('button[data-action="healing"]').forEach(el => {
el.addEventListener('click', async event => { el.addEventListener('click', async event => {
ui.notifications.warn("healing"); await this._applyChatCardHealing(message, event);
}); });
}); });
element.querySelectorAll('button[data-index]').forEach(el => { 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 => { el.addEventListener('click', async event => {
await this._processRollButtonEvent(message, event); await this._processRollButtonEvent(message, event);
}); });
@@ -57,43 +96,143 @@ export class ChatHelper {
static async _processRollDamageButtonEvent(message, event) { static async _processRollDamageButtonEvent(message, event) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
let rollFormula = message.flags.mgt2.damage.formula; const damageFlags = message.flags?.mgt2?.damage;
if (!damageFlags?.formula) {
let roll = await new Roll(rollFormula, {}).roll(); ui.notifications.warn(game.i18n.localize("MGT2.Errors.InvalidRollFormula"));
return;
let speaker;
let selectTokens = canvas.tokens.controlled;
if (selectTokens.length > 0) {
speaker = selectTokens[0].actor;
} else {
speaker = game.user.character;
} }
let rollTypeName = message.flags.mgt2.damage.rollTypeName ? message.flags.mgt2.damage.rollTypeName + " DAMAGE" : null; 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 = { const chatData = {
user: game.user.id, user: game.user.id,
speaker: ChatMessage.getSpeaker({ actor: speaker }), speaker: message.speaker,
formula: roll._formula, formula: roll._formula,
tooltip: await roll.getTooltip(), tooltip: await roll.getTooltip(),
total: Math.round(roll.total * 100) / 100, total: Math.round(roll.total * 100) / 100,
showButtons: true, showButtons: true,
hasDamage: true, hasDamage: true,
rollTypeName: rollTypeName, rollTypeName,
rollObjectName: message.flags.mgt2.damage.rollObjectName 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); const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
chatData.content = html; 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); return roll.toMessage(chatData);
} }
static _applyChatCardDamage(message, event) { 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 roll = message.rolls[0];
return Promise.all(canvas.tokens.controlled.map(t => { const amount = Math.round(roll.total * 100) / 100;
const a = t.actor; const ap = message.flags?.mgt2?.damage?.ap ?? 0;
return a.applyDamage(roll.total); 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);
} }
} }

View File

@@ -143,7 +143,7 @@ MGT2.SpeedBands = Object.freeze({
VerySlow: "MGT2.SpeedBands.VerySlow", VerySlow: "MGT2.SpeedBands.VerySlow",
Slow: "MGT2.SpeedBands.Slow", Slow: "MGT2.SpeedBands.Slow",
Medium: "MGT2.SpeedBands.Medium", Medium: "MGT2.SpeedBands.Medium",
High: "MGT2.SpeedBands.High.", High: "MGT2.SpeedBands.High",
Fast: "MGT2.SpeedBands.Fast", Fast: "MGT2.SpeedBands.Fast",
VeryFast: "MGT2.SpeedBands.VeryFast", VeryFast: "MGT2.SpeedBands.VeryFast",
Subsonic: "MGT2.SpeedBands.Subsonic", Subsonic: "MGT2.SpeedBands.Subsonic",
@@ -155,3 +155,33 @@ MGT2.Durations = Object.freeze({
Minutes: "MGT2.Durations.Minutes", Minutes: "MGT2.Durations.Minutes",
Heures: "MGT2.Durations.Heures" 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"
});

View File

@@ -1,6 +1,7 @@
import { import {
CharacterData, CharacterData,
VehiculeData, VehiculeData,
CreatureData,
ItemData, ItemData,
EquipmentData, EquipmentData,
DiseaseData, DiseaseData,
@@ -17,7 +18,7 @@ import {
import { MGT2 } from "./config.js"; import { MGT2 } from "./config.js";
import { TravellerActor, MGT2Combatant } from "./actors/actor.js"; import { TravellerActor, MGT2Combatant } from "./actors/actor.js";
import { TravellerItem } from "./item.js"; import { TravellerItem } from "./item.js";
import { TravellerItemSheet, TravellerCharacterSheet, TravellerVehiculeSheet } from "./applications/sheets/_module.mjs"; import { TravellerItemSheet, TravellerCharacterSheet, TravellerVehiculeSheet, TravellerCreatureSheet } from "./applications/sheets/_module.mjs";
import { preloadHandlebarsTemplates } from "./templates.js"; import { preloadHandlebarsTemplates } from "./templates.js";
//import { MGT2Helper } from "./helper.js"; //import { MGT2Helper } from "./helper.js";
import {ChatHelper} from "./chatHelper.js"; import {ChatHelper} from "./chatHelper.js";
@@ -73,6 +74,10 @@ Hooks.once("init", async function () {
"characteristics.charm.value", "characteristics.charm.value",
"characteristics.psionic.value", "characteristics.psionic.value",
"characteristics.other.value"] "characteristics.other.value"]
},
creature: {
bar: ["life"],
value: ["life.value", "life.max", "speed", "armor", "psi"]
} }
}; };
@@ -91,13 +96,15 @@ Hooks.once("init", async function () {
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet); 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", 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", 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.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
foundry.documents.collections.Items.registerSheet("mgt2", TravellerItemSheet, { makeDefault: true }); foundry.documents.collections.Items.registerSheet("mgt2", TravellerItemSheet, { makeDefault: true });
Object.assign(CONFIG.Actor.dataModels, { Object.assign(CONFIG.Actor.dataModels, {
"character": CharacterData, "character": CharacterData,
"vehicule": VehiculeData "vehicule": VehiculeData,
"creature": CreatureData
}); });
Object.assign(CONFIG.Item.dataModels, { Object.assign(CONFIG.Item.dataModels, {

View File

@@ -109,18 +109,9 @@ export class MGT2Helper {
} }
static getDifficultyDisplay(difficulty) { static getDifficultyDisplay(difficulty) {
switch(difficulty) { const key = `MGT2.Difficulty.${difficulty}`;
case "Simple": return game.i18n.localize("MGT2.Difficulty.Simple") + " (2+)"; const label = game.i18n.localize(key);
case "Easy": return game.i18n.localize("MGT2.Difficulty.Easy") + " (4+)"; return label !== key ? label : null;
case "Routine": return game.i18n.localize("MGT2.Difficulty.Routine") + " (6+)";
case "Average": return game.i18n.localize("MGT2.Difficulty.Average") + " (8+)";
case "Difficult": return game.i18n.localize("MGT2.Difficulty.Difficult") + " (10+)";
case "VeryDifficult": return game.i18n.localize("MGT2.Difficulty.VeryDifficult") + " (12+)";
case "Formidable": return game.i18n.localize("MGT2.Difficulty.Formidable") + " (14+)";
case "Impossible": return game.i18n.localize("MGT2.Difficulty.Impossible") + " (16+)";
default:
return null;
}
} }
static getRangeDisplay(range) { static getRangeDisplay(range) {

View File

@@ -47,7 +47,9 @@ export default class CharacterData extends foundry.abstract.TypeDataModel {
}), }),
health: new fields.SchemaField({ health: new fields.SchemaField({
radiations: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }) 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({ study: new fields.SchemaField({
skill: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }), skill: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
@@ -61,7 +63,7 @@ export default class CharacterData extends foundry.abstract.TypeDataModel {
debt: 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 }), livingCost: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
monthlyShipPayments: 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.StringField({ required: false, blank: true, trim: true, initial: "" }) notes: new fields.HTMLField({ required: false, blank: true, trim: true })
}), }),
containerView: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }), containerView: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
containerDropIn: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }), containerDropIn: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),

View 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;
}
}
}
}
}
}

View File

@@ -1,6 +1,7 @@
// Actor DataModels // Actor DataModels
export { default as CharacterData } from "./character.mjs"; export { default as CharacterData } from "./character.mjs";
export { default as VehiculeData } from "./vehicule.mjs"; export { default as VehiculeData } from "./vehicule.mjs";
export { default as CreatureData } from "./creature.mjs";
// Item DataModels // Item DataModels
export { default as ItemData } from "./items/item.mjs"; export { default as ItemData } from "./items/item.mjs";

View File

@@ -13,7 +13,7 @@ export function createCharacteristicField(show = true, showMax = false) {
export class ItemBaseData extends foundry.abstract.TypeDataModel { export class ItemBaseData extends foundry.abstract.TypeDataModel {
static defineSchema() { static defineSchema() {
return { return {
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }), description: new fields.HTMLField({ required: false, blank: true, trim: true }),
subType: new fields.StringField({ required: false, blank: false, nullable: true }) subType: new fields.StringField({ required: false, blank: false, nullable: true })
}; };
} }

View File

@@ -10,7 +10,7 @@ export default class ItemContainerData extends ItemBaseData {
schema.weight = new fields.NumberField({ required: false, initial: 0, integer: false }); schema.weight = new fields.NumberField({ required: false, initial: 0, integer: false });
schema.weightless = new fields.BooleanField({ required: false, initial: false }); schema.weightless = new fields.BooleanField({ required: false, initial: false });
schema.locked = new fields.BooleanField({ required: false, initial: false }); // GM only schema.locked = new fields.BooleanField({ required: false, initial: false }); // GM only
schema.lockedDescription = new fields.StringField({ required: false, blank: true, trim: true, nullable: true }); schema.lockedDescription = new fields.HTMLField({ required: false, blank: true, trim: true });
return schema; return schema;
} }
} }

View File

@@ -3,7 +3,7 @@ const fields = foundry.data.fields;
export default class SpeciesData extends foundry.abstract.TypeDataModel { export default class SpeciesData extends foundry.abstract.TypeDataModel {
static defineSchema() { static defineSchema() {
return { return {
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }), description: new fields.HTMLField({ required: false, blank: true, trim: true }),
descriptionLong: new fields.HTMLField({ required: false, blank: true, trim: true }), descriptionLong: new fields.HTMLField({ required: false, blank: true, trim: true }),
traits: new fields.ArrayField( traits: new fields.ArrayField(
new fields.SchemaField({ new fields.SchemaField({

View File

@@ -13,12 +13,18 @@ export default class WeaponData extends PhysicalItemData {
schema.damage = new fields.StringField({ required: false, blank: true, trim: 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.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.magazineCost = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
schema.traits = new fields.ArrayField( schema.traits = new fields.SchemaField({
new fields.SchemaField({ ap: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
name: new fields.StringField({ required: true, blank: true, trim: true }), auto: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
description: new fields.StringField({ required: false, blank: true, trim: 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( schema.options = new fields.ArrayField(
new fields.SchemaField({ new fields.SchemaField({
name: new fields.StringField({ required: true, blank: true, trim: true }), name: new fields.StringField({ required: true, blank: true, trim: true }),
@@ -27,4 +33,21 @@ export default class WeaponData extends PhysicalItemData {
); );
return schema; 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(", ");
}
} }

View File

@@ -27,7 +27,9 @@ export default class VehiculeData extends foundry.abstract.TypeDataModel {
}), }),
skills: new fields.SchemaField({ skills: new fields.SchemaField({
autopilot: new fields.NumberField({ required: true, initial: 0, integer: true }) 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 })
}; };
} }
} }

View File

@@ -5,21 +5,49 @@ const { FormDataExtended } = foundry.applications.ux;
export class RollPromptHelper { export class RollPromptHelper {
static async roll(options) { 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', { const htmlContent = await renderTemplate('systems/mgt2/templates/roll-prompt.html', {
config: CONFIG.MGT2, config: CONFIG.MGT2,
characteristics: options.characteristics, // Character-mode fields
characteristic: options.characteristic, characteristics: options.characteristics ?? [],
skills: options.skills, characteristic: options.characteristic ?? "",
skill: options.skill, skills: options.skills ?? [],
fatigue: options.fatigue, skill: options.skill ?? "",
encumbrance: options.encumbrance, fatigue: options.fatigue ?? false,
difficulty: options.difficulty 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,
}); });
const theme = game.settings.get("mgt2", "theme");
return await DialogV2.wait({ return await DialogV2.wait({
window: { title: options.title ?? options.rollTypeName ?? game.i18n.localize("MGT2.RollPrompt.Roll") }, window: { title: options.title ?? options.rollTypeName ?? game.i18n.localize("MGT2.RollPrompt.Roll") },
classes: ["mgt2-roll-dialog"],
content: htmlContent, content: htmlContent,
rejectClose: false, rejectClose: false,
buttons: [ buttons: [

View File

@@ -19,14 +19,19 @@ export const preloadHandlebarsTemplates = async function() {
"systems/mgt2/templates/items/weapon-sheet.html", "systems/mgt2/templates/items/weapon-sheet.html",
"systems/mgt2/templates/items/parts/sheet-configuration.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.html",
"systems/mgt2/templates/items/parts/sheet-physical-item-tab.html",
"systems/mgt2/templates/roll-prompt.html", "systems/mgt2/templates/roll-prompt.html",
"systems/mgt2/templates/chat/roll.html", "systems/mgt2/templates/chat/roll.html",
//"systems/mgt2/templates/chat/roll-characteristic.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-sheet.html",
"systems/mgt2/templates/actors/actor-config-characteristic-sheet.html", "systems/mgt2/templates/actors/actor-config-characteristic-sheet.html",
"systems/mgt2/templates/actors/trait-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" "systems/mgt2/templates/editor-fullview.html"
]; ];
return loadTemplates(templatePaths); const loader = foundry.applications?.handlebars?.loadTemplates ?? loadTemplates;
return loader(templatePaths);
}; };

View File

@@ -51,6 +51,7 @@
font-weight: 600 font-weight: 600
font-style: italic font-style: italic
text-align: center text-align: center
color: var(--mgt2-color-form)
position: relative position: relative
& > a & > a
&.roll &.roll
@@ -122,7 +123,7 @@
background-color: transparent background-color: transparent
text-align: center text-align: center
font-size: 1.5rem font-size: 1.5rem
width: 2rem width: 3rem
height: 2rem height: 2rem
border: none border: none
outline: none outline: none
@@ -183,13 +184,14 @@ ul
float: left float: left
margin: 0 margin: 0
padding: 0 padding: 0
color: var(--mgt2-color-primary-light) color: var(--mgt2-color-form)
input input
display: block display: block
border: none border: none
font-weight: bold font-weight: bold
font-family: "Roboto Condensed", sans-serif font-family: "Roboto Condensed", sans-serif
background-color: #fff background-color: #fff
color: var(--mgt2-color-form)
font-size: 0.8rem font-size: 0.8rem
border: 1px solid #fff border: 1px solid #fff
&:hover &:hover
@@ -203,6 +205,7 @@ ul
font-size: 0.7rem font-size: 0.7rem
text-transform: uppercase text-transform: uppercase
text-wrap: nowrap text-wrap: nowrap
color: var(--mgt2-color-form)
i i
margin-right: 0.25rem margin-right: 0.25rem
.character-body .character-body
@@ -210,6 +213,7 @@ ul
flex-direction: row flex-direction: row
align-content: flex-start align-content: flex-start
flex-wrap: nowrap flex-wrap: nowrap
min-height: 330px
.tab .tab
width: 100% width: 100%
@@ -236,7 +240,7 @@ ul
display: flex display: flex
margin: 0 margin: 0
padding: 0 padding: 0
color: #4b4a44 color: var(--mgt2-color-form)
justify-content: space-between justify-content: space-between
align-items: center align-items: center
width: 100% width: 100%
@@ -274,3 +278,15 @@ ul
flex-shrink: 0 flex-shrink: 0
display: flex display: flex
flex-direction: row 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

View File

@@ -1,47 +1,482 @@
.chat-sidebar, // MGT2 Chat Roll Cards
.mgt2-buttons button
background: rgba(0, 0, 0, 0.1) // Light theme for ALL chat messages - matching character sheet
border: 1px solid var(--color-border-light-2) li.chat-message
border-radius: 3px background: #ffffff !important
box-shadow: 0 0 2px #FFF inset border: 1px solid #ccbbbb !important
//.chat-message box-shadow: 0 2px 8px rgba(0,0,0,0.18) !important
// &.message
// color: #0A0405
// background-color: #fff
// background-image: none
.dice-formula,
.dice-total
background-color: #fff
.mgt2-buttons
display: flex
justify-content: center
align-items: center
flex-wrap: nowrap
color: #0A0405 color: #0A0405
margin-top: 5px padding: 0 !important
button
i .message-header
font-size: 1.1rem background: #0A0405
padding: 0 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 margin: 0
.roll-info
.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 display: flex
flex-direction: column flex-direction: column
.roll-type-group align-items: center
flex-direction: row gap: 3px
flex-wrap: wrap
justify-content: space-between h4.dice-total
display: flex font-family: 'Barlow Condensed', sans-serif
.roll-type-name font-size: 2.2rem !important
font-size: 11px font-weight: 900
text-transform: uppercase color: #0A0405 !important
color: #515151 margin: 2px 0
.roll-object-name line-height: 1
font-weight: 400
font-size: 1.4rem
.roll-success
font-size: 1.2rem
font-weight: bold
text-transform: uppercase
margin-top: 1rem
text-align: center 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

View 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

View File

@@ -2,3 +2,274 @@
.dialog-button .dialog-button
color: var(--mgt2-color-primary) color: var(--mgt2-color-primary)
background-color: var(--mgt2-bgcolor-primary) !important 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

View File

@@ -50,6 +50,7 @@
font-size: 13px font-size: 13px
&.field-name &.field-name
background-color: var(--mgt2-input-bgcolor) background-color: var(--mgt2-input-bgcolor)
color: var(--mgt2-color-primary)
font-size: 2rem font-size: 2rem
border: none border: none
font-weight: 700 font-weight: 700
@@ -58,6 +59,7 @@
padding: 0 padding: 0
&.field-item-name &.field-item-name
background-color: var(--mgt2-input-bgcolor) background-color: var(--mgt2-input-bgcolor)
color: var(--mgt2-color-primary)
height: auto height: auto
font-size: 2rem font-size: 2rem
font-weight: 700 font-weight: 700

View File

@@ -1,38 +1,459 @@
// MGT2 Item Sheets
// New unified layout: type-bar header (image+name) stats-bar full-width tabs
// Outer window
.itemsheet .itemsheet
display: flex display: flex !important
flex-wrap: nowrap flex-direction: column !important
flex-direction: row height: 100% !important
.itemsheet-header background: #ffffff !important
display: flex overflow: hidden !important
background-color: var(--mgt2-bgcolor-primary)
color: red // Type bar (replaces vertical sidebar label)
padding: 0.5rem .item-type-bar
align-items: center background: #0A0405 !important
flex: 0 0 2rem 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 label
writing-mode: tb-rl color: #664444 !important
transform: rotate(-180deg) font-family: 'Barlow Condensed', sans-serif !important
font-weight: 700 font-size: 0.65rem !important
font-size: 20px font-weight: 700 !important
letter-spacing: 5px text-transform: uppercase !important
font-family: "Rubik Mono One", monospace letter-spacing: 1px !important
font-style: normal cursor: pointer !important
text-transform: uppercase
// 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 .itemsheet-maincol
flex: 0 0 130px display: none !important
padding: 0 1rem 0 0
.itemsheet-panel .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 display: flex
flex: inherit flex-wrap: wrap
padding: 1rem gap: 6px 12px
img align-items: center
&.profile-img
width: 100px .mgt2-trait-num
height: 100px display: flex
.itemsheet input, align-items: center
.itemsheet select gap: 4px
color: var(--mgt2-input-color) label
background-color: var(--mgt2-input-bgcolor) font-size: 0.85em
display: block white-space: nowrap
width: 100% input[type="number"]
font-size: 13px 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

View File

@@ -1,40 +1,111 @@
.mgt2 // ── Overflow fixes: allow sidebar nav to protrude OUTSIDE the window frame
.sheet-sidebar // All ancestors of nav.sheet-sidebar need overflow:visible so absolute-positioned
.item // nav (left: 100% of character-body) can extend to the right of the window border.
margin: 0 1rem // Layered !important beats Foundry's unlayered overflow:hidden per CSS cascade spec.
nav[data-group="sidebar"].tabs .mgt2.character, .mgt2.creature, .mgt2.vehicule
position: absolute overflow: visible !important
left: 100% > .window-content
top: 172px overflow: visible !important
display: flex .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 flex-direction: column
z-index: -1 // 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 & > .item
height: 40px
position: relative position: relative
display: flex display: flex !important
justify-content: end flex-direction: column !important
align-items: center justify-content: center !important
padding-right: 0.75rem align-items: center !important
background: var(--mgt2-bgcolor-primary) gap: 4px !important
color: var(--mgt2-color-primary) min-height: 54px
border: 1px solid transparent padding: 8px 4px
font-size: 1rem color: rgba(238,64,80,0.45)
transition: all 250ms ease border-bottom: 1px solid rgba(238,64,80,0.07)
margin-left: 0 cursor: pointer
&.active transition: background 180ms ease, color 180ms ease, box-shadow 180ms ease
text-shadow: none user-select: none
margin: 0 text-decoration: none !important
border-color: var(--mgt2-color-primary)
&::after // Left accent bar
border-left: none &::before
inset: 0.25rem 0.25rem 0.25rem 0 content: ''
&::after
content: ""
position: absolute position: absolute
inset: 0.25rem left: -3px
border: 1px solid var(--mgt2-color-primary) top: 18%
pointer-events: none 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 i
margin-left: 0.8rem 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

View File

@@ -63,7 +63,7 @@
&:not(:last-child) &:not(:last-child)
margin-right: 0.4rem margin-right: 0.4rem
i i
color: black color: var(--mgt2-color-form)
a[data-roll] a[data-roll]
margin-right: 0.5rem margin-right: 0.5rem
.heading .heading

View 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

View File

@@ -13,3 +13,5 @@
@import 'components/_tabs' @import 'components/_tabs'
@import 'components/_tab-sidebar' @import 'components/_tab-sidebar'
@import 'components/_tables' @import 'components/_tables'
@import 'components/_creature'
@import 'components/_vehicule'

View File

@@ -11,6 +11,7 @@
&.sheet &.sheet
.window-content .window-content
background: var(--mgt2-bgcolor-form) background: var(--mgt2-bgcolor-form)
color: var(--mgt2-color-form)
padding: 0 padding: 0
.nopad .nopad

2
styles/mgt2.min.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{ {
"id": "mgt2", "id": "mgt2",
"version": "0.1.4", "version": "14.0.0",
"title": "MGT2 - Mongoose Traveller (Unofficial)", "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", "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", "background": "systems/mgt2/assets/screens/rosette-nebula-ngc2239-hoo.webp",
@@ -35,9 +35,68 @@
"path": "lang/fr.json" "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": { "compatibility": {
"minimum": "11", "minimum": "13",
"verified": "12.324" "verified": "14"
}, },
"grid": { "grid": {
"distance": 1.5, "distance": 1.5,

View File

@@ -2,11 +2,13 @@
"Actor": { "Actor": {
"types": [ "types": [
"character", "character",
"vehicule" "vehicule",
"creature"
], ],
"htmlFields": ["notes"], "htmlFields": ["notes", "biography"],
"character": {}, "character": {},
"vehicule": {} "vehicule": {},
"creature": {}
}, },
"Item": { "Item": {
"types": [ "types": [

View File

@@ -1,6 +1,6 @@
<form class="{{cssClass}} flexcol" autocomplete="off"> <form class="{{cssClass}} flexcol" autocomplete="off">
<div class="form-group"> <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> </div>
<fieldset> <fieldset>
<legend>{{ localize 'MGT2.Actor.Initiative' }}</legend> <legend>{{ localize 'MGT2.Actor.Initiative' }}</legend>

View File

@@ -1,13 +1,4 @@
<div class="{{cssClass}} flexcol" style="align-content: flex-start;align-items: baseline;overflow: hidden;height: 100%;"> <div class="{{cssClass}} flexcol" 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>
<section class="character-header"> <section class="character-header">
<div class="character-header-img"> <div class="character-header-img">
<img class="profile" src="{{img}}" data-edit="img" title="{{name}}" height="130" width="100" /> <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> <label class="upcase">{{ localize 'MGT2.Actor.Rads' }}</label>
<input class="field" name="system.health.radiations" type="text" value="{{system.health.radiations}}" /> <input class="field" name="system.health.radiations" type="text" value="{{system.health.radiations}}" />
</div> </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-container">
<div class="table-row heading color-1"> <div class="table-row heading color-1">
<div class="row-item row-item-30 row-item-left upcase">{{ localize 'MGT2.Actor.Wounds' }}</div> <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|}} {{#each skills as |skill id|}}
<div class="table-row" data-item-id="{{skill._id}}" role="rowgroup"> <div class="table-row" data-item-id="{{skill._id}}" role="rowgroup">
<div class="row-item row-item-30 row-item-left flex-fix"> <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>
<div class="row-item row-item-center">{{skill.system.level}}</div> <div class="row-item row-item-center">{{skill.system.level}}</div>
<div class="row-item item-controls"> <div class="row-item item-controls">
@@ -410,7 +417,7 @@
{{#each psionics as |psionic id|}} {{#each psionics as |psionic id|}}
<div class="table-row" data-item-id="{{psionic._id}}" role="rowgroup"> <div class="table-row" data-item-id="{{psionic._id}}" role="rowgroup">
<div class="row-item row-item-left"> <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>
<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.level}}</div>
<div class="row-item row-item-10 row-item-center flex-fix">{{psionic.system.psionic.cost}}</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|}} {{#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="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"> <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>
<div class="row-item row-item-2 row-item-center">{{weapon.range}}</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-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-2 row-item-right flex-fix">{{weapon.weight}}</div>
<div class="row-item row-item-15 item-controls flex-fix"> <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-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-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> <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|}} {{#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="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"> <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>
<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.radiations}}</div>
<div class="row-item row-item-10 row-item-center">{{armor.system.protection}}</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-2 row-item-right flex-fix">{{armor.weight}}</div>
<div class="row-item row-item-15 item-controls flex-fix"> <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-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-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> <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-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-40 row-item-left">{{augment.system.improvement}}</div>
<div class="row-item row-item-15 item-controls flex-fix"> <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-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-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> <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|}} {{#each computers as |computer id|}}
<div class="table-row drop-item-list" data-item-id="{{computer._id}}" role="rowgroup"> <div class="table-row drop-item-list" data-item-id="{{computer._id}}" role="rowgroup">
<div class="row-item row-item-50 row-item-left"> <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>
<div class="row-item row-item-2 row-item-center {{overloadClass}}">{{computer.system.processingUsed}}/{{computer.system.processing}} <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}} {{#if computer.system.overload}}<a title="Overload"><i class="fa-solid fa-triangle-exclamation"></i></a>{{/if}}
</div> </div>
<div class="row-item row-item-2 row-item-right flex-fix">{{computer.weight}}</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"> <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-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-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> <a class="item-control item-delete" title="{{ localize 'MGT2.Actor.DeleteComputer' }}"><i class="fas fa-trash"></i></a>
@@ -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-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-2 row-item-right flex-fix">{{equipment.weight}}</div>
<div class="row-item row-item-15 item-controls flex-fix"> <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-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-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> <a class="item-control item-delete" title="{{ localize 'MGT2.Actor.DeleteEquipment' }}"><i class="fas fa-trash"></i></a>
@@ -697,7 +704,7 @@
</div> </div>
<div class="field-group mt-1"> <div class="field-group mt-1">
<label class="upcase">{{ localize 'MGT2.Actor.Notes' }}</label> <label class="upcase">{{ localize 'MGT2.Actor.Notes' }}</label>
<textarea name="system.finance.description" rows="3">{{system.finance.description}}</textarea> {{formInput systemFields.finance.fields.notes enriched=enrichedFinanceNotes value=system.finance.notes name="system.finance.notes" toggled=true}}
</div> </div>
</div> </div>
</div> </div>
@@ -729,20 +736,27 @@
</div> </div>
<div class="tab w100 h100" data-group="sidebar" data-tab="notes"> <div class="tab w100 h100" data-group="sidebar" data-tab="notes">
<div class="header upcase">{{ localize 'MGT2.Actor.Notes' }}</div> <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>
<div class="tab w100 h100" data-group="sidebar" data-tab="biography"> <div class="tab w100 h100" data-group="sidebar" data-tab="biography">
<div class="header upcase">{{ localize 'MGT2.Actor.Biography' }}</div> <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> </div>
{{#if showTrash}} {{#if showTrash}}
<!-- <div class="tab" data-group="inventory" data-tab="trash"> <!-- <div class="tab" data-group="inventory" data-tab="trash">
<p>À FAIRE</p> <p>À FAIRE</p>
</div> --> </div> -->
{{/if}} {{/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> </div>
<section class="actor-footer"> <section class="actor-footer">
<div><a name="config" title="Config" style="margin-right: 0.5rem;"><i class="fa-solid fa-gear"></i></a></div> <div><a name="config" title="Config" style="margin-right: 0.5rem;"><i class="fa-solid fa-gear"></i></a></div>
</section> </section>
</div>
</div> </div>

View 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>

View 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>

View File

@@ -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"> <section class="vehicule-header">
<div class="vehicule-header-img"> <div class="vehicule-header-img">
<img class="profile" src="{{img}}" data-edit="img" title="{{name}}" height="130" width="100" /> <img class="profile" src="{{img}}" data-edit="img" title="{{name}}" />
</div> </div>
<div class="vehicule-header-body"> <div class="vehicule-header-body">
<input class="field-name" name="name" type="text" value="{{name}}" /> <input class="vehicule-name" name="name" type="text" value="{{name}}" />
<ul class="character-summary"> <div class="vehicule-header-stats">
<li class="w5-10"><input name="system.personal.title" type="text" value="{{system.personal.title}}" placeholder="{{ localize 'MGT2.Actor.PlaceholderTITLE' }}" /></li> <div class="vehicule-stat-box vehicule-hull">
<li class="w2-10"><input name="system.personal.species" type="text" value="{{system.personal.species}}" placeholder="{{ localize 'MGT2.Actor.PlaceholderSPECIES' }}" /></li> <label>{{ localize 'MGT2.Vehicule.Hull' }}</label>
<li class="w1-10"><input name="system.personal.age" type="text" value="{{system.personal.age}}" placeholder="{{ localize 'MGT2.Actor.PlaceholderAGE' }}" /></li> <div class="vehicule-stat-value">
<li class="w2-10"><input name="system.personal.wup" type="text" value="{{system.personal.wup}}" placeholder="{{ localize 'MGT2.Actor.PlaceholderUCP' }}" /></li> <input type="number" name="system.life.value" value="{{system.life.value}}" />
</ul> <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> </div>
</section> </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>

View 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>

View 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>

View File

@@ -1,46 +1,103 @@
<div class="roll-info"> <div class="mgt2-chat-roll">
<div class="roll-object-name">{{rollObjectName}}</div> <div class="mgt2-roll-header">
<span class="mgt2-roll-char-name">{{rollObjectName}}</span>
{{#if rollTypeName}} {{#if rollTypeName}}
<div class="mgt2-roll-meta">
<span class="mgt2-roll-type">{{rollTypeName}}</span>
{{#if rollDifficulty}} {{#if rollDifficulty}}
<div class="roll-type-group"><div class="roll-type-name">{{rollTypeName}}</div><div class="roll-type-name">{{ localize rollDifficultyLabel }}</div></div> <span class="mgt2-roll-sep"></span>
{{else}} <span class="mgt2-roll-difficulty">{{rollDifficultyLabel}}</span>
<div class="roll-type-name">{{rollTypeName}}</div>
{{/if}} {{/if}}
</div>
{{/if}} {{/if}}
</div>
{{#if rollMessage}} {{#if rollMessage}}
<div>{{rollMessage}}</div> <div class="mgt2-roll-modifier">{{rollMessage}}</div>
{{/if}} {{/if}}
{{#if rollModifiers}} {{#if rollModifiers}}
{{#each rollModifiers as |rollModifier i| }} <div class="mgt2-roll-modifiers">
<div>{{rollModifier}}</div> {{#each rollModifiers as |mod i|}}
<span class="mgt2-roll-mod-tag">{{mod}}</span>
{{/each}} {{/each}}
</div>
{{/if}} {{/if}}
</div>
<div class="dice-roll"> <div class="dice-roll">
{{#if flavor}} {{#if flavor}}
<div class="dice-flavor">{{flavor}}</div> <div class="dice-flavor">{{flavor}}</div>
{{/if}} {{/if}}
<div class="dice-result"><div class="dice-formula">{{formula}}</div>{{{tooltip}}}<h4 class="dice-total">{{total}}</h4></div> <div class="dice-result">
</div> <div class="dice-formula">{{formula}}</div>
{{#if rollSuccess}} {{{tooltip}}}
<div class="roll-success">{{ localize 'MGT2.Chat.Roll.Success' }}</div> <h4 class="dice-total {{#if rollSuccess}}success{{else if rollFailure}}failure{{/if}}" {{#if rollBreakdown}}data-tooltip="{{rollBreakdown}}"{{/if}}>{{total}}</h4>
{{else if rollFailure}} </div>
<div class="roll-success">{{ localize 'MGT2.Chat.Roll.Failure' }}</div> </div>
{{/if}}
{{#if showButtons}} {{#if apValue}}
<div class="mgt2-buttons"> <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}}
{{#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 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 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}}
{{#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}} {{#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="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}}
{{#if showRollRequest}}
<button data-action="requestRoll" data-roll="characteristic" data-roll-characteristic="strength" title="Roll!"><i class="fa-solid fa-dice"></i></button>
{{/if}} {{/if}}
{{#if showRollDamage}} {{#if showRollDamage}}
<button data-action="rollDamage" title="Roll">{{ localize 'MGT2.Chat.Roll.Damages' }}</button> <button data-action="rollDamage">{{ localize 'MGT2.Chat.Roll.Damages' }}</button>
{{/if}} {{/if}}
{{#each cardButtons as |cardButton i| }} {{#if radiationWeapon}}
<button data-index="{{i}}" title="{{cardButton.label}}">{{cardButton.label}}</button> <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}} {{/each}}
</div>
{{/if}}
</div> </div>
{{/if}}

View File

@@ -1,35 +1,43 @@
<div class="{{cssClass}} itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="item-type-bar">
<label>{{localize 'MGT2.TYPES.Item.armor'}}</label> <span class="item-type-label"><i class="fas fa-shield-halved"></i> {{localize 'MGT2.TYPES.Item.armor'}}</span>
</div> </div>
<div class="itemsheet-panel"> <div class="item-sheet-header">
<div class="itemsheet-maincol"> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" /> <input class="item-header-name" name="name" type="text" value="{{item.name}}" />
{{> systems/mgt2/templates/items/parts/sheet-physical-item.html }}
</div> </div>
<div class="w-100">
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" /> <nav class="horizontal-tabs tabs" data-group="primary">
<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="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="tab2">{{ localize 'MGT2.Items.Details' }}</a>
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a> <a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
</nav> </nav>
<div class="tab-content-area">
<div class="tab" data-group="primary" data-tab="tab1"> <div class="tab" data-group="primary" data-tab="tab1">
<div class="field-group mt-1"> <div class="field-group">
<label>{{ localize 'MGT2.Items.Description' }}</label> <label>{{ localize 'MGT2.Items.Description' }}</label>
<textarea name="system.description" rows="6">{{system.description}}</textarea> {{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
</div> </div>
</div> </div>
<div class="tab" data-group="primary" data-tab="tab2"> <div class="tab" data-group="primary" data-tab="tab2">
<div class="field-groups mt-1"> <div class="item-details-grid">
<div class="field-group w3-10"> {{> 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> <label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />{{ localize 'MGT2.Items.Equipped' }}</label>
</div> </div>
<div class="field-group w3-10"> <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> <label class="mgt2-checkbox"><input type="checkbox" name="system.powered" data-dtype="Boolean" {{checked system.powered}} />{{ localize 'MGT2.Items.Powered' }}</label>
</div> </div>
{{#if hadContainer}} {{#if hadContainer}}
<div class="field-group w3-10"> <div class="field-row full">
<label>{{ localize 'MGT2.Items.Storage' }}</label> <label>{{ localize 'MGT2.Items.Storage' }}</label>
<select name="system.container.id"> <select name="system.container.id">
{{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}} {{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}}
@@ -37,29 +45,19 @@
</div> </div>
{{/if}} {{/if}}
</div> </div>
<div class="field-groups mt-1"> <div class="table-container">
<div class="field-group"> <div class="table-row heading">
<label>{{ localize 'MGT2.Items.Radiations' }}</label> <div class="row-item row-item-left row-item-30">{{ localize 'MGT2.Items.Options' }}</div>
<input type="number" name="system.radiations" value="{{system.radiations}}" data-dtype="Number" />
</div>
<div class="field-group">
<label>{{ localize 'MGT2.Items.Protection' }}</label>
<input type="text" name="system.protection" value="{{system.protection}}" data-dtype="String" />
</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-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 class="row-item row-item-right"><a class="options-create" data-property="options"><i class="fas fa-plus"></i></a></div>
</div> </div>
{{#each system.options as |option i| }} {{#each system.options as |option i| }}
<div class="table-row dropitem options-part" data-options-part="{{i}}" data-property="options" role="rowgroup"> <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 row-item-30"><input type="text" name="system.options.{{i}}.name" value="{{option.name}}" /></div>
<div class="row-item row-item-left"> <div class="row-item row-item-left">
<textarea name="system.options.{{i}}.description" rows="3">{{option.description}}</textarea> <textarea name="system.options.{{i}}.description" rows="2">{{option.description}}</textarea>
</div> </div>
<div class="row-item row-item-right row-item-5 flex-fix item-controls"> <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> <a class="item-control options-delete" title="Delete Option"><i class="fas fa-trash"></i></a>
</div> </div>
</div> </div>
@@ -70,5 +68,4 @@
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }} {{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
</div> </div>
</div> </div>
</div>
</div> </div>

View File

@@ -1,44 +1,50 @@
<div class="{{cssClass}} itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Career' }}</label></div> <div class="item-type-bar">
<div class="itemsheet-panel"> <span class="item-type-label"><i class="fas fa-briefcase"></i> {{ localize 'MGT2.Items.Career' }}</span>
<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>
<div class="field-group mt-1"> <div class="item-sheet-header">
<label class="upcase">{{ localize 'MGT2.Items.Rank' }}</label> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<input type="number" name="system.rank" value="{{system.rank}}" data-dtype="Number" /> <input class="item-header-name" name="name" type="text" value="{{item.name}}" data-dtype="String" />
</div> </div>
</div> <nav class="horizontal-tabs tabs" data-group="primary">
<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="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> <a class="item tab-select" data-tab="events">{{ localize 'MGT2.Items.EventsMishaps' }}</a>
</nav> </nav>
<div class="tab-content-area">
<div class="tab" data-group="primary" data-tab="tab1"> <div class="tab" data-group="primary" data-tab="tab1">
<textarea name="system.description" rows="6">{{system.description}}</textarea> {{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="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>
<div class="tab" data-group="primary" data-tab="events"> <div class="tab" data-group="primary" data-tab="events">
<div class="table-container color-2 mt-1"> <div class="table-container">
<div class="table-row heading color-2"> <div class="table-row heading">
<div class="row-item row-item-left upcase">{{ localize 'MGT2.Items.Age' }}</div> <div class="row-item row-item-left">{{ localize 'MGT2.Items.Age' }}</div>
<div class="row-item row-item-left upcase">{{ localize 'MGT2.Items.Details' }}</div> <div class="row-item row-item-left">{{ localize 'MGT2.Items.Details' }}</div>
<div class="row-item item-controls"><a class="event-create"><i class="fas fa-plus"></i></a></div> <div class="row-item row-item-right item-controls"><a class="event-create"><i class="fas fa-plus"></i></a></div>
</div> </div>
{{#each system.events as |event i| }} {{#each system.events as |event i| }}
<div class="table-row dropitem events-part" data-events-part="{{i}}" role="rowgroup"> <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"><input type="number" name="system.events.{{i}}.age" value="{{event.age}}" data-dtype="Number" /></div>
<div class="row-item row-item-left"> <div class="row-item row-item-left">
<textarea name="system.events.{{i}}.description" rows="3">{{event.description}}</textarea> <textarea name="system.events.{{i}}.description" rows="2">{{event.description}}</textarea>
</div> </div>
<div class="row-item item-controls"> <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> <a class="item-control event-delete" title="Delete Event"><i class="fas fa-trash"></i></a>
</div> </div>
</div> </div>
@@ -46,5 +52,4 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>

View File

@@ -1,32 +1,36 @@
<div class="{{cssClass}} itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="item-type-bar">
<label>{{localize 'MGT2.EquipmentSubType.computer'}}</label> <span class="item-type-label"><i class="fas fa-desktop"></i> {{localize 'MGT2.EquipmentSubType.computer'}}</span>
</div> </div>
<div class="itemsheet-panel"> <div class="item-sheet-header">
<div class="itemsheet-maincol"> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" /> <input class="item-header-name" name="name" type="text" value="{{item.name}}" />
{{> systems/mgt2/templates/items/parts/sheet-physical-item.html }}
</div> </div>
<div class="w-100">
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" /> <nav class="horizontal-tabs tabs" data-group="primary">
<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="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="tab2">{{ localize 'MGT2.Items.Details' }}</a>
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a> <a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
</nav> </nav>
<div class="tab-content-area">
<div class="tab" data-group="primary" data-tab="tab1"> <div class="tab" data-group="primary" data-tab="tab1">
<div class="field-group mt-1"> <div class="field-group">
<label>{{ localize 'MGT2.Items.Description' }}</label> <label>{{ localize 'MGT2.Items.Description' }}</label>
<textarea name="system.description" rows="6">{{system.description}}</textarea> {{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
</div> </div>
</div> </div>
<div class="tab" data-group="primary" data-tab="tab2"> <div class="tab" data-group="primary" data-tab="tab2">
<div class="field-groups mt-1"> <div class="item-details-grid">
<div class="field-group"> {{> 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" 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> <label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />{{ localize 'MGT2.Items.Equipped' }}</label>
</div> </div>
{{#if hadContainer}} {{#if hadContainer}}
<div class="field-group"> <div class="field-row full">
<label>{{ localize 'MGT2.Items.Storage' }}</label> <label>{{ localize 'MGT2.Items.Storage' }}</label>
<select name="system.container.id"> <select name="system.container.id">
{{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}} {{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}}
@@ -34,21 +38,17 @@
</div> </div>
{{/if}} {{/if}}
</div> </div>
<div class="field-group mt-1"> <div class="table-container" style="margin-top:8px;">
<label>{{ localize 'MGT2.Items.Processing' }}</label> <div class="table-row heading">
<input type="number" name="system.processing" value="{{system.processing}}" data-dtype="Number" /> <div class="row-item row-item-left">{{ localize 'MGT2.Items.Options' }}</div>
</div> <div class="row-item row-item-left">{{ localize 'MGT2.Items.Description' }}</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 class="row-item row-item-right"><a class="options-create" data-property="options"><i class="fas fa-plus"></i></a></div>
</div> </div>
{{#each system.options as |option i| }} {{#each system.options as |option i| }}
<div class="table-row dropitem options-part" data-options-part="{{i}}" data-property="options" role="rowgroup"> <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"><input type="text" name="system.options.{{i}}.name" value="{{option.name}}" /></div>
<div class="row-item row-item-left flex-grow-3"> <div class="row-item row-item-left">
<textarea name="system.options.{{i}}.description" rows="3">{{option.description}}</textarea> <textarea name="system.options.{{i}}.description" rows="2">{{option.description}}</textarea>
</div> </div>
<div class="row-item row-item-right item-controls"> <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> <a class="item-control options-delete" title="Delete Option"><i class="fas fa-trash"></i></a>
@@ -61,5 +61,4 @@
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }} {{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
</div> </div>
</div> </div>
</div>
</div> </div>

View File

@@ -1,84 +1,80 @@
<div class="{{cssClass}} itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Contact' }}</label></div> <div class="item-type-bar">
<div class="itemsheet-panel"> <span class="item-type-label"><i class="fas fa-address-card"></i> {{ localize 'MGT2.Items.Contact' }}</span>
<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>
<div class="field-group mt-1"> <div class="item-sheet-header">
<label>{{ localize 'MGT2.Items.Attitude' }}</label> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<select name="system.attitude"> <input class="item-header-name" name="name" type="text" value="{{item.name}}" />
{{selectOptions config.Attitudes selected = system.attitude localize = true}}
</select>
</div> </div>
<div class="field-group mt-1"> <nav class="horizontal-tabs tabs" data-group="primary">
<label>{{ localize 'MGT2.Items.Status' }}</label>
<select name="system.status">
{{selectOptions config.ContactStatus selected = system.status localize = true}}
</select>
</div>
</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="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="description">{{ localize 'MGT2.Items.Description' }}</a>
<a class="item tab-select" data-tab="notes">{{ localize 'MGT2.Items.Notes' }}</a> <a class="item tab-select" data-tab="notes">{{ localize 'MGT2.Items.Notes' }}</a>
</nav> </nav>
<div class="tab-content-area">
<div class="tab" data-group="primary" data-tab="tab1"> <div class="tab" data-group="primary" data-tab="tab1">
<div class="field-group mt-1"> <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="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> <label>{{ localize 'MGT2.Items.Title' }}</label>
<input type="text" name="system.title" value="{{system.title}}" data-dtype="String" /> <input type="text" name="system.title" value="{{system.title}}" data-dtype="String" />
</div> </div>
<div class="field-group mt-1"> <div class="field-row">
<label>{{ localize 'MGT2.Items.Nickname' }}</label> <label>{{ localize 'MGT2.Items.Nickname' }}</label>
<input type="text" name="system.nickname" value="{{system.nickname}}" data-dtype="String" /> <input type="text" name="system.nickname" value="{{system.nickname}}" data-dtype="String" />
</div> </div>
<div class="field-groups mt-1"> <div class="field-row">
<div class="field-group flex-1">
<label>{{ localize 'MGT2.Items.Homeworld' }}</label> <label>{{ localize 'MGT2.Items.Homeworld' }}</label>
<input type="text" name="system.homeworld" value="{{system.homeworld}}" data-dtype="String" /> <input type="text" name="system.homeworld" value="{{system.homeworld}}" data-dtype="String" />
</div> </div>
<div class="field-group flex-1"> <div class="field-row">
<label>{{ localize 'MGT2.Items.Location' }}</label> <label>{{ localize 'MGT2.Items.Location' }}</label>
<input type="text" name="system.location" value="{{system.location}}" data-dtype="String" /> <input type="text" name="system.location" value="{{system.location}}" data-dtype="String" />
</div> </div>
</div> <div class="field-row full">
<div class="field-group mt-1">
<label>{{ localize 'MGT2.Items.Occupation' }}</label> <label>{{ localize 'MGT2.Items.Occupation' }}</label>
<input type="text" name="system.occupation" value="{{system.occupation}}" data-dtype="String" /> <input type="text" name="system.occupation" value="{{system.occupation}}" data-dtype="String" />
</div> </div>
</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>
<div class="tab w100 h100" data-group="primary" data-tab="notes"> <div class="tab" data-group="primary" data-tab="description">
{{editor system.notes target="system.notes" button=true editable=true}} {{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
</div> </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> </div>
</div> </div>

View File

@@ -1,55 +1,59 @@
<div class="{{cssClass}} itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Container' }}</label></div> <div class="item-type-bar">
<div class="itemsheet-panel"> <span class="item-type-label"><i class="fas fa-box"></i> {{ localize 'MGT2.Items.Container' }}</span>
<div class="itemsheet-maincol"> {{#if system.locked}}<span class="item-type-label" style="margin-left:8px;"><i class="fa-solid fa-lock"></i></span>{{/if}}
<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> </div>
{{/if}} <div class="item-sheet-header">
<div class="field-group mt-05"> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<label>{{ localize 'MGT2.Items.Weight' }} ({{unitlabels.weight}})</label> <input class="item-header-name" name="name" type="text" value="{{item.name}}" data-dtype="String" />
<input type="number" value="{{weight}}" readonly />
</div> </div>
</div> <nav class="horizontal-tabs tabs" data-group="primary">
<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="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="tab2">{{ localize 'MGT2.Items.Details' }}</a>
{{#if isGM}}
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a> <a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
{{/if}}
</nav> </nav>
<div class="tab-content-area">
<div class="tab" data-group="primary" data-tab="tab1"> <div class="tab" data-group="primary" data-tab="tab1">
<div class="field-group mt-1"> <div class="field-group">
<label>{{ localize 'MGT2.Items.Description' }}</label> <label>{{ localize 'MGT2.Items.Description' }}</label>
<textarea name="system.description" rows="6">{{system.description}}</textarea> {{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
</div> </div>
</div> </div>
<div class="tab" data-group="primary" data-tab="tab2"> <div class="tab" data-group="primary" data-tab="tab2">
<div class="field-groups mt-05"> <div class="item-details-grid">
<div class="field-group flex-1"> <div class="field-row">
<label class="mgt2-checkbox"><input type="checkbox" name="system.onHand" data-dtype="Boolean" {{checked system.onHand}} />{{ localize 'MGT2.Items.OnHand' }}</label> <label>{{ localize 'MGT2.Items.Weight' }} ({{unitlabels.weight}})</label>
<input type="number" value="{{weight}}" readonly style="opacity:0.7;" class="short" />
</div> </div>
{{#if isGM}} <div class="field-row">
<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">
<label>{{ localize 'MGT2.Items.Location' }}</label> <label>{{ localize 'MGT2.Items.Location' }}</label>
<input type="text" name="system.location" value="{{system.location}}" data-dtype="String" /> <input type="text" name="system.location" value="{{system.location}}" data-dtype="String" />
</div> </div>
<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> </div>
<div class="tab" data-group="primary" data-tab="tab3">
{{#if isGM}} {{#if isGM}}
<div class="field-group mt-05"> <div class="field-row field-row--check">
<label>{{ localize 'MGT2.Items.LockedDescription' }}</label> <label class="mgt2-checkbox">
<textarea name="system.lockedDescription" rows="6">{{system.lockedDescription}}</textarea> <input type="checkbox" name="system.locked" id="locked-{{item.id}}" data-dtype="Boolean" {{checked system.locked}} />
{{ localize 'MGT2.Items.Locked' }}
</label>
</div> </div>
{{/if}} {{/if}}
</div> </div>
</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> </div>
</div> </div>

View File

@@ -1,45 +1,51 @@
<div class="{{cssClass}} itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="item-type-bar">
{{#if (eq system.subType "disease")}} {{#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")}} {{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}} {{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}} {{/if}}
</div> </div>
<div class="itemsheet-panel"> <div class="item-sheet-header">
<div class="itemsheet-maincol"> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" /> <input class="item-header-name" name="name" type="text" value="{{item.name}}" />
<div class="field-group mt-1"> </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="tab" data-group="primary" data-tab="tab2">
<div class="item-details-grid">
<div class="field-row">
<label>{{ localize 'MGT2.Items.Type' }}</label> <label>{{ localize 'MGT2.Items.Type' }}</label>
<select name="system.subType"> <select name="system.subType">
{{selectOptions config.DiseaseSubType selected = system.subType localize = true}} {{selectOptions config.DiseaseSubType selected=system.subType localize=true}}
</select> </select>
</div> </div>
</div> <div class="field-row">
<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> <label>{{ localize 'MGT2.Items.Difficulty' }}</label>
<select name="system.difficulty"> <select name="system.difficulty">
{{selectOptions config.Difficulty selected = system.difficulty localize = true}} {{selectOptions config.Difficulty selected=system.difficulty localize=true}}
</select> </select>
</div> </div>
<div class="field-group w3-10" style="margin: 0 1rem;"> <div class="field-row">
<label>{{ localize 'MGT2.Items.Damage' }}</label> <label>{{ localize 'MGT2.Items.Damage' }}</label>
<input type="text" name="system.damage" value="{{system.damage}}" data-dtype="String" /> <input type="text" name="system.damage" value="{{system.damage}}" data-dtype="String" />
</div> </div>
<div class="field-group w3-10"> <div class="field-row">
<label>{{ localize 'MGT2.Items.Interval' }}</label> <label>{{ localize 'MGT2.Items.Interval' }}</label>
<input type="text" name="system.interval" value="{{system.interval}}" data-dtype="String" /> <input type="text" name="system.interval" value="{{system.interval}}" data-dtype="String" />
</div> </div>
</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>
</div> </div>
</div> </div>

View File

@@ -1,46 +1,46 @@
<div class="{{cssClass}} itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="item-type-bar">
{{#if (eq system.subType "augment")}} {{#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")}} {{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")}} {{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}} {{else}}
<label>{{localize 'MGT2.EquipmentSubType.equipment'}}</label> <span class="item-type-label"><i class="fas fa-toolbox"></i> {{localize 'MGT2.EquipmentSubType.equipment'}}</span>
{{/if}} {{/if}}
</div> </div>
<div class="itemsheet-panel"> <div class="item-sheet-header">
<div class="itemsheet-maincol"> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" /> <input class="item-header-name" name="name" type="text" value="{{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> </div>
{{> systems/mgt2/templates/items/parts/sheet-physical-item.html }}
</div> <nav class="horizontal-tabs tabs" data-group="primary">
<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="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="tab2">{{ localize 'MGT2.Items.Details' }}</a>
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a> <a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
</nav> </nav>
<div class="tab-content-area">
<div class="tab" data-group="primary" data-tab="tab1"> <div class="tab" data-group="primary" data-tab="tab1">
<div class="field-group mt-1"> <div class="field-group">
<label>{{ localize 'MGT2.Items.Description' }}</label> <label>{{ localize 'MGT2.Items.Description' }}</label>
<textarea name="system.description" rows="6">{{system.description}}</textarea> {{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
</div> </div>
</div> </div>
<div class="tab" data-group="primary" data-tab="tab2"> <div class="tab" data-group="primary" data-tab="tab2">
<div class="field-groups mt-1"> <div class="item-details-grid">
<div class="field-group"> <div class="field-row">
<label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />Equipped</label> <label>{{ localize 'MGT2.Items.Type' }}</label>
<select name="system.subType">
{{selectOptions config.EquipmentSubType selected=system.subType localize=true}}
</select>
</div>
{{> 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> </div>
{{#if hadContainer}} {{#if hadContainer}}
<div class="field-group"> <div class="field-row full">
<label>{{ localize 'MGT2.Items.Storage' }}</label> <label>{{ localize 'MGT2.Items.Storage' }}</label>
<select name="system.container.id"> <select name="system.container.id">
{{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}} {{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}}
@@ -53,5 +53,4 @@
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }} {{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
</div> </div>
</div> </div>
</div>
</div> </div>

View File

@@ -1,58 +1,57 @@
<div class="{{cssClass}} itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="item-type-bar">
{{#if (eq system.subType "loot")}} {{#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}} {{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}} {{/if}}
</div> </div>
<div class="itemsheet-panel"> <div class="item-sheet-header">
<div class="itemsheet-maincol"> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" /> <input class="item-header-name" name="name" type="text" value="{{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> </div>
{{> systems/mgt2/templates/items/parts/sheet-physical-item.html }}
</div> <nav class="horizontal-tabs tabs" data-group="primary">
<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="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="tab2">{{ localize 'MGT2.Items.Details' }}</a>
</nav> </nav>
<div class="tab-content-area">
<div class="tab" data-group="primary" data-tab="tab1"> <div class="tab" data-group="primary" data-tab="tab1">
<div class="field-group mt-1"> <div class="field-group">
<label>{{ localize 'MGT2.Items.Description' }}</label> <label>{{ localize 'MGT2.Items.Description' }}</label>
<textarea name="system.description" rows="6">{{system.description}}</textarea> {{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
</div> </div>
</div> </div>
<div class="tab" data-group="primary" data-tab="tab2"> <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>
{{> systems/mgt2/templates/items/parts/sheet-physical-item-tab.html }}
{{#if hadContainer}} {{#if hadContainer}}
<div class="field-groups mt-1"> <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")}} {{#if (eq system.subType "software")}}
<div class="field-group"> <div class="field-row">
<label>{{ localize 'MGT2.Items.Bandwidth' }}</label>
<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> <label>{{ localize 'MGT2.Items.Computer' }}</label>
<select name="system.software.computerId"> <select name="system.software.computerId">
{{selectOptions computers selected=system.software.computerId valueAttr="_id" labelAttr="name"}} {{selectOptions computers selected=system.software.computerId valueAttr="_id" labelAttr="name"}}
</select> </select>
</div> </div>
{{/if}} {{/if}}
<div class="field-group">
<label>{{ localize 'MGT2.Items.Storage' }}</label>
<select name="system.container.id">
{{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}}
</select>
</div>
</div>
{{/if}}
{{#if (eq system.subType "software")}}
<div class="field-group mt-1">
<label>{{ localize 'MGT2.Items.Bandwidth' }}</label>
<input type="number" name="system.software.bandwidth" value="{{system.software.bandwidth}}" data-dtype="Number" />
</div>
{{/if}} {{/if}}
</div> </div>
</div> </div>

View 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>

View File

@@ -1,21 +1,22 @@
<div class="field-group mt-05"> <div class="item-stat-pill">
<label>{{ localize 'MGT2.Items.Quantity' }}</label> <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" /> <input type="number" name="system.quantity" value="{{system.quantity}}" data-dtype="Number" integer="true" positive="true" />
</div> </div>
<div class="field-group mt-05"> <div class="item-stat-pill">
<label>{{ localize 'MGT2.Items.Weight' }} ({{unitlabels.weight}})</label> <span class="stat-label">{{ localize 'MGT2.Items.Weight' }} ({{unitlabels.weight}})</span>
<input type="number" name="weight" value="{{weight}}" data-dtype="Number" step="0.5" /> <input type="number" name="weight" value="{{weight}}" data-dtype="Number" step="0.5" />
</div> </div>
<div class="field-group mt-05"> <div class="item-stat-pill-checkbox">
<label class="mgt2-checkbox"><input type="checkbox" name="system.weightless" data-dtype="Boolean" {{checked system.weightless}} />{{ localize 'MGT2.Items.Weightless' }}</label> <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>
<div class="field-group mt-05"> <div class="item-stat-pill">
<label>{{ localize 'MGT2.Items.Cost' }}</label> <span class="stat-label">{{ localize 'MGT2.Items.Cost' }}</span>
<input type="number" name="system.cost" value="{{system.cost}}" data-dtype="Number" step="1" /> <input type="number" name="system.cost" value="{{system.cost}}" data-dtype="Number" step="1" />
</div> </div>
<div class="field-group mt-05"> <div class="item-stat-pill">
<label>{{ localize 'MGT2.Items.TL' }}</label> <span class="stat-label">{{ localize 'MGT2.Items.TL' }}</span>
<select name="system.tl"> <select name="system.tl">
{{selectOptions config.TL selected = system.tl localize = true}} {{selectOptions config.TL selected = system.tl localize = true}}
</select> </select>
</div> </div>

View File

@@ -1,63 +1,62 @@
<div class="{{cssClass}} itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"><label>{{ localize 'MGT2.Specie' }}</label></div> <div class="item-type-bar">
<div class="itemsheet-panel"> <span class="item-type-label"><i class="fas fa-dna"></i> {{ localize 'MGT2.Specie' }}</span>
<div class="itemsheet-maincol">
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
</div> </div>
<div class="w-100" style="display: flex;flex-direction: column;justify-content: flex-start;align-items: stretch;"> <div class="item-sheet-header">
<input class="field item-name" name="name" type="text" value="{{item.name}}" /> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<div class="field-group mt-1"> <input class="item-header-name" name="name" type="text" value="{{item.name}}" />
<label>{{ localize 'MGT2.Items.Description' }}</label> </div>
<nav class="horizontal-tabs tabs mt-1" data-group="primary"> <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="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="tab2">{{ localize 'MGT2.Items.DetailedDescription' }}</a>
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Details' }}</a> <a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Details' }}</a>
</nav> </nav>
<div class="tab-content-area">
<div class="tab" data-group="primary" data-tab="tab1"> <div class="tab" data-group="primary" data-tab="tab1">
<div class="field-group mt-1"> <div class="field-group">
<label>{{ localize 'MGT2.Items.Description' }}</label> <label>{{ localize 'MGT2.Items.Description' }}</label>
<textarea name="system.description" rows="6">{{system.description}}</textarea> {{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
</div> </div>
</div> </div>
<div class="tab w100 h100" data-group="primary" data-tab="tab2"> <div class="tab" data-group="primary" data-tab="tab2">
{{editor system.descriptionLong target="system.descriptionLong" button=true editable=true}} {{formInput systemFields.descriptionLong enriched=enrichedDescriptionLong value=system.descriptionLong name="system.descriptionLong" toggled=true}}
</div> </div>
<div class="tab" data-group="primary" data-tab="tab3"> <div class="tab" data-group="primary" data-tab="tab3">
<div class="table-container"> <div class="table-container">
<div class="table-row heading color-2"> <div class="table-row heading">
<div class="row-item row-item-left upcase">{{ localize 'MGT2.Items.Characteristics' }}</div> <div class="row-item row-item-left">{{ 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-left" style="flex:0 0 120px;">{{ 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 class="row-item row-item-right item-controls"><a class="modifiers-create"><i class="fas fa-plus"></i></a></div>
</div> </div>
{{#each system.modifiers as |modifier i| }} {{#each system.modifiers as |modifier i| }}
<div class="table-row modifiers-part" data-modifiers-part="{{i}}" role="rowgroup"> <div class="table-row modifiers-part" data-modifiers-part="{{i}}" role="rowgroup">
<div class="row-item row-item-left"> <div class="row-item row-item-left">
<select name="system.modifiers.{{i}}.characteristic"> <select name="system.modifiers.{{i}}.characteristic">
{{selectOptions ../config.Characteristics selected=modifier.characteristic localize = true}} {{selectOptions ../config.Characteristics selected=modifier.characteristic localize=true}}
</select> </select>
</div> </div>
<div class="row-item row-item-left row-item-15 flex-fix"> <div class="row-item row-item-left" style="flex:0 0 120px;">
<input type="number" name="system.modifiers.{{i}}.value" value="{{modifier.value}}" /> <input type="number" name="system.modifiers.{{i}}.value" value="{{modifier.value}}" />
</div> </div>
<div class="row-item row-item-15 item-controls flex-fix"> <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> <a class="item-control modifiers-delete" title="Delete Trait"><i class="fas fa-trash"></i></a>
</div> </div>
</div> </div>
{{/each}} {{/each}}
</div> </div>
<div class="table-container mt-1"> <div class="table-container" style="margin-top:8px;">
<div class="table-row heading color-2"> <div class="table-row heading">
<div class="row-item row-item-30 row-item-left upcase">{{ localize 'MGT2.Items.Traits' }}</div> <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-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 class="row-item row-item-right item-controls"><a class="options-create" data-property="traits"><i class="fas fa-plus"></i></a></div>
</div> </div>
{{#each system.traits as |trait i| }} {{#each system.traits as |trait i| }}
<div class="table-row options-part" data-options-part="{{i}}" data-property="traits" role="rowgroup"> <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" style="flex:0 0 30%;"><input type="text" name="system.traits.{{i}}.name" value="{{trait.name}}" /></div>
<div class="row-item row-item-left"> <div class="row-item row-item-left">
<textarea name="system.traits.{{i}}.description" rows="3">{{trait.description}}</textarea> <textarea name="system.traits.{{i}}.description" rows="2">{{trait.description}}</textarea>
</div> </div>
<div class="row-item row-item-15 item-controls flex-fix"> <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> <a class="item-control options-delete" title="Delete Trait"><i class="fas fa-trash"></i></a>
</div> </div>
</div> </div>
@@ -65,6 +64,4 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div> </div>

View File

@@ -1,79 +1,84 @@
<div class="{{cssClass}} flexrow itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="item-type-bar">
{{#if (eq system.subType "skill")}} {{#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")}} {{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}} {{/if}}
</div> </div>
<div class="itemsheet-panel"> <div class="item-sheet-header">
<div class="itemsheet-maincol"> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<img class="profile-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" /> <input class="item-header-name" name="name" type="text" value="{{item.name}}" />
<div class="field-group mt-1"> </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>
{{!-- 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> <label>{{ localize 'MGT2.Items.Type' }}</label>
<select name="system.subType"> <select name="system.subType">
{{selectOptions config.TalentSubType selected = system.subType localize = true}} {{selectOptions config.TalentSubType selected=system.subType localize=true}}
</select> </select>
</div> </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> </div>
<div class="w-100">
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" />
{{#if (eq system.subType "skill")}} {{#if (eq system.subType "skill")}}
<div class="field-groups mt-1"> <div class="field-row full">
<div class="field-group">
<label>{{ localize 'MGT2.Items.Speciality' }}</label> <label>{{ localize 'MGT2.Items.Speciality' }}</label>
<input type="text" name="system.skill.speciality" value="{{system.skill.speciality}}" data-dtype="String" /> <input type="text" name="system.skill.speciality" value="{{system.skill.speciality}}" data-dtype="String" />
</div> </div>
<div class="field-group"> <div class="field-row field-row--check">
<label>{{ localize 'MGT2.Items.Level' }}</label> <label class="mgt2-checkbox">
<input type="text" name="system.level" value="{{system.level}}" data-dtype="Number" /> <input type="checkbox" name="system.skill.reduceEncumbrance" data-dtype="Boolean" {{checked system.skill.reduceEncumbrance}} />
</div> {{ localize 'MGT2.Items.ReduceEncumbrance' }}
</label>
</div> </div>
{{else if (eq system.subType "psionic")}} {{else if (eq system.subType "psionic")}}
<div class="field-groups mt-1"> <div class="field-row">
<div class="field-group"> <label>{{ localize 'MGT2.Items.PSICost' }}</label>
<label>{{ localize 'MGT2.Items.Level' }}</label> <input type="number" name="system.psionic.cost" value="{{system.psionic.cost}}" data-dtype="Number" class="short" />
<input type="text" name="system.level" value="{{system.level}}" data-dtype="Number" />
</div> </div>
<div class="field-group"> <div class="field-row">
<label>{{ localize 'MGT2.Items.Reach' }}</label> <label>{{ localize 'MGT2.Items.Reach' }}</label>
<select name="system.psionic.reach"> <select name="system.psionic.reach">
<option></option> <option value=""></option>
{{selectOptions config.PsionicReach selected = system.psionic.reach localize = true}} {{selectOptions config.PsionicReach selected=system.psionic.reach localize=true}}
</select> </select>
</div> </div>
<div class="field-group"> <div class="field-row full">
<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> <label>{{ localize 'MGT2.Items.Duration' }}</label>
<div class="flexrow"> <div class="range-inputs">
<input type="text" name="system.psionic.duration" value="{{system.psionic.duration}}" data-dtype="String" class="flex-basis-50" /> <input type="text" name="system.psionic.duration" value="{{system.psionic.duration}}" data-dtype="String" />
<select name="system.psionic.durationUnit" class="flex-basis-50 flex-fix"> <select name="system.psionic.durationUnit">
{{selectOptions config.Durations selected=system.psionic.durationUnit localize = true}} {{selectOptions config.Durations selected=system.psionic.durationUnit localize=true}}
</select> </select>
</div> </div>
</div> </div>
</div>
{{/if}} {{/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>
<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> </div>
{{/if}}
{{!-- Tab 3 : Configuration (jet de dé) --}}
<div class="tab" data-group="primary" data-tab="tab3">
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }} {{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
</div> </div>
</div>
</div> </div>
</div> </div>

View File

@@ -1,30 +1,76 @@
<div class="{{cssClass}} itemsheet"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Weapon' }}</label></div> <div class="item-type-bar">
<div class="itemsheet-panel"> <span class="item-type-label"><i class="fas fa-gun"></i> {{ localize 'MGT2.Items.Weapon' }}</span>
<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>
<div class="w-100"> <div class="item-sheet-header">
<input class="field field-item-name" name="name" type="text" value="{{item.name}}" /> <img class="item-header-img" src="{{item.img}}" data-edit="img" data-tooltip="{{item.name}}" />
<nav class="horizontal-tabs tabs mt-1" data-group="primary"> <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="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="tab2">{{ localize 'MGT2.Items.Details' }}</a>
<a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a> <a class="item tab-select" data-tab="tab3">{{ localize 'MGT2.Items.Configuration' }}</a>
</nav> </nav>
<div class="tab-content-area">
<div class="tab" data-group="primary" data-tab="tab1"> <div class="tab" data-group="primary" data-tab="tab1">
<div class="field-group mt-1"> <div class="field-group">
<label>{{ localize 'MGT2.Items.Description' }}</label> <label>{{ localize 'MGT2.Items.Description' }}</label>
<textarea name="system.description" rows="6">{{system.description}}</textarea> {{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" toggled=true}}
</div> </div>
</div> </div>
<div class="tab" data-group="primary" data-tab="tab2"> <div class="tab" data-group="primary" data-tab="tab2">
<div class="field-groups mt-1"> <div class="item-details-grid">
<div class="field-group flex-1"> <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.TL' }}</label>
<select name="system.tl">{{selectOptions config.TL selected=system.tl localize=true}}</select>
</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">
<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">
{{selectOptions config.MetricRange selected=system.range.unit localize=true}}
</select>
</div>
</div>
{{/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> <label class="mgt2-checkbox"><input type="checkbox" name="system.equipped" data-dtype="Boolean" {{checked system.equipped}} />{{ localize 'MGT2.Items.Equipped' }}</label>
</div> </div>
{{#if hadContainer}} {{#if hadContainer}}
<div class="field-group"> <div class="field-row full">
<label>{{ localize 'MGT2.Items.Storage' }}</label> <label>{{ localize 'MGT2.Items.Storage' }}</label>
<select name="system.container.id"> <select name="system.container.id">
{{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}} {{selectOptions containers selected=system.container.id valueAttr="_id" labelAttr="name"}}
@@ -32,58 +78,47 @@
</div> </div>
{{/if}} {{/if}}
</div> </div>
<div class="field-groups mt-1"> <fieldset class="mgt2-weapon-traits">
<div class="field-group w3-10"> <legend>{{ localize 'MGT2.WeaponTraits.SectionTitle' }}</legend>
<label class="mgt2-checkbox"><input type="checkbox" name="system.range.isMelee" data-dtype="Boolean" {{checked system.range.isMelee}} />{{ localize 'MGT2.Items.IsMelee' }}</label> <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>
{{#unless system.range.isMelee}} <div class="mgt2-trait-num" data-tooltip="{{ localize 'MGT2.WeaponTraits.AutoHint' }}">
<div class="field-group w3-10"> <label>{{ localize 'MGT2.WeaponTraits.Auto' }}</label>
<label>{{ localize 'MGT2.Items.Range' }}</label> <input type="number" name="system.traits.auto" value="{{system.traits.auto}}" data-dtype="Number" min="0" class="short" />
<div class="flexrow">
<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>
</div> </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> </div>
{{/unless}} <label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.BulkyHint' }}">
</div> <input type="checkbox" name="system.traits.bulky" data-dtype="Boolean" {{checked system.traits.bulky}} />{{ localize 'MGT2.WeaponTraits.Bulky' }}
<div class="field-groups mt-1"> </label>
<div class="field-group w3-10"> <label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.VeryBulkyHint' }}">
<label>{{ localize 'MGT2.Items.Damage' }}</label> <input type="checkbox" name="system.traits.veryBulky" data-dtype="Boolean" {{checked system.traits.veryBulky}} />{{ localize 'MGT2.WeaponTraits.VeryBulky' }}
<input type="text" name="system.damage" value="{{system.damage}}" data-dtype="String" /> </label>
</div> <label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.StunHint' }}">
<div class="field-group w3-10" style="margin: 0 1rem;"> <input type="checkbox" name="system.traits.stun" data-dtype="Boolean" {{checked system.traits.stun}} />{{ localize 'MGT2.WeaponTraits.Stun' }}
<label>{{ localize 'MGT2.Items.Magazine' }}</label> </label>
<input type="number" name="system.magazine" value="{{system.magazine}}" data-dtype="Number" /> <label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.SmartHint' }}">
</div> <input type="checkbox" name="system.traits.smart" data-dtype="Boolean" {{checked system.traits.smart}} />{{ localize 'MGT2.WeaponTraits.Smart' }}
<div class="field-group w3-10"> </label>
<label>{{ localize 'MGT2.Items.MagazineCost' }}</label> <label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.RadiationHint' }}">
<input type="number" name="system.magazineCost" value="{{system.magazineCost}}" data-dtype="Number" /> <input type="checkbox" name="system.traits.radiation" data-dtype="Boolean" {{checked system.traits.radiation}} />{{ localize 'MGT2.WeaponTraits.Radiation' }}
</div> </label>
</div> <label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.ScopeHint' }}">
<div class="table-container"> <input type="checkbox" name="system.traits.scope" data-dtype="Boolean" {{checked system.traits.scope}} />{{ localize 'MGT2.WeaponTraits.Scope' }}
<div class="table-row heading color-2"> </label>
<div class="row-item row-item-left upcase">{{ localize 'MGT2.Items.Trait' }}</div> <label class="mgt2-checkbox mgt2-trait-bool" data-tooltip="{{ localize 'MGT2.WeaponTraits.ZeroGHint' }}">
<div class="row-item row-item-left">{{ localize 'MGT2.Items.Description' }}</div> <input type="checkbox" name="system.traits.zeroG" data-dtype="Boolean" {{checked system.traits.zeroG}} />{{ localize 'MGT2.WeaponTraits.ZeroG' }}
<div class="row-item row-item-right"><a class="options-create" data-property="traits"><i class="fas fa-plus"></i></a></div> </label>
</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}}
</div> </div>
</fieldset>
</div> </div>
<div class="tab" data-group="primary" data-tab="tab3"> <div class="tab" data-group="primary" data-tab="tab3">
{{> systems/mgt2/templates/items/parts/sheet-configuration.html }} {{> systems/mgt2/templates/items/parts/sheet-configuration.html }}
</div> </div>
</div> </div>
</div>
</div> </div>

View File

@@ -1,4 +1,32 @@
<form class="{{cssClass}} flexcol" autocomplete="off" style="padding: 0 6px;"> <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"> <div class="form-group">
<label>{{ localize 'MGT2.RollPrompt.CharacteristicDM' }}</label> <label>{{ localize 'MGT2.RollPrompt.CharacteristicDM' }}</label>
<select name="characteristic"> <select name="characteristic">
@@ -12,27 +40,166 @@
{{selectOptions skills selected=skill valueAttr="_id" labelAttr="name"}} {{selectOptions skills selected=skill valueAttr="_id" labelAttr="name"}}
</select> </select>
</div> </div>
{{#unless isAttack}}
<div class="form-group"> <div class="form-group">
<label>{{ localize 'MGT2.RollPrompt.Timeframes' }}</label> <label>{{ localize 'MGT2.RollPrompt.Timeframes' }}</label>
<select name="timeframes"> <select name="timeframes">
{{selectOptions config.Timeframes selected = timeframe localize = true}} {{selectOptions config.Timeframes selected = timeframe localize = true}}
</select> </select>
</div> </div>
{{/unless}}
<fieldset> <fieldset>
<legend>{{ localize 'MGT2.RollPrompt.States' }}</legend> <legend>{{ localize 'MGT2.RollPrompt.States' }}</legend>
<div class="form-group"> {{!-- Hidden checkboxes preserve form values for the roll calculation --}}
<label class="mgt2-checkbox"><input type="checkbox" name="encumbrance" data-dtype="Boolean" {{checked encumbrance}} />{{ localize 'MGT2.RollPrompt.EncumbranceDM' }}</label> <input type="checkbox" name="encumbrance" data-dtype="Boolean" {{checked encumbrance}} style="display:none" />
<label class="mgt2-checkbox"><input type="checkbox" name="fatigue" data-dtype="Boolean" {{checked fatigue}} />{{ localize 'MGT2.RollPrompt.FatigueDM' }}</label> <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> </div>
</fieldset> </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"> <div class="form-group">
<label>{{ localize 'MGT2.RollPrompt.CustomDM' }}</label> <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>
<div class="form-group"> <div class="form-group">
<label>{{ localize 'MGT2.RollPrompt.Difficulty' }}</label> <label>{{ localize 'MGT2.RollPrompt.Difficulty' }}</label>
<select name="difficulty"> <select name="difficulty">
<option></option> <option value=""></option>
{{selectOptions config.Difficulty selected = difficulty localize = true}} {{selectOptions config.Difficulty selected = difficulty localize = true}}
</select> </select>
</div> </div>