Spot the link / entityClass error!

This commit is contained in:
Professor Bunbury 2020-10-23 17:45:27 -04:00
parent 5f5a145626
commit d392b568db
56 changed files with 6353 additions and 3288 deletions

1
.gitignore vendored
View file

@ -25,3 +25,4 @@ hs_err_pid*
# IDE Folders
.idea/
.vs/
node_modules

View file

@ -1,9 +1,27 @@
# sw5e
SW5E Foundry VTT System
# Foundry Virtual Tabletop - SW5e Game System
For easy install
This game system for [Foundry Virtual Tabletop](http://foundryvtt.com) provides character sheet and game system
support for the SW5E roleplaying game.
In the Foundry Configuration and Setup window go to the Game Systems tab, click on the "Install System" button, and
place the following link into the Manifest URL at the bottom
This system provides character sheet support for Actors and Items, mechanical support for dice and rules necessary to
play games of SW5E, and compendium content for Monsters, Heroes, Items, Powers, Class Features, Monster
Features, and more!
The software component of this system is distributed under the GNUv3 license.
## Installation Instructions
To install and use the SW5e system for Foundry Virtual Tabletop, simply paste the following URL into the
**Install System** dialog on the Setup menu of the application.
https://raw.githubusercontent.com/unrealkakeman89/sw5e/master/system.json
If you wish to manually install the system, you must clone or extract it into the ``Data/systems/sw5e`` folder. You
may do this by cloning the repository or downloading a zip archive from the
[Releases Page](https://github.com/unrealkakeman89/sw5e).
## Community Contribution
Code and content contributions are accepted. Please feel free to submit issues to the issue tracker or submit merge
requests for code changes. Approval for such requests involves code and (if necessary) design review by The Dev Team.
Please reach out on the SW5E Foundry Dev Discord with any questions.

32
gulpfile.js Normal file
View file

@ -0,0 +1,32 @@
const gulp = require('gulp');
const less = require('gulp-less');
/* ----------------------------------------- */
/* Compile LESS
/* ----------------------------------------- */
const SW5E_LESS = ["less/*.less"];
function compileLESS() {
return gulp.src("less/sw5e.less")
.pipe(less())
.pipe(gulp.dest("./"))
}
const css = gulp.series(compileLESS);
/* ----------------------------------------- */
/* Watch Updates
/* ----------------------------------------- */
function watchUpdates() {
gulp.watch(SW5E_LESS, css);
}
/* ----------------------------------------- */
/* Export Tasks
/* ----------------------------------------- */
exports.default = gulp.series(
gulp.parallel(css),
watchUpdates
);
exports.css = css;

View file

@ -1,29 +1,58 @@
{
"ACTOR.TypeCharacter": "Player Character",
"ACTOR.TypeNpc": "Non-Player Character",
"ACTOR.TypeVehicle": "Vehicle",
"ITEM.TypeBackpack": "Backpack",
"ITEM.TypeClass": "Class",
"ITEM.TypeConsumable": "Consumable",
"ITEM.TypeEquipment": "Equipment",
"ITEM.TypeFeat": "Feature",
"ITEM.TypeLoot": "Loot",
"ITEM.TypePower": "Power",
"ITEM.TypeTool": "Tool",
"ITEM.TypeWeapon": "Weapon",
"Star Wars 5th Edition": "Star Wars 5th Edition",
"SW5E.title": "Star Wars 5th Edition",
"SW5E.AbbreviationCR": "CR",
"SW5E.AbbreviationConc": "Conc.",
"SW5E.AbbreviationDC": "DC",
"SW5E.AbbreviationLR": "LR",
"SW5E.AbbreviationLevel": "Lvl.",
"SW5E.AbbreviationLbs": "lbs.",
"SW5E.AbbreviationSR": "SR",
"SW5E.Ability": "Ability",
"SW5E.AbilityCha": "Charisma",
"SW5E.AbilityCon": "Constitution",
"SW5E.AbilityDex": "Dexterity",
"SW5E.AbilityInt": "Intelligence",
"SW5E.AbilityModifier": "Ability Modifier",
"SW5E.AbilityStr": "Strength",
"SW5E.AbilityUseCantUse": "You are not currently able to use this ability!",
"SW5E.AbilityUseCharged": "charged",
"SW5E.AbilityUseConsume": "Consume Available Usage?",
"SW5E.AbilityUseDepleted": "depleted",
"SW5E.AbilityUseHint": "Configure how you would like to use the",
"SW5E.AbilityUseRechargeHint": "This ability uses a recharge mechanic and is currently",
"SW5E.AbilityUseWarnEnd": "available uses per",
"SW5E.AbilityUseWarnStart": "This ability has",
"SW5E.AbilityStrAbbr": "str",
"SW5E.AbilityCon": "Constitution",
"SW5E.AbilityConAbbr": "con",
"SW5E.AbilityDex": "Dexterity",
"SW5E.AbilityDexAbbr": "dex",
"SW5E.AbilityInt": "Intelligence",
"SW5E.AbilityIntAbbr": "int",
"SW5E.AbilityWis": "Wisdom",
"SW5E.AbilityWisAbbr": "wis",
"SW5E.AbilityCha": "Charisma",
"SW5E.AbilityChaAbbr": "cha",
"SW5E.AbilityModifier": "Ability Modifier",
"SW5E.AbilityPromptText": "What type of {ability} check?",
"SW5E.AbilityPromptTitle": "{ability} Ability Check",
"SW5E.AbilityUseHint": "Configure how you would like to use the {name} {type}.",
"SW5E.AbilityUseUnavailableHint": "There are no uses of this item remaining!",
"SW5E.AbilityUseChargedHint": "This {type} is charged and ready to use!",
"SW5E.AbilityUseRechargedHint": "This {type} is depleted and must be recharged!",
"SW5E.AbilityUseNormalHint": "This {type} has {value} of {max} uses per {per} remaining.",
"SW5E.AbilityUseConsumableChargeHint": "Using this {type} will consume 1 charge of {value} remaining.",
"SW5E.AbilityUseConsumableQuantityHint": "Using this {type} will consume 1 quantity of {quantity} remaining",
"SW5E.AbilityUseConsumableDestroyHint": "Using this {type} will consume its final charge and it will be destroyed.",
"SW5E.AbilityUseConsume": "Consume Available Usage?",
"SW5E.AbilityUseChargesLabel": "{value} Charges",
"SW5E.AbilityUseConsumableLabel": "{max} per {per}",
"SW5E.AbilityUseCast": "Cast Power",
"SW5E.AbilityUseUse": "Use Ability",
"SW5E.Action": "Action",
"SW5E.ActionPl": "Actions",
"SW5E.ActionAbil": "Ability Check",
"SW5E.ActionHeal": "Healing",
"SW5E.ActionMPAK": "Melee Power Attack",
@ -33,6 +62,8 @@
"SW5E.ActionRWAK": "Ranged Weapon Attack",
"SW5E.ActionSave": "Saving Throw",
"SW5E.ActionUtil": "Utility",
"SW5E.ActionWarningNoItem": "The requested item {item} no longer exists on Actor {name}",
"SW5E.ActionWarningNoToken": "You must have one or more controlled Tokens in order to use this option.",
"SW5E.Add": "Add",
"SW5E.Advantage": "Advantage",
"SW5E.Alignment": "Alignment",
@ -44,15 +75,18 @@
"SW5E.AlignmentLN": "Lawful Neutral",
"SW5E.AlignmentND": "Neutral Dark",
"SW5E.AlignmentNL": "Neutral Light",
"SW5E.AlignmentBN": "Balenced Neutral",
"SW5E.AlignmentBN": "Balanced Neutral",
"SW5E.Archetypes": "Archetypes",
"SW5E.Appearance": "Appearance",
"SW5E.ArmorClass": "Armor Class",
"SW5E.AC": "AC",
"SW5E.ArmorProperties": "Armor Properties",
"SW5E.ArmorProperAbsorptive": "Absorptive",
"SW5E.ArmorProperAgile": "Agile",
"SW5E.ArmorProperAnchor": "Anchor",
"SW5E.ArmorProperAvoidant": "Avoidant",
"SW5E.ArmorProperBarbed": "Barbed",
"SW5E.ArmorProperBulky": "Bulky",
"SW5E.ArmorProperCharging": "Charging",
"SW5E.ArmorProperConcealing": "Concealing",
"SW5E.ArmorProperCumbersome": "Cumbersome",
@ -65,6 +99,7 @@
"SW5E.ArmorProperLightweight": "Lightweight",
"SW5E.ArmorProperMagnetic": "Magnetic",
"SW5E.ArmorProperObscured": "Obscured",
"SW5E.ArmorProperObtrusive": "Obtrusive",
"SW5E.ArmorProperPowered": "Powered",
"SW5E.ArmorProperReactive": "Reactive",
"SW5E.ArmorProperRegulated": "Regulated",
@ -74,22 +109,26 @@
"SW5E.ArmorProperSilent": "Silent",
"SW5E.ArmorProperSpiked": "Spiked",
"SW5E.ArmorProperSteadfast": "Steadfast",
"SW5E.ArmorProperStrength": "Strength Rqmt.",
"SW5E.ArmorProperVersatile": "Versatile",
"SW5E.Attack": "Attack",
"SW5E.AttackPl": "Attacks",
"SW5E.AttackRoll": "Attack Roll",
"SW5E.Attributes": "Attributes",
"SW5E.Attuned": "Attuned",
"SW5E.Background": "Background",
"SW5E.Biography": "Biography",
"SW5E.Bonds": "Bonds",
"SW5E.BonusAbilityCheck": "Global Ability Check Bonus",
"SW5E.BonusAbilitySave": "Global Saving Throw Bonus",
"SW5E.BonusAbilitySkill": "Global Skill Check Bonus",
"SW5E.BonusAction": "Bonus Action",
"SW5E.BonusMSAttack": "Melee Power Attack Bonus",
"SW5E.BonusMSDamage": "Melee Power Damage Bonus",
"SW5E.BonusMPAttack": "Melee Power Attack Bonus",
"SW5E.BonusMPDamage": "Melee Power Damage Bonus",
"SW5E.BonusMWAttack": "Melee Weapon Attack Bonus",
"SW5E.BonusMWDamage": "Melee Weapon Damage Bonus",
"SW5E.BonusRSAttack": "Ranged Power Attack Bonus",
"SW5E.BonusRSDamage": "Ranged Power Damage Bonus",
"SW5E.BonusRPAttack": "Ranged Power Attack Bonus",
"SW5E.BonusRPDamage": "Ranged Power Damage Bonus",
"SW5E.BonusRWAttack": "Ranged Weapon Attack Bonus",
"SW5E.BonusRWDamage": "Ranged Weapon Damage Bonus",
"SW5E.BonusSaveForm": "Update Bonuses",
@ -116,6 +155,7 @@
"SW5E.ConBlinded": "Blinded",
"SW5E.ConCharmed": "Charmed",
"SW5E.ConDeafened": "Deafened",
"SW5E.ConDiseased": "Diseased",
"SW5E.ConExhaustion": "Exhaustion",
"SW5E.ConFrightened": "Frightened",
"SW5E.ConGrappled": "Grappled",
@ -128,9 +168,22 @@
"SW5E.ConProne": "Prone",
"SW5E.ConRestrained": "Restrained",
"SW5E.ConShocked": "Shocked",
"SW5E.ConSlowed": "Slowed",
"SW5E.ConStunned": "Stunned",
"SW5E.ConUnconscious": "Unconscious",
"SW5E.Concentration": "Concentration",
"SW5E.ConsumeTitle": "Resource Consumption",
"SW5E.ConsumeAmmunition": "Ammunition",
"SW5E.ConsumeAttribute": "Attribute",
"SW5E.ConsumeMaterial": "Material",
"SW5E.ConsumeCharges": "Item Uses",
"SW5E.ConsumeWarningNoResource": "{name} is designated to consume {type} but no resource is specified!",
"SW5E.ConsumeWarningNoSource": "The designated {type} source that {name} consumes no longer exists!",
"SW5E.ConsumeWarningNoQuantity": "{name} has run out of its designated {type}!",
"SW5E.ConsumeWarningZeroAttribute": "{name} has run out of its designated attribute resource pool!",
"SW5E.ConsumableAmmunition": "Ammunition",
"SW5E.ConsumableFood": "Food",
"SW5E.ConsumablePoison": "Poison",
"SW5E.ConsumableAdrenal": "Adrenal",
@ -138,9 +191,19 @@
"SW5E.ConsumableMedpac": "Medpac",
"SW5E.ConsumableTrinket": "Trinket",
"SW5E.ConsumableTechnology": "Technology",
"SW5E.ConsumableAmmunition": "Ammunition",
"SW5E.ConsumableForce": "Force Points",
"SW5E.ConsumableTech": "Tech Points",
"SW5E.ConsumableUseWarnStart": "This consumable has",
"SW5E.ConsumableUseWarnEnd": "of the current unit",
"SW5E.ConsumableUnitWarn": "units remaining",
"SW5E.ConsumableLastChargeWarn": "This is the last charge of this unit and consuming it will also reduce the item's quantity by 1",
"SW5E.ConsumableWithoutCharges": "available units to use",
"SW5E.Consumed": "Consumed",
"SW5E.CostGP": "Cost (GP)",
"SW5E.Cover": "Cover",
"SW5E.CoverHalf": "Half",
"SW5E.CoverThreeQuarters": "Three Quarters",
"SW5E.CoverTotal": "Total",
"SW5E.CostGP": "Cost (CR)",
"SW5E.Critical": "Critical",
"SW5E.CriticalHit": "Critical Hit",
"SW5E.Currency": "Currency",
@ -151,24 +214,31 @@
"SW5E.DamRes": "Damage Resistances",
"SW5E.DamVuln": "Damage Vulnerabilities",
"SW5E.Damage": "Damage",
"SW5E.DamageAcid": "Acid",
"SW5E.DamageCold": "Cold",
"SW5E.DamageEnergy": "Energy",
"SW5E.DamageFire": "Fire",
"SW5E.DamageForce": "Force",
"SW5E.DamageIon": "ION",
"SW5E.DamageKinetic": "Kinetic",
"SW5E.DamageLightning": "Lightning",
"SW5E.DamageNecrotic": "Necrotic",
"SW5E.DamagePoison": "Poison",
"SW5E.DamagePsychic": "Psychic",
"SW5E.DamageSonic": "Sonic",
"SW5E.DamageAcid": "Acid",
"SW5E.DamageCold": "Cold",
"SW5E.DamageEnergy": "Energy",
"SW5E.DamageFire": "Fire",
"SW5E.DamageForce": "Force",
"SW5E.DamageIon": "Ion",
"SW5E.DamageKinetic": "Kinetic",
"SW5E.DamageLightning": "Lightning",
"SW5E.DamageNecrotic": "Necrotic",
"SW5E.DamagePoison": "Poison",
"SW5E.DamagePsychic": "Psychic",
"SW5E.DamageSonic": "Sonic",
"SW5E.DamageRoll": "Damage Roll",
"SW5E.Day": "Day",
"SW5E.DeathSave": "Death Saves",
"SW5E.DeathSaveCriticalSuccess": "{name} critically succeeded on a death saving throw and has regained 1 Hit Point!",
"SW5E.DeathSaveSuccess": "{name} has survived with 3 death save successes and is now stable!",
"SW5E.DeathSaveFailure": "{name} has died with 3 death save failures!",
"SW5E.DeathSavingThrow": "Death Saving Throw",
"SW5E.DeathSaveUnnecessary": "You do not need to roll death saves because you have a positive number of hit points or have already reached 3 successes or failures.",
"SW5E.Default": "Default",
"SW5E.DefaultAbilityCheck": "Default Ability Check",
"SW5E.Description": "Description",
"SW5E.Details": "Details",
"SW5E.Dimensions": "Dimensions",
"SW5E.Disadvantage": "Disadvantage",
"SW5E.DistAny": "Any",
"SW5E.DistFt": "Feet",
@ -176,6 +246,7 @@
"SW5E.DistSelf": "Self",
"SW5E.DistTouch": "Touch",
"SW5E.Duration": "Duration",
"SW5E.Effects": "Effects",
"SW5E.EquipmentBonus": "Magical Bonus",
"SW5E.EquipmentClothing": "Clothing",
"SW5E.EquipmentHeavy": "Heavy Armor",
@ -185,19 +256,20 @@
"SW5E.EquipmentShield": "Shield",
"SW5E.EquipmentShieldProficiency": "Shields",
"SW5E.EquipmentTrinket": "Trinket",
"SW5E.EquipmentVehicle": "Vehicle Equipment",
"SW5E.Equipped": "Equipped",
"SW5E.Exhaustion": "Exhaustion",
"SW5E.Expertise": "Expertise",
"SW5E.FeatureActionRecharge": "Action Recharge",
"SW5E.Flaws": "Flaws",
<<<<<<< Updated upstream
=======
"SW5E.ItemTypeArchetype": "Archetype",
"SW5E.ItemTypeBackground": "Background",
"Sw5E.ItemTypeBackgroundPl": "Backgrounds",
>>>>>>> Stashed changes
"SW5E.ItemTypeClass": "Class",
"SW5E.ItemTypeClassPl": "Class Levels",
"SW5E.ItemTypeClassFeat": "Class Feature",
"SW5E.ItemTypeClassFeats": "Class Features",
"SW5E.ItemTypeConsumable": "Consumable",
"SW5E.ItemTypeConsumablePl": "Consumables",
"SW5E.ItemTypeContainer": "Container",
@ -214,6 +286,7 @@
"SW5E.ItemTypePowerPl": "Powers",
"SW5E.ItemTypeWeapon": "Weapon",
"SW5E.ItemTypeWeaponPl": "Weapons",
"SW5E.ItemNoUses": "{name} has no available uses remaining.",
"SW5E.FeatureActive": "Active Abilities",
"SW5E.FeatureAdd": "Create Feature",
@ -223,12 +296,37 @@
"SW5E.FeatureRechargeResult": "1d6 Result",
"SW5E.FeatureUsage": "Feature Usage",
"SW5E.Features": "Features",
"SW5E.FeetAbbr": "ft.",
"SW5E.Filter": "Filter",
"SW5E.FilterNoPowers": "No powers found for this set of filters.",
"SW5E.NoPowerLevels": "This character has no powercaster levels, but you may add powers manually.",
"SW5E.FlagsInstructions": "Configure character features and traits which fine-tune behaviors of the SW5e system.",
"SW5E.FlagsSave": "Update Special Traits",
"SW5E.FlagsTitle": "Configure Special Traits",
"SW5E.FlagsPowerfulBuild": "Powerful Build",
"SW5E.FlagsPowerfulBuildHint": "Provides increased carrying capacity.",
"SW5E.FlagsSavageAttacks": "Savage Attacks",
"SW5E.FlagsSavageAttacksHint": "Adds extra critical hit weapon dice.",
"SW5E.FlagsElvenAccuracy": "Elven Accuracy",
"SW5E.FlagsElvenAccuracyHint": "Roll an extra d20 with advantage to Dex, Int, Wis, or Cha.",
"SW5E.FlagsHalflingLucky": "Halfling Lucky",
"SW5E.FlagsHalflingLuckyHint": "Reroll ones when rolling d20 checks.",
"SW5E.FlagsInitiativeAdv": "Advantage on Initiative",
"SW5E.FlagsInitiativeAdvHint": "Provided by feats or magical items.",
"SW5E.FlagsAlert": "Alert Feat",
"SW5E.FlagsAlertHint": "Provides +5 to Initiative.",
"SW5E.FlagsJOAT": "Jack of All Trades",
"SW5E.FlagsJOATHint": "Half-Proficiency to Ability Checks in which you are not already Proficient.",
"SW5E.FlagsObservant": "Observant Feat",
"SW5E.FlagsObservantHint": "Provides a +5 to passive Perception and Investigation.",
"SW5E.FlagsReliableTalent": "Reliable Talent",
"SW5E.FlagsReliableTalentHint": "Rogues Reliable Talent Feature.",
"SW5E.FlagsRemarkableAthlete": "Remarkable Athlete.",
"SW5E.FlagsRemarkableAthleteHint": "Half-Proficiency (rounded-up) to physical Ability Checks and Initiative.",
"SW5E.FlagsCritThreshold": "Critical Hit Threshold",
"SW5E.FlagsCritThresholdHint": "Allow for expanded critical range; for example Improved or Superior Critical",
"SW5E.Flat": "Flat",
"SW5E.Formula": "Formula",
"SW5E.GrantedAbilities": "Granted Abilities",
@ -236,9 +334,15 @@
"SW5E.Healing": "Healing",
"SW5E.HealingTemp": "Healing (Temporary)",
"SW5E.Health": "Health",
"SW5E.HP": "Health",
"SW5E.HealthConditions": "Health Conditions",
"SW5E.HealthFormula": "Health Formula",
"SW5E.HPFormula": "Health Formula",
"SW5E.HitDice": "Hit Dice",
"SW5E.HitDiceRoll": "Roll Hit Dice",
"SW5E.HitDiceUsed": "Hit Dice Used",
"SW5E.HitDiceWarn": "{name} has no available {formula} Hit Dice remaining!",
"SW5E.Ideals": "Ideals",
"SW5E.Identified": "Identified",
"SW5E.Initiative": "Initiative",
"SW5E.Inspiration": "Inspiration",
@ -248,12 +352,10 @@
"SW5E.ItemActivationCost": "Activation Cost",
"SW5E.ItemAttackBonus": "Attack Roll Bonus",
"SW5E.ItemConsumableActivation": "Consumable Activation",
"SW5E.ItemConsumableUsage": "Consumable Usage",
"SW5E.ItemConsumableDetails": "Consumable Details",
"SW5E.ItemConsumableStatus": "Consumable Status",
"SW5E.ItemConsumableType": "Consumable Type",
"SW5E.ItemConsumableUsage": "Consumable Usage",
"SW5E.ItemConsumeOnUse": "Consume on Use",
"SW5E.ItemContainerCapacity": "Capacity",
"SW5E.ItemContainerCapacityItems": "Items",
"SW5E.ItemContainerCapacityType": "Capacity Type",
@ -276,6 +378,10 @@
"SW5E.ItemEquipmentUsage": "Equipment Usage",
"SW5E.ItemName": "Item Name",
"SW5E.ItemNew": "New {type}",
"SW5E.ItemRechargeCheck": "{name} recharge check",
"SW5E.ItemRechargeFailure": "failure!",
"SW5E.ItemRechargeSuccess": "success!",
"SW5E.ItemRequiredStr": "Required Strength",
"SW5E.ItemToolProficiency": "Tool Proficiency",
@ -287,112 +393,9 @@
"SW5E.ItemWeaponType": "Weapon Type",
"SW5E.ItemWeaponUsage": "Weapon Usage",
"SW5E.JackOfAllTrades": "Jack of all Trades",
"SW5E.LairAct": "Lair Action",
"SW5E.LairAct": "Uses Lair Action",
"SW5E.LairActionLabel": "Lair Action",
"SW5E.Languages": "Languages",
<<<<<<< Updated upstream
"SW5E.LanguagesAleena": "Aleena",
"SW5E.LanguagesAntarian": "Antarian",
"SW5E.LanguagesAnzellan": "Anzellan",
"SW5E.LanguagesAqualish": "Aqualish",
"SW5E.LanguagesArdennian": "Ardennian",
"SW5E.LanguagesBalosur": "Balosur",
"SW5E.LanguagesBarabel": "Barabel",
"SW5E.LanguagesBasic": "Basic",
"SW5E.LanguagesBesalisk": "Besalisk",
"SW5E.LanguagesBinary": "Binary",
"SW5E.LanguagesBith": "Bith",
"SW5E.LanguagesBocce": "Bocce",
"SW5E.LanguagesBothese": "Bothese",
"SW5E.LanguagesCatharese": "Catharese",
"SW5E.LanguagesCerean": "Cerean",
"SW5E.LanguagesChadra-Fan": "Chadra-Fan",
"SW5E.LanguagesChagri": "Chagri",
"SW5E.LanguagesCheunh": "Cheunh",
"SW5E.LanguagesChevin": "Chevin",
"SW5E.LanguagesChironan": "Chironan",
"SW5E.LanguagesClawdite": "Clawdite",
"SW5E.LanguagesCodruese": "Codruese",
"SW5E.LanguagesColicoid": "Colicoid",
"SW5E.LanguagesDashadi": "Dashadi",
"SW5E.LanguagesDefel": "Defel",
"SW5E.LanguagesDevaronese": "Devaronese",
"SW5E.LanguagesDosh": "Dosh",
"SW5E.LanguagesDraethos": "Draethos",
"SW5E.LanguagesDug": "Dug",
"SW5E.LanguagesDurese": "Durese",
"SW5E.LanguagesEwokese": "Ewokese",
"SW5E.LanguagesFalleen": "Falleen",
"SW5E.LanguagesFelucianese": "Felucianese",
"SW5E.LanguagesGamorrese": "Gamorrese",
"SW5E.LanguagesGand": "Gand",
"SW5E.LanguagesGeonosian": "Geonosian",
"SW5E.LanguagesGivin": "Givin",
"SW5E.LanguagesGran": "Gran",
"SW5E.LanguagesGungan": "Gungan",
"SW5E.LanguagesHapan": "Hapan",
"SW5E.LanguagesHarchese": "Harchese",
"SW5E.LanguagesHerglese": "Herglese",
"SW5E.LanguagesHonoghran": "Honoghran",
"SW5E.LanguagesHuttese": "Huttese",
"SW5E.LanguagesIktotchese": "Iktotchese",
"SW5E.LanguagesIthorese": "Ithorese",
"SW5E.LanguagesJawaese": "Jawaese",
"SW5E.LanguagesKaleesh": "Kaleesh",
"SW5E.LanguagesKaminoan": "Kaminoan",
"SW5E.LanguagesKarkaran": "Karkaran",
"SW5E.LanguagesKelDor": "Kel Dor",
"SW5E.LanguagesKharan": "Kharan",
"SW5E.LanguagesKillik": "Killik",
"SW5E.LanguagesKlatooinian": "Klatooinian",
"SW5E.LanguagesKubazian": "Kubazian",
"SW5E.LanguagesKushiban": "Kushiban",
"SW5E.LanguagesKyuzo": "Kyuzo",
"SW5E.LanguagesLannik": "Lannik",
"SW5E.LanguagesLasat": "Lasat",
"SW5E.LanguagesLowickese": "Lowickese",
"SW5E.LanguagesMandoa": "Mando'a",
"SW5E.LanguagesMiralukese": "Miralukese",
"SW5E.LanguagesMirialan": "Mirialan",
"SW5E.LanguagesMonCal": "Mon Cal",
"SW5E.LanguagesMustafarian": "Mustafarian",
"SW5E.LanguagesMuun": "Muun",
"SW5E.LanguagesNautila": "Nautila",
"SW5E.LanguagesOrtolan": "Ortolan",
"SW5E.LanguagesPakPak": "Pak Pak",
"SW5E.LanguagesPyke": "Pyke",
"SW5E.LanguagesQuarrenese": "Quarrenese",
"SW5E.LanguagesRakata": "Rakata",
"SW5E.LanguagesRattataki": "Rattataki",
"SW5E.LanguagesRishii": "Rishii",
"SW5E.LanguagesRodese": "Rodese",
"SW5E.LanguagesSelkatha": "Selkatha",
"SW5E.LanguagesSemblan": "Semblan",
"SW5E.LanguagesShyriiwook": "Shyriiwook",
"SW5E.LanguagesShistavanen": "Shistavanen",
"SW5E.LanguagesSith": "Sith",
"SW5E.LanguagesSquibbian": "Squibbian",
"SW5E.LanguagesSriluurian": "Sriluurian",
"SW5E.LanguagesSsi-ruuvi": "Ssi-ruuvi",
"SW5E.LanguagesSullustese": "Sullustese",
"SW5E.LanguagesTalzzi": "Talzzi",
"SW5E.LanguagesThisspiasian": "Thisspiasian",
"SW5E.LanguagesTogorese": "Togorese",
"SW5E.LanguagesTogruti": "Togruti",
"SW5E.LanguagesToydarian": "Toydarian",
"SW5E.LanguagesTusken": "Tusken",
"SW5E.LanguagesTwileki": "Twi'leki",
"SW5E.LanguagesUgnaught": "Ugnaught",
"SW5E.LanguagesUmbaran": "Umbaran",
"SW5E.LanguagesUtapese": "Utapese",
"SW5E.LanguagesVerpine": "Verpine",
"SW5E.LanguagesVong": "Vong",
"SW5E.LanguagesVoss": "Voss",
"SW5E.LanguagesYevethan": "Yevethan",
"SW5E.LanguagesZabraki": "Zabraki",
"SW5E.LanguagesZygerrian": "Zygerrian",
"SW5E.LegAct": "Legd. Actions",
"SW5E.LegRes": "Legd. Resistance",
=======
"SW5E.LanguagesAleena": "Aleena",
"SW5E.LanguagesAntarian": "Antarian",
"SW5E.LanguagesAnzellan": "Anzellan",
@ -501,11 +504,18 @@
"SW5E.LegAct": "Legendary Actions",
"SW5E.LegendaryActionLabel": "Legendary Action",
"SW5E.LegRes": "Legendary Resistance",
>>>>>>> Stashed changes
"SW5E.Level": "Level",
"SW5E.LevelScaling": "Level Scaling",
"SW5E.LimitedUses": "Limited Uses",
"SW5E.LongRest": "Long Rest",
"SW5E.LongRestNormal": "Long Rest (8 hours)",
"SW5E.LongRestGritty": "Long Rest (7 days)",
"SW5E.LongRestEpic": "Long Rest (1 hour)",
"SW5E.LongRestOvernight": "Long Rest (New Day)",
"SW5E.LongRestResult": "{name} takes a long rest and recovers {health} Hit Points and {dice} Hit Dice.",
"SW5E.LongRestResultHitDice": "{name} takes a long rest and recovers {dice} Hit Dice.",
"SW5E.LongRestResultHitPoints": "{name} takes a long rest and recovers {health} Hit Points.",
"SW5E.LongRestResultShort": "{name} takes a long rest.",
"SW5E.Max": "Max",
"SW5E.Modifier": "Modifier",
"SW5E.Name": "Character Name",
@ -514,6 +524,9 @@
"SW5E.Normal": "Normal",
"SW5E.NotProficient": "Not Proficient",
"SW5E.OtherFormula": "Other Formula",
"SW5E.PactMagic": "Pact Magic",
"SW5E.Passive": "Passive",
"SW5E.PersonalityTraits": "Personality Traits",
"SW5E.PlaceTemplate": "Place Measured Template",
"SW5E.Polymorph": "Polymorph",
"SW5E.PolymorphAcceptSettings": "Custom Settings",
@ -531,9 +544,13 @@
"SW5E.PolymorphMergeSkills": "Merge skill proficiencies (take the highest)",
"SW5E.PolymorphPromptTitle": "Transforming Actor",
"SW5E.PolymorphRestoreTransformation": "Restore Transformation",
"SW5E.PolymorphRevertWarn": "You do not have permission to revert this Actor's polymorphed state.",
"SW5E.PolymorphTmpClass": "Temporary Class",
"SW5E.PolymorphTokens": "Transform all linked tokens?",
"SW5E.PolymorphWarn": "You are not allowed to polymorph this actor!",
"SW5E.PolymorphWildShape": "Wild Shape",
"SW5E.Prepared": "Prepared",
"SW5E.Concentrate": "Concentrate",
"SW5E.Concentrated": "Concentrate",
"SW5E.Price": "Price",
"SW5E.Proficiency": "Proficiency",
@ -543,6 +560,8 @@
"SW5E.Range": "Range",
"SW5E.Rarity": "Rarity",
"SW5E.Reaction": "Reaction",
"SW5E.ReactionPl": "Reactions",
"SW5E.Recharge": "Recharge",
"SW5E.RequiredMaterials": "Required Materials",
"SW5E.Requirements": "Requirements",
"SW5E.ResourcePrimary": "Resource 1",
@ -556,6 +575,14 @@
"SW5E.RollMode": "Roll Mode",
"SW5E.RollSituationalBonus": "Situational Bonus?",
"SW5E.Save": "Save",
"SW5E.SheetClassCharacter": "Default Character Sheet",
"SW5E.SheetClassNPC": "Default NPC Sheet",
"SW5E.SheetClassVehicle": "Default Vehicle Sheet",
"SW5E.SheetClassItem": "Default Item Sheet",
"SW5E.SavingThrow": "Saving Throw",
"SW5E.SaveDC": "DC {dc} {ability}",
"SW5E.SavePromptTitle": "{ability} Saving Throw",
"SW5E.ScalingFormula": "Scaling Formula",
"SW5E.SchoolLgt": "Light",
"SW5E.SchoolUni": "Universal",
@ -568,8 +595,14 @@
"SW5E.SenseTS": "Tremorsense",
"SW5E.Senses": "Senses",
"SW5E.ShortRest": "Short Rest",
"SW5E.ShortRestNormal": "Short Rest (1 hour)",
"SW5E.ShortRestGritty": "Short Rest (8 hours)",
"SW5E.ShortRestEpic": "Short Rest (5 minutes)",
"SW5E.ShortRestOvernight": "Short Rest (New Day)",
"SW5E.ShortRestHint": "Take a short rest? On a short rest you may spend remaining Hit Dice and recover primary or secondary resources.",
"SW5E.ShortRestNoHD": "No Hit Dice remaining",
"SW5E.ShortRestResult": "{name} takes a short rest spending {dice} Hit Dice to recover {health} Hit Points.",
"SW5E.ShortRestResultShort": "{name} takes a short rest.",
"SW5E.ShortRestSelect": "Select Dice to Roll",
"SW5E.Size": "Size",
"SW5E.SizeGargantuan": "Gargantuan",
@ -596,6 +629,7 @@
"SW5E.SkillSte": "Stealth",
"SW5E.SkillSur": "Survival",
"SW5E.SkillTec": "Technology",
"SW5E.SkillPromptTitle": "{skill} Skill Check",
"SW5E.Slots": "Slots",
"SW5E.Source": "Source",
"SW5E.Special": "Special",
@ -628,7 +662,8 @@
"SW5E.PowerLevel7": "7th Level",
"SW5E.PowerLevel8": "8th Level",
"SW5E.PowerLevel9": "9th Level",
"SW5E.PowerLevelPact": "Pact Slot",
"SW5E.PowerLevelSlot": "{level} ({n} Slots)",
"SW5E.PowerLevelPact": "Pact Slot [Level {level}] ({n} Slots)",
"SW5E.PowerMaterials": "Powercasting Materials",
"SW5E.PowerName": "Power Name",
"SW5E.PowerNone": "None",
@ -636,6 +671,8 @@
"SW5E.PowerPrepInnate": "Innate Powercasting",
"SW5E.PowerPrepPrepared": "Prepared",
"SW5E.PowerPrepAlways": "Always Prepared",
"SW5E.PowerPreparationMode": "Power Preparation Mode",
"SW5E.PowerPrepared": "Prepared",
"SW5E.PowerConcentrationMode": "Power Concentration Mode",
"SW5E.PowerConcentrating": "Concentrating",
"SW5E.PowerProgArt": "Artificer",
@ -649,6 +686,7 @@
"SW5E.Powerbook": "Powerbook",
"SW5E.SpeciesDescription": "Description",
"SW5E.SpeciesTraits": "Species Traits",
"SW5E.StealthDisadvantage": "Stealth Disadvantage",
"SW5E.SubclassName": "Subclass Name",
"SW5E.Supply": "Supply",
"SW5E.Target": "Target",
@ -657,6 +695,7 @@
"SW5E.TargetCreature": "Creature",
"SW5E.TargetCube": "Cube",
"SW5E.TargetCylinder": "Cylinder",
"SW5E.TargetDroid": "Droid",
"SW5E.TargetEnemy": "Enemy",
"SW5E.TargetLine": "Line",
"SW5E.TargetObject": "Object",
@ -666,8 +705,10 @@
"SW5E.TargetSphere": "Sphere",
"SW5E.TargetSquare": "Square",
"SW5E.TargetWall": "Wall",
"SW5E.TargetDroid": "Droid",
"SW5E.TargetWeapon": "Weapon",
"SW5E.TargetWidth": "Line Width",
"SW5E.Temp": "Temp",
"SW5E.Threshold": "Threshold",
"SW5E.TimeDay": "Days",
"SW5E.TimeHour": "Hours",
"SW5E.TimeInst": "Instantaneous",
@ -677,38 +718,40 @@
"SW5E.TimeRound": "Rounds",
"SW5E.TimeTurn": "Turns",
"SW5E.TimeYear": "Years",
"SW5E.ToolArmormech": "Armormech's Tools",
"SW5E.ToolArmstech": "Armstech's Tools",
"SW5E.ToolArtificer": "Artificer's Tools",
"SW5E.ToolArtist": "Artist's Tools",
"SW5E.ToolAstrotech": "Astrotech's Tools",
"SW5E.ToolBiotech": "Biotech's Tools",
"SW5E.ToolConstructor": "Constructor's Tools",
"SW5E.ToolCybertech": "Cybertech's Tools",
"SW5E.ToolJeweler": "Jeweler's Tools",
"SW5E.ToolSurveyor": "Surveyor's Tools",
"SW5E.ToolSynthweaver": "Synthweavers's Tools",
"SW5E.ToolTinker": "Tinker's Tools",
"SW5E.ToolAntitoxkit": "Antitoxkit",
"SW5E.ToolArchaeologistKit": "Archaeologist Kit",
"SW5E.ToolAudiotechKit": "Audiotech's Kit",
"SW5E.ToolBioanalysisKit": "Bioanalysis Kit",
"SW5E.ToolBrewerKit": "Brewer's Kit",
"SW5E.ToolChefKit": "Chef's Kit",
"SW5E.ToolDemolitionKit": "Demolition Kit",
"SW5E.ToolDisguiseKit": "Disguise Kit",
"SW5E.ToolForgeryKit": "Forgery Kit",
"SW5E.ToolMechanicKit": "Mechanic's Kit",
"SW5E.ToolMunitionsKit": "Munitions Kit",
"SW5E.ToolPoisonKit": "Poisoner's Kit",
"SW5E.ToolScavengingKit": "Scavenging Kit",
"SW5E.ToolSecurityKit": "Security Kit",
"SW5E.ToolSlicerKit": "Slicer's Kit",
"SW5E.ToolSpiceKit": "Spicer's Kit",
"SW5E.ToolArmormech": "Armormech's Tools",
"SW5E.ToolArmstech": "Armstech's Tools",
"SW5E.ToolArtificer": "Artificer's Tools",
"SW5E.ToolArtist": "Artist's Tools",
"SW5E.ToolAstrotech": "Astrotech's Tools",
"SW5E.ToolBiotech": "Biotech's Tools",
"SW5E.ToolCheck": "Tool Check",
"SW5E.ToolConstructor": "Constructor's Tools",
"SW5E.ToolCybertech": "Cybertech's Tools",
"SW5E.ToolJeweler": "Jeweler's Tools",
"SW5E.ToolSurveyor": "Surveyor's Tools",
"SW5E.ToolSynthweaver": "Synthweavers's Tools",
"SW5E.ToolTinker": "Tinker's Tools",
"SW5E.ToolAntitoxkit": "Antitoxkit",
"SW5E.ToolArchaeologistKit": "Archaeologist Kit",
"SW5E.ToolAudiotechKit": "Audiotech's Kit",
"SW5E.ToolBioanalysisKit": "Bioanalysis Kit",
"SW5E.ToolBrewerKit": "Brewer's Kit",
"SW5E.ToolChefKit": "Chef's Kit",
"SW5E.ToolDemolitionKit": "Demolition Kit",
"SW5E.ToolDisguiseKit": "Disguise Kit",
"SW5E.ToolForgeryKit": "Forgery Kit",
"SW5E.ToolMechanicKit": "Mechanic's Kit",
"SW5E.ToolMunitionsKit": "Munitions Kit",
"SW5E.ToolPoisonKit": "Poisoner's Kit",
"SW5E.ToolScavengingKit": "Scavenging Kit",
"SW5E.ToolSecurityKit": "Security Kit",
"SW5E.ToolSlicerKit": "Slicer's Kit",
"SW5E.ToolSpiceKit": "Spicer's Kit",
"SW5E.ToolGamingSet": "Gaming Set",
"SW5E.ToolMusicalInstrument": "Musical Instrument",
"SW5E.ToolVehicle": "Vehicle (Land or Water)",
"SW5E.TraitArmorProf": "Armor Proficiencies",
"SW5E.TraitSave": "Update",
"SW5E.TraitSelectorSpecial": "Special (Split with Semi-Colon)",
"SW5E.TraitToolProf": "Tool Proficiencies",
"SW5E.TraitWeaponProf": "Weapon Proficiencies",
@ -718,6 +761,20 @@
"SW5E.Usage": "Usage",
"SW5E.Use": "Use",
"SW5E.Uses": "Uses",
"SW5E.Vehicle": "Vehicle",
"SW5E.VehicleActionStations": "Action Stations",
"SW5E.VehicleActionThresholds": "Action Thresholds",
"SW5E.VehicleCargo": "Cargo",
"SW5E.VehicleCargoCapacity": "Cargo Capacity",
"SW5E.VehicleCargoCrew": "Cargo & Crew",
"SW5E.VehicleCreatureCapacity": "Creature Capacity",
"SW5E.VehicleCrew": "Crew",
"SW5E.VehicleCrewed": "Crewed",
"SW5E.VehicleCrewAction": "Crew Action",
"SW5E.VehicleEquipment": "Vehicle Equipment",
"SW5E.VehicleMishap": "Mishap",
"SW5E.VehiclePassengers": "Passengers",
"SW5E.VehicleUncrewed": "Uncrewed",
"SW5E.Versatile": "Versatile",
"SW5E.VersatileDamage": "Versatile Damage",
"SW5E.VsDC": "vs DC.",
@ -728,43 +785,48 @@
"SW5E.WeaponMartialB": "Martial Blaster",
"SW5E.WeaponMartialLW": "Martial Lightweapon",
"SW5E.WeaponNatural": "Natural",
"SW5E.WeaponPropertiesAmm": "Ammunition",
"SW5E.WeaponPropertiesBur": "Burst",
"SW5E.WeaponPropertiesDef": "Defensive",
"SW5E.WeaponPropertiesDex": "Dexterity Rqmt.",
"SW5E.WeaponPropertiesDrm": "Disarming",
"SW5E.WeaponPropertiesDgd": "Disguised",
"SW5E.WeaponPropertiesDis": "Disintegrate",
"SW5E.WeaponPropertiesDpt": "Disruptive",
"SW5E.WeaponPropertiesDou": "Double",
"SW5E.WeaponPropertiesFin": "Finesse",
"SW5E.WeaponPropertiesFix": "Fixed",
"SW5E.WeaponPropertiesFoc": "Focus",
"SW5E.WeaponPropertiesHvy": "Heavy",
"SW5E.WeaponPropertiesHid": "Hidden",
"SW5E.WeaponPropertiesKen": "Keen",
"SW5E.WeaponPropertiesLgt": "Light",
"SW5E.WeaponPropertiesLum": "Luminous",
"SW5E.WeaponPropertiesPic": "Piercing",
"SW5E.WeaponPropertiesRan": "Range",
"SW5E.WeaponPropertiesRap": "Rapid",
"SW5E.WeaponPropertiesRch": "Reach",
"SW5E.WeaponPropertiesRel": "Reload",
"SW5E.WeaponPropertiesRet": "Returning",
"SW5E.WeaponPropertiesShk": "Shocking",
"SW5E.WeaponPropertiesSpc": "Special",
"SW5E.WeaponPropertiesStr": "Strength Rqmt.",
"SW5E.WeaponPropertiesThr": "Thrown",
"SW5E.WeaponPropertiesTwo": "Two-Handed",
"SW5E.WeaponPropertiesVer": "Versatile",
"SW5E.WeaponPropertiesVic": "Vicious",
"SW5E.WeaponSiege": "Siege",
"SW5E.WeaponPropertiesAmm": "Ammunition",
"SW5E.WeaponPropertiesAut": "Auto",
"SW5E.WeaponPropertiesBur": "Burst",
"SW5E.WeaponPropertiesDef": "Defensive",
"SW5E.WeaponPropertiesDex": "Dexterity Rqmt.",
"SW5E.WeaponPropertiesDir": "Dire",
"SW5E.WeaponPropertiesDrm": "Disarming",
"SW5E.WeaponPropertiesDgd": "Disguised",
"SW5E.WeaponPropertiesDis": "Disintegrate",
"SW5E.WeaponPropertiesDpt": "Disruptive",
"SW5E.WeaponPropertiesDou": "Double",
"SW5E.WeaponPropertiesFin": "Finesse",
"SW5E.WeaponPropertiesFix": "Fixed",
"SW5E.WeaponPropertiesFoc": "Focus",
"SW5E.WeaponPropertiesHvy": "Heavy",
"SW5E.WeaponPropertiesHid": "Hidden",
"SW5E.WeaponPropertiesKen": "Keen",
"SW5E.WeaponPropertiesLgt": "Light",
"SW5E.WeaponPropertiesLum": "Luminous",
"SW5E.WeaponPropertiesMig": "Mighty",
"SW5E.WeaponPropertiesPic": "Piercing",
"SW5E.WeaponPropertiesRan": "Range",
"SW5E.WeaponPropertiesRap": "Rapid",
"SW5E.WeaponPropertiesRch": "Reach",
"SW5E.WeaponPropertiesRel": "Reload",
"SW5E.WeaponPropertiesRet": "Returning",
"SW5E.WeaponPropertiesShk": "Shocking",
"SW5E.WeaponPropertiesSil": "Silent",
"SW5E.WeaponPropertiesSpc": "Special",
"SW5E.WeaponPropertiesStr": "Strength Rqmt.",
"SW5E.WeaponPropertiesThr": "Thrown",
"SW5E.WeaponPropertiesTwo": "Two-Handed",
"SW5E.WeaponPropertiesVer": "Versatile",
"SW5E.WeaponPropertiesVic": "Vicious",
"SW5E.WeaponSimpleVW": "Simple Vibroweapon",
"SW5E.WeaponSimpleProficiency": "Simple Weapons",
"SW5E.WeaponSimpleB": "Simple Blaster",
"SW5E.WeaponSimpleLW": "Simple Lightweapon",
"SW5E.Weight": "Weight",
"SW5E.available": "available",
"SW5E.description": "A comprehensive game system for running games of Dungeons & Dragons 5th Edition in the Foundry VTT environment.",
"SW5E.description": "A comprehensive game system for running games of Star Wars 5th Edition in the Foundry VTT environment.",
"SW5E.of": "of",
"SW5E.power": "power",
"SETTINGS.5eAllowPolymorphingL": "Allow players to polymorph their own actors.",
@ -775,12 +837,19 @@
"SETTINGS.5eAutoPowerTemplateN": "Always place Power Template",
"SETTINGS.5eCurWtL": "Carried currency affects character encumbrance following the rules on PHB pg. 143.",
"SETTINGS.5eCurWtN": "Apply Currency Weight",
"SETTINGS.5eDiagDMG": "Dungeon Master's Guide (5/10/5)",
"SETTINGS.5eDiagEuclidean": "Euclidean (7.07 ft. Diagonal)",
"SETTINGS.5eDiagL": "Configure which diagonal movement rule should be used for games within this system.",
"SETTINGS.5eDiagN": "Diagonal Movement Rule",
"SETTINGS.5eDiagPHB": "Player's Handbook (5/5/5)",
"SETTINGS.5eDiagPHB": "PHB: Equidistant (5/5/5)",
"SETTINGS.5eDiagDMG": "DMG: Alternating (5/10/5)",
"SETTINGS.5eInitTBL": "Append the raw Dexterity ability score to break ties in Initiative.",
"SETTINGS.5eInitTBN": "Initiative Dexterity Tiebreaker",
"SETTINGS.5eNoExpL": "Remove experience bars from character sheets.",
"SETTINGS.5eNoExpN": "Disable Experience Tracking"
"SETTINGS.5eNoExpN": "Disable Experience Tracking",
"SETTINGS.5eRestL": "Configure which rest variant should be used for games within this system.",
"SETTINGS.5eRestN": "Rest Variant",
"SETTINGS.5eRestPHB": "Player's Handbook (LR: 8 hours, SR: 1 hour)",
"SETTINGS.5eRestGritty": "Gritty Realism (LR: 7 days, SR: 8 hours)",
"SETTINGS.5eRestEpic": "Epic Heroism (LR: 1 hour, SR: 1 min)"
}

View file

@ -1,597 +0,0 @@
{
"Star Wars 5th Edition": "Star Wars 5th Edition",
"SW5E.title": "Star Wars 5th Edition",
"SW5E.AbbreviationCR": "CR",
"SW5E.AbbreviationConc": "Conc.",
"SW5E.AbbreviationDC": "DC",
"SW5E.AbbreviationLR": "LR",
"SW5E.AbbreviationLbs": "lbs.",
"SW5E.AbbreviationSR": "SR",
"SW5E.Ability": "Ability",
"SW5E.AbilityCha": "Charisma",
"SW5E.AbilityCon": "Constitution",
"SW5E.AbilityDex": "Dexterity",
"SW5E.AbilityInt": "Intelligence",
"SW5E.AbilityModifier": "Ability Modifier",
"SW5E.AbilityStr": "Strength",
"SW5E.AbilityUseCantUse": "You are not currently able to use this ability!",
"SW5E.AbilityUseCharged": "charged",
"SW5E.AbilityUseConsume": "Consume Available Usage?",
"SW5E.AbilityUseDepleted": "depleted",
"SW5E.AbilityUseHint": "Configure how you would like to use the",
"SW5E.AbilityUseRechargeHint": "This ability uses a recharge mechanic and is currently",
"SW5E.AbilityUseWarnEnd": "available uses per",
"SW5E.AbilityUseWarnStart": "This ability has",
"SW5E.AbilityWis": "Wisdom",
"SW5E.Action": "Action",
"SW5E.ActionAbil": "Ability Check",
"SW5E.ActionHeal": "Healing",
"SW5E.ActionMPAK": "Melee Power Attack",
"SW5E.ActionMWAK": "Melee Weapon Attack",
"SW5E.ActionOther": "Other",
"SW5E.ActionRPAK": "Ranged Power Attack",
"SW5E.ActionRWAK": "Ranged Weapon Attack",
"SW5E.ActionSave": "Saving Throw",
"SW5E.ActionUtil": "Utility",
"SW5E.Add": "Add",
"SW5E.Advantage": "Advantage",
"SW5E.Alignment": "Alignment",
"SW5E.AlignmentCD": "Chaotic Dark",
"SW5E.AlignmentCL": "Chaotic Light",
"SW5E.AlignmentCN": "Chaotic Neutral",
"SW5E.AlignmentLD": "Lawful Dark",
"SW5E.AlignmentLL": "Lawful Light",
"SW5E.AlignmentLN": "Lawful Neutral",
"SW5E.AlignmentND": "Neutral Dark",
"SW5E.AlignmentNL": "Neutral Light",
"SW5E.AlignmentBN": "Balenced Neutral",
"SW5E.ArmorClass": "Armor Class",
"SW5E.ArmorProperties": "Armor Properties",
"SW5E.ArmorProperAbsorptive": "Absorptive",
"SW5E.ArmorProperAgile": "Agile",
"SW5E.ArmorProperAnchor": "Anchor",
"SW5E.ArmorProperAvoidant": "Avoidant",
"SW5E.ArmorProperBarbed": "Barbed",
"SW5E.ArmorProperCharging": "Charging",
"SW5E.ArmorProperConcealing": "Concealing",
"SW5E.ArmorProperCumbersome": "Cumbersome",
"SW5E.ArmorProperGauntleted": "Gauntleted",
"SW5E.ArmorProperImbalanced": "Imbalanced",
"SW5E.ArmorProperImpermeable": "Impermeable",
"SW5E.ArmorProperInsulated": "Insulated",
"SW5E.ArmorProperInterlocking": "Interlocking",
"SW5E.ArmorProperLambent": "Lambent",
"SW5E.ArmorProperLightweight": "Lightweight",
"SW5E.ArmorProperMagnetic": "Magnetic",
"SW5E.ArmorProperObscured": "Obscured",
"SW5E.ArmorProperPowered": "Powered",
"SW5E.ArmorProperReactive": "Reactive",
"SW5E.ArmorProperRegulated": "Regulated",
"SW5E.ArmorProperReinforced": "Reinforced",
"SW5E.ArmorProperResponsive": "Responsive",
"SW5E.ArmorProperRigid": "Rigid",
"SW5E.ArmorProperSilent": "Silent",
"SW5E.ArmorProperSpiked": "Spiked",
"SW5E.ArmorProperSteadfast": "Steadfast",
"SW5E.ArmorProperVersatile": "Versatile",
"SW5E.Attack": "Attack",
"SW5E.Attributes": "Attributes",
"SW5E.Attuned": "Attuned",
"SW5E.Background": "Background",
"SW5E.Biography": "Biography",
"SW5E.BonusAbilityCheck": "Global Ability Check Bonus",
"SW5E.BonusAbilitySave": "Global Saving Throw Bonus",
"SW5E.BonusAbilitySkill": "Global Skill Check Bonus",
"SW5E.BonusAction": "Bonus Action",
"SW5E.BonusMSAttack": "Melee Power Attack Bonus",
"SW5E.BonusMSDamage": "Melee Power Damage Bonus",
"SW5E.BonusMWAttack": "Melee Weapon Attack Bonus",
"SW5E.BonusMWDamage": "Melee Weapon Damage Bonus",
"SW5E.BonusRSAttack": "Ranged Power Attack Bonus",
"SW5E.BonusRSDamage": "Ranged Power Damage Bonus",
"SW5E.BonusRWAttack": "Ranged Weapon Attack Bonus",
"SW5E.BonusRWDamage": "Ranged Weapon Damage Bonus",
"SW5E.BonusSaveForm": "Update Bonuses",
"SW5E.BonusPowerDC": "Global Power DC Bonus",
"SW5E.BonusTitle": "Configure Actor Bonuses",
"SW5E.Bonuses": "Global Bonuses",
"SW5E.BonusesHint": "Define global bonuses as formulas which are added to certain rolls. For example: 1d4 + 2",
"SW5E.BonusesInstructions": "Configure character bonuses which are added to the appropriate dice roll",
"SW5E.ChallengeRating": "Challenge Rating",
"SW5E.Charged": "Charged",
"SW5E.Charges": "Charges",
"SW5E.ChatContextDamage": "Apply Damage",
"SW5E.ChatContextHealing": "Apply Healing",
"SW5E.ChatContextDoubleDamage": "Apply Double Damage",
"SW5E.ChatContextHalfDamage": "Apply Half Damage",
"SW5E.ChatFlavor": "Chat Message Flavor",
"SW5E.ClassLevels": "Class Levels",
"SW5E.ClassName": "Class Name",
"SW5E.ClassSkillsNumber": "Number of Starting Skills",
"SW5E.ClassSkillsChosen": "Chosen Class Skills",
"SW5E.ComponentMaterial": "Material",
"SW5E.ComponentSomatic": "Somatic",
"SW5E.ComponentVerbal": "Verbal",
"SW5E.ConBlinded": "Blinded",
"SW5E.ConCharmed": "Charmed",
"SW5E.ConDeafened": "Deafened",
"SW5E.ConExhaustion": "Exhaustion",
"SW5E.ConFrightened": "Frightened",
"SW5E.ConGrappled": "Grappled",
"SW5E.ConImm": "Condition Immunities",
"SW5E.ConIncapacitated": "Incapacitated",
"SW5E.ConInvisible": "Invisible",
"SW5E.ConParalyzed": "Paralyzed",
"SW5E.ConPetrified": "Petrified",
"SW5E.ConPoisoned": "Poisoned",
"SW5E.ConProne": "Prone",
"SW5E.ConRestrained": "Restrained",
"SW5E.ConShocked": "Shocked",
"SW5E.ConStunned": "Stunned",
"SW5E.ConUnconscious": "Unconscious",
"SW5E.Concentration": "Concentration",
"SW5E.ConsumableFood": "Food",
"SW5E.ConsumablePoison": "Poison",
"SW5E.ConsumableAdrenal": "Adrenal",
"SW5E.ConsumableExplosive": "Explosive",
"SW5E.ConsumableMedpac": "Medpac",
"SW5E.ConsumableTrinket": "Trinket",
"SW5E.ConsumableTechnology": "Technology",
"SW5E.ConsumableAmmunition": "Ammunition",
"SW5E.Consumed": "Consumed",
"SW5E.CostGP": "Cost (GP)",
"SW5E.Critical": "Critical",
"SW5E.CriticalHit": "Critical Hit",
"SW5E.Currency": "Currency",
"SW5E.CurrencyConvert": "Convert All Currency",
"SW5E.CurrencyConvertHint": "Convert all carried currency to the highest possible denomination to reduce the amount of coinage carried by the character. Be wary, this action cannot be undone.",
"SW5E.CurrencyGC": "Credits",
"SW5E.DamImm": "Damage Immunities",
"SW5E.DamRes": "Damage Resistances",
"SW5E.DamVuln": "Damage Vulnerabilities",
"SW5E.Damage": "Damage",
"SW5E.DamageAcid": "Acid",
"SW5E.DamageCold": "Cold",
"SW5E.DamageEnergy": "Energy",
"SW5E.DamageFire": "Fire",
"SW5E.DamageForce": "Force",
"SW5E.DamageIon": "ION",
"SW5E.DamageKinetic": "Kinetic",
"SW5E.DamageLightning": "Lightning",
"SW5E.DamageNecrotic": "Necrotic",
"SW5E.DamagePoison": "Poison",
"SW5E.DamagePsychic": "Psychic",
"SW5E.DamageSonic": "Sonic",
"SW5E.Day": "Day",
"SW5E.DeathSave": "Death Saves",
"SW5E.Default": "Default",
"SW5E.DefaultAbilityCheck": "Default Ability Check",
"SW5E.Description": "Description",
"SW5E.Details": "Details",
"SW5E.Disadvantage": "Disadvantage",
"SW5E.DistAny": "Any",
"SW5E.DistFt": "Feet",
"SW5E.DistMi": "Miles",
"SW5E.DistSelf": "Self",
"SW5E.DistTouch": "Touch",
"SW5E.Duration": "Duration",
"SW5E.EquipmentBonus": "Magical Bonus",
"SW5E.EquipmentClothing": "Clothing",
"SW5E.EquipmentHeavy": "Heavy Armor",
"SW5E.EquipmentLight": "Light Armor",
"SW5E.EquipmentMedium": "Medium Armor",
"SW5E.EquipmentNatural": "Natural Armor",
"SW5E.EquipmentShield": "Shield",
"SW5E.EquipmentShieldProficiency": "Shields",
"SW5E.EquipmentTrinket": "Trinket",
"SW5E.Equipped": "Equipped",
"SW5E.Exhaustion": "Exhaustion",
"SW5E.Expertise": "Expertise",
"SW5E.FeatureActionRecharge": "Action Recharge",
"SW5E.ItemTypeClass": "Class",
"SW5E.ItemTypeClassPl": "Class Levels",
"SW5E.ItemTypeConsumable": "Consumable",
"SW5E.ItemTypeConsumablePl": "Consumables",
"SW5E.ItemTypeContainer": "Container",
"SW5E.ItemTypeContainerPl": "Containers",
"SW5E.ItemTypeEquipment": "Equipment",
"SW5E.ItemTypeEquipmentPl": "Equipment",
"SW5E.ItemTypeLoot": "Loot",
"SW5E.ItemTypeLootPl": "Loot",
"SW5E.ItemTypeSpecies": "Species",
"SW5E.ItemTypeSpeciesPl": "Species",
"SW5E.ItemTypeTool": "Tool",
"SW5E.ItemTypeToolPl": "Tools",
"SW5E.ItemTypePower": "Power",
"SW5E.ItemTypePowerPl": "Powers",
"SW5E.ItemTypeWeapon": "Weapon",
"SW5E.ItemTypeWeaponPl": "Weapons",
"SW5E.FeatureActive": "Active Abilities",
"SW5E.FeatureAdd": "Create Feature",
"SW5E.FeatureAttack": "Feature Attack",
"SW5E.FeaturePassive": "Passive Abilities",
"SW5E.FeatureRechargeOn": "Recharge On",
"SW5E.FeatureRechargeResult": "1d6 Result",
"SW5E.FeatureUsage": "Feature Usage",
"SW5E.Features": "Features",
"SW5E.Filter": "Filter",
"SW5E.FilterNoPowers": "No powers found for this set of filters.",
"SW5E.NoPowerLevels": "This character has no powercaster levels, but you may add powers manually.",
"SW5E.FlagsInstructions": "Configure character features and traits which fine-tune behaviors of the SW5e system.",
"SW5E.FlagsSave": "Update Special Traits",
"SW5E.FlagsTitle": "Configure Special Traits",
"SW5E.Flat": "Flat",
"SW5E.Formula": "Formula",
"SW5E.GrantedAbilities": "Granted Abilities",
"SW5E.HalfProficient": "Half Proficient",
"SW5E.Healing": "Healing",
"SW5E.HealingTemp": "Healing (Temporary)",
"SW5E.Health": "Health",
"SW5E.HealthFormula": "Health Formula",
"SW5E.HitDice": "Hit Dice",
"SW5E.HitDiceUsed": "Hit Dice Used",
"SW5E.Identified": "Identified",
"SW5E.Initiative": "Initiative",
"SW5E.Inspiration": "Inspiration",
"SW5E.Inventory": "Inventory",
"SW5E.ItemActionType": "Action Type",
"SW5E.ItemActivationCondition": "Activation Condition",
"SW5E.ItemActivationCost": "Activation Cost",
"SW5E.ItemAttackBonus": "Attack Roll Bonus",
"SW5E.ItemConsumableActivation": "Consumable Activation",
"SW5E.ItemConsumableDetails": "Consumable Details",
"SW5E.ItemConsumableStatus": "Consumable Status",
"SW5E.ItemConsumableType": "Consumable Type",
"SW5E.ItemConsumableUsage": "Consumable Usage",
"SW5E.ItemConsumeOnUse": "Consume on Use",
"SW5E.ItemContainerCapacity": "Capacity",
"SW5E.ItemContainerCapacityItems": "Items",
"SW5E.ItemContainerCapacityType": "Capacity Type",
"SW5E.ItemContainerCapacityWeight": "Weight",
"SW5E.ItemContainerDetails": "Container Details",
"SW5E.ItemContainerProperties": "Container Properties",
"SW5E.ItemContainerWeightless": "Weightless Contents",
"SW5E.ItemCreate": "Create Item",
"SW5E.ItemDelete": "Delete Item",
"SW5E.ItemDestroyEmpty": "Destroy on Empty",
"SW5E.ItemEdit": "Edit Item",
"SW5E.ItemEquipmentAction": "Equipment Action",
"SW5E.ItemEquipmentDetails": "Equipment Details",
"SW5E.ItemEquipmentDexMod": "Max. Dexterity Modifier",
"SW5E.ItemEquipmentStatus": "Equipment Status",
"SW5E.ItemEquipmentStealthDisav": "Imposes Stealth Disadvantage",
"SW5E.ItemEquipmentType": "Equipment Type",
"SW5E.ItemEquipmentUsage": "Equipment Usage",
"SW5E.ItemName": "Item Name",
"SW5E.ItemRequiredStr": "Required Strength",
"SW5E.ItemToolProficiency": "Tool Proficiency",
"SW5E.ItemWeaponAttack": "Weapon Attack",
"SW5E.ItemWeaponDetails": "Weapon Details",
"SW5E.ItemWeaponProperties": "Weapon Properties",
"SW5E.ItemWeaponStatus": "Weapon Status",
"SW5E.ItemWeaponType": "Weapon Type",
"SW5E.ItemWeaponUsage": "Weapon Usage",
"SW5E.JackOfAllTrades": "Jack of all Trades",
"SW5E.LairAct": "Lair Action",
"SW5E.Languages": "Languages",
"SW5E.LanguagesBasic": "Basic",
"SW5E.LanguagesBinary": "Binary",
"SW5E.LanguagesBith": "Bith",
"SW5E.LanguagesBocce": "Bocce",
"SW5E.LanguagesBothese": "Bothese",
"SW5E.LanguagesCartharese": "Catharese",
"SW5E.LanguagesCheunh": "Cheunh",
"SW5E.LanguagesDurese": "Durese",
"SW5E.LanguagesDug": "Dug",
"SW5E.LanguagesEwokese": "Ewokese",
"SW5E.LanguagesGamorrese": "Gamorrese",
"SW5E.LanguagesGeonosian": "Geonosian",
"SW5E.LanguagesHapan": "Hapan",
"SW5E.LanguagesHuttese": "Huttese",
"SW5E.LanguagesJawaese": "Jawaese",
"SW5E.LanguagesKaleesh": "Kaleesh",
"SW5E.LanguagesKaminoan": "Kaminoan",
"SW5E.LanguagesKelDor": "Kel Dor",
"SW5E.LanguagesMandoa": "Mando'a",
"SW5E.LanguagesMonCal": "Mon Cal",
"SW5E.LanguagesPakPak": "Pak Pak",
"SW5E.LanguagesRodese": "Rodese",
"SW5E.LanguagesSith": "Sith",
"SW5E.LanguagesTogruti": "Togruti",
"SW5E.LanguagesDosh": "Dosh",
"SW5E.LanguagesTwileki": "Twi'leki",
"SW5E.LanguagesTusken": "Tusken",
"SW5E.LanguagesShyriiwook": "Shyriiwook",
"SW5E.LanguagesZabraki": "Zabraki",
"SW5E.LanguagesVong": "Vong",
"SW5E.LegAct": "Legd. Actions",
"SW5E.LegRes": "Legd. Resistance",
"SW5E.Level": "Level",
"SW5E.LevelScaling": "Level Scaling",
"SW5E.LimitedUses": "Limited Uses",
"SW5E.LongRest": "Long Rest",
"SW5E.Max": "Max",
"SW5E.Modifier": "Modifier",
"SW5E.Name": "Character Name",
"SW5E.NoCharges": "No Charges",
"SW5E.None": "None",
"SW5E.Normal": "Normal",
"SW5E.NotProficient": "Not Proficient",
"SW5E.OtherFormula": "Other Formula",
"SW5E.PlaceTemplate": "Place Measured Template",
"SW5E.Polymorph": "Polymorph",
"SW5E.PolymorphAcceptSettings": "Custom Settings",
"SW5E.PolymorphKeepBio": "Keep biography",
"SW5E.PolymorphKeepClass": "Keep proficiency bonus (leaves class items in sheet)",
"SW5E.PolymorphKeepFeats": "Keep features",
"SW5E.PolymorphKeepItems": "Keep equipment",
"SW5E.PolymorphKeepMental": "Keep mental ability scores (Int, Wis, Cha)",
"SW5E.PolymorphKeepPhysical": "Keep physical ability scores (Str, Dex, Con)",
"SW5E.PolymorphKeepSaves": "Keep saving throw proficiencies",
"SW5E.PolymorphKeepSkills": "Keep skill proficiencies",
"SW5E.PolymorphKeepPowers": "Keep powers",
"SW5E.PolymorphKeepVision": "Keep vision (character and token)",
"SW5E.PolymorphMergeSaves": "Merge saving throw proficiencies (take the highest)",
"SW5E.PolymorphMergeSkills": "Merge skill proficiencies (take the highest)",
"SW5E.PolymorphPromptTitle": "Transforming Actor",
"SW5E.PolymorphRestoreTransformation": "Restore Transformation",
"SW5E.PolymorphTmpClass": "Temporary Class",
"SW5E.PolymorphTokens": "Transform all linked tokens?",
"SW5E.PolymorphWildShape": "Wild Shape",
"SW5E.Concentrated": "Concentrate",
"SW5E.Price": "Price",
"SW5E.Proficiency": "Proficiency",
"SW5E.Proficient": "Proficient",
"SW5E.Quantity": "Quantity",
"SW5E.Species": "Species",
"SW5E.Range": "Range",
"SW5E.Rarity": "Rarity",
"SW5E.Reaction": "Reaction",
"SW5E.RequiredMaterials": "Required Materials",
"SW5E.Requirements": "Requirements",
"SW5E.ResourcePrimary": "Resource 1",
"SW5E.ResourceSecondary": "Resource 2",
"SW5E.ResourceTertiary": "Resource 3",
"SW5E.RestL": "L. Rest",
"SW5E.RestS": "S. Rest",
"SW5E.Ritual": "Ritual",
"SW5E.Roll": "Roll",
"SW5E.RollExample": "e.g. +1d4",
"SW5E.RollMode": "Roll Mode",
"SW5E.RollSituationalBonus": "Situational Bonus?",
"SW5E.Save": "Save",
"SW5E.ScalingFormula": "Scaling Formula",
"SW5E.SchoolLgt": "Light",
"SW5E.SchoolUni": "Universal",
"SW5E.SchoolDrk": "Dark",
"SW5E.SchoolTec": "Tech",
"SW5E.SchoolEnh": "Enhancement",
"SW5E.SenseBS": "Blindsight",
"SW5E.SenseDV": "Darkvision",
"SW5E.SenseTR": "Truesight",
"SW5E.SenseTS": "Tremorsense",
"SW5E.Senses": "Senses",
"SW5E.ShortRest": "Short Rest",
"SW5E.ShortRestHint": "Take a short rest? On a short rest you may spend remaining Hit Dice and recover primary or secondary resources.",
"SW5E.ShortRestNoHD": "No Hit Dice remaining",
"SW5E.ShortRestSelect": "Select Dice to Roll",
"SW5E.Size": "Size",
"SW5E.SizeGargantuan": "Gargantuan",
"SW5E.SizeHuge": "Huge",
"SW5E.SizeLarge": "Large",
"SW5E.SizeMedium": "Medium",
"SW5E.SizeSmall": "Small",
"SW5E.SizeTiny": "Tiny",
"SW5E.SkillAcr": "Acrobatics",
"SW5E.SkillAni": "Animal Handling",
"SW5E.SkillAth": "Athletics",
"SW5E.SkillDec": "Deception",
"SW5E.SkillIns": "Insight",
"SW5E.SkillInv": "Investigation",
"SW5E.SkillItm": "Intimidation",
"SW5E.SkillLor": "Lore",
"SW5E.SkillMed": "Medicine",
"SW5E.SkillNat": "Nature",
"SW5E.SkillPer": "Persuasion",
"SW5E.SkillPrc": "Perception",
"SW5E.SkillPrf": "Performance",
"SW5E.SkillPil": "Piloting",
"SW5E.SkillSlt": "Sleight of Hand",
"SW5E.SkillSte": "Stealth",
"SW5E.SkillSur": "Survival",
"SW5E.SkillTec": "Technology",
"SW5E.Slots": "Slots",
"SW5E.Source": "Source",
"SW5E.Special": "Special",
"SW5E.SpecialTraits": "Special Traits",
"SW5E.Speed": "Speed",
"SW5E.SpeedSpecial": "Special Movement",
"SW5E.PowerAbility": "Powercasting Ability",
"SW5E.PowerAdd": "Add Power",
"SW5E.PowerAtWill": "At-Will",
"SW5E.PowerCastConsume": "Consume Power Slot?",
"SW5E.PowerCastHint": "Configure how you would like to cast the",
"SW5E.PowerCastNoSlots": "You have no available power slots to cast this power",
"SW5E.PowerCastUpcast": "Cast at Level",
"SW5E.PowercasterLevel": "Powercaster Level",
"SW5E.PowerCastingHeader": "Power Casting",
"SW5E.Powercasting": "Powercasting",
"SW5E.PowerComponents": "Power Components",
"SW5E.PowerCreate": "Create Power",
"SW5E.PowerDC": "Power DC",
"SW5E.PowerDetails": "Power Details",
"SW5E.PowerEffects": "Power Effects",
"SW5E.PowerLevel": "Power Level",
"SW5E.PowerLevel0": "At-Will",
"SW5E.PowerLevel1": "1st Level",
"SW5E.PowerLevel2": "2nd Level",
"SW5E.PowerLevel3": "3rd Level",
"SW5E.PowerLevel4": "4th Level",
"SW5E.PowerLevel5": "5th Level",
"SW5E.PowerLevel6": "6th Level",
"SW5E.PowerLevel7": "7th Level",
"SW5E.PowerLevel8": "8th Level",
"SW5E.PowerLevel9": "9th Level",
"SW5E.PowerLevelPact": "Pact Slot",
"SW5E.PowerMaterials": "Powercasting Materials",
"SW5E.PowerName": "Power Name",
"SW5E.PowerNone": "None",
"SW5E.PowerPrepAtWill": "At-Will",
"SW5E.PowerPrepInnate": "Innate Powercasting",
"SW5E.PowerPrepPrepared": "Prepared",
"SW5E.PowerPrepAlways": "Always Prepared",
"SW5E.PowerConcentrationMode": "Power Concentration Mode",
"SW5E.PowerConcentrating": "Concentrating",
"SW5E.PowerProgArt": "Artificer",
"SW5E.PowerProgFull": "Full Caster",
"SW5E.PowerProgOverride": "Override slots",
"SW5E.PowerProgression": "Power Progression",
"SW5E.PowerSchool": "Power School",
"SW5E.PowerTarget": "Power Target",
"SW5E.PowerUnprepared": "Unprepared",
"SW5E.PowerUsage": "Power Usage",
"SW5E.Powerbook": "Powerbook",
"SW5E.SpeciesTraits": "Species Traits",
"SW5E.SubclassName": "Subclass Name",
"SW5E.Supply": "Supply",
"SW5E.Target": "Target",
"SW5E.TargetAlly": "Ally",
"SW5E.TargetCone": "Cone",
"SW5E.TargetCreature": "Creature",
"SW5E.TargetCube": "Cube",
"SW5E.TargetCylinder": "Cylinder",
"SW5E.TargetEnemy": "Enemy",
"SW5E.TargetLine": "Line",
"SW5E.TargetObject": "Object",
"SW5E.TargetRadius": "Radius",
"SW5E.TargetSelf": "Self",
"SW5E.TargetSpace": "Space",
"SW5E.TargetSphere": "Sphere",
"SW5E.TargetSquare": "Square",
"SW5E.TargetWall": "Wall",
"SW5E.TargetDroid": "Droid",
"SW5E.Temp": "Temp",
"SW5E.TimeDay": "Days",
"SW5E.TimeHour": "Hours",
"SW5E.TimeInst": "Instantaneous",
"SW5E.TimeMinute": "Minutes",
"SW5E.TimeMonth": "Months",
"SW5E.TimePerm": "Permanent",
"SW5E.TimeRound": "Rounds",
"SW5E.TimeTurn": "Turns",
"SW5E.TimeYear": "Years",
"SW5E.ToolArmormech": "Armormech's Tools",
"SW5E.ToolArmstech": "Armstech's Tools",
"SW5E.ToolArtificer": "Artificer's Tools",
"SW5E.ToolArtist": "Artist's Tools",
"SW5E.ToolAstrotech": "Astrotech's Tools",
"SW5E.ToolBiotech": "Biotech's Tools",
"SW5E.ToolConstructor": "Constructor's Tools",
"SW5E.ToolCybertech": "Cybertech's Tools",
"SW5E.ToolJeweler": "Jeweler's Tools",
"SW5E.ToolSurveyor": "Surveyor's Tools",
"SW5E.ToolSynthweaver": "Synthweavers's Tools",
"SW5E.ToolTinker": "Tinker's Tools",
"SW5E.ToolAntitoxkit": "Antitoxkit",
"SW5E.ToolArchaeologistKit": "Archaeologist Kit",
"SW5E.ToolAudiotechKit": "Audiotech's Kit",
"SW5E.ToolBioanalysisKit": "Bioanalysis Kit",
"SW5E.ToolBrewerKit": "Brewer's Kit",
"SW5E.ToolChefKit": "Chef's Kit",
"SW5E.ToolDemolitionKit": "Demolition Kit",
"SW5E.ToolDisguiseKit": "Disguise Kit",
"SW5E.ToolForgeryKit": "Forgery Kit",
"SW5E.ToolMechanicKit": "Mechanic's Kit",
"SW5E.ToolMunitionsKit": "Munitions Kit",
"SW5E.ToolPoisonKit": "Poisoner's Kit",
"SW5E.ToolScavengingKit": "Scavenging Kit",
"SW5E.ToolSecurityKit": "Security Kit",
"SW5E.ToolSlicerKit": "Slicer's Kit",
"SW5E.ToolSpiceKit": "Spicer's Kit",
"SW5E.ToolGamingSet": "Gaming Set",
"SW5E.ToolMusicalInstrument": "Musical Instrument",
"SW5E.ToolVehicle": "Vehicle (Land or Water)",
"SW5E.TraitArmorProf": "Armor Proficiencies",
"SW5E.TraitSelectorSpecial": "Special (Split with Semi-Colon)",
"SW5E.TraitToolProf": "Tool Proficiencies",
"SW5E.TraitWeaponProf": "Weapon Proficiencies",
"SW5E.Type": "Type",
"SW5E.Unequipped": "Not Equipped",
"SW5E.Unlimited": "Unlimited",
"SW5E.Usage": "Usage",
"SW5E.Use": "Use",
"SW5E.Uses": "Uses",
"SW5E.Versatile": "Versatile",
"SW5E.VersatileDamage": "Versatile Damage",
"SW5E.VsDC": "vs DC.",
"SW5E.WeaponAmmo": "Ammunition",
"SW5E.WeaponImprov": "Improvised",
"SW5E.WeaponMartialVW": "Martial Vibroweapon",
"SW5E.WeaponMartialProficiency": "Martial Weapons",
"SW5E.WeaponMartialB": "Martial Blaster",
"SW5E.WeaponMartialLW": "Martial Lightweapon",
"SW5E.WeaponNatural": "Natural",
"SW5E.WeaponPropertiesAmm": "Ammunition",
"SW5E.WeaponPropertiesBur": "Burst",
"SW5E.WeaponPropertiesDef": "Defensive",
"SW5E.WeaponPropertiesDex": "Dexterity Rqmt.",
"SW5E.WeaponPropertiesDrm": "Disarming",
"SW5E.WeaponPropertiesDgd": "Disguised",
"SW5E.WeaponPropertiesDis": "Disintegrate",
"SW5E.WeaponPropertiesDpt": "Disruptive",
"SW5E.WeaponPropertiesDou": "Double",
"SW5E.WeaponPropertiesFin": "Finesse",
"SW5E.WeaponPropertiesFix": "Fixed",
"SW5E.WeaponPropertiesFoc": "Focus",
"SW5E.WeaponPropertiesHvy": "Heavy",
"SW5E.WeaponPropertiesHid": "Hidden",
"SW5E.WeaponPropertiesKen": "Keen",
"SW5E.WeaponPropertiesLgt": "Light",
"SW5E.WeaponPropertiesLum": "Luminous",
"SW5E.WeaponPropertiesPic": "Piercing",
"SW5E.WeaponPropertiesRan": "Range",
"SW5E.WeaponPropertiesRap": "Rapid",
"SW5E.WeaponPropertiesRch": "Reach",
"SW5E.WeaponPropertiesRel": "Reload",
"SW5E.WeaponPropertiesRet": "Returning",
"SW5E.WeaponPropertiesShk": "Shocking",
"SW5E.WeaponPropertiesSpc": "Special",
"SW5E.WeaponPropertiesStr": "Strength Rqmt.",
"SW5E.WeaponPropertiesThr": "Thrown",
"SW5E.WeaponPropertiesTwo": "Two-Handed",
"SW5E.WeaponPropertiesVer": "Versatile",
"SW5E.WeaponPropertiesVic": "Vicious",
"SW5E.WeaponSimpleVW": "Simple Vibroweapon",
"SW5E.WeaponSimpleProficiency": "Simple Weapons",
"SW5E.WeaponSimpleB": "Simple Blaster",
"SW5E.WeaponSimpleLW": "Simple Lightweapon",
"SW5E.Weight": "Weight",
"SW5E.available": "available",
"SW5E.description": "A comprehensive game system for running games of Dungeons & Dragons 5th Edition in the Foundry VTT environment.",
"SW5E.of": "of",
"SW5E.power": "power",
"SETTINGS.5eAllowPolymorphingL": "Allow players to polymorph their own actors.",
"SETTINGS.5eAllowPolymorphingN": "Allow Polymorphing",
"SETTINGS.5eAutoCollapseCardL": "Automatically collapse Item Card descriptions in the Chat Log",
"SETTINGS.5eAutoCollapseCardN": "Collapse Item Cards in Chat",
"SETTINGS.5eAutoPowerTemplateL": "When a power is cast, defaults to begin the process to create the corresponding Measured Template if any (requires TRUSTED or higher player role)",
"SETTINGS.5eAutoPowerTemplateN": "Always place Power Template",
"SETTINGS.5eCurWtL": "Carried currency affects character encumbrance following the rules on PHB pg. 143.",
"SETTINGS.5eCurWtN": "Apply Currency Weight",
"SETTINGS.5eDiagDMG": "Dungeon Master's Guide (5/10/5)",
"SETTINGS.5eDiagL": "Configure which diagonal movement rule should be used for games within this system.",
"SETTINGS.5eDiagN": "Diagonal Movement Rule",
"SETTINGS.5eDiagPHB": "Player's Handbook (5/5/5)",
"SETTINGS.5eInitTBL": "Append the raw Dexterity ability score to break ties in Initiative.",
"SETTINGS.5eInitTBN": "Initiative Dexterity Tiebreaker",
"SETTINGS.5eNoExpL": "Remove experience bars from character sheets.",
"SETTINGS.5eNoExpN": "Disable Experience Tracking"
}

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,14 @@
import {TraitSelector} from "../../apps/trait-selector.js";
import {ActorSheetFlags} from "../../apps/actor-flags.js";
import Item5e from "../../item/entity.js";
import TraitSelector from "../../apps/trait-selector.js";
import ActorSheetFlags from "../../apps/actor-flags.js";
import {SW5E} from '../../config.js';
/**
* Extend the basic ActorSheet class to do all the D&D5e things!
* Extend the basic ActorSheet class to do all the SW5e things!
* This sheet is an Abstract layer which is not used.
*
* @type {ActorSheet}
* @extends {ActorSheet}
*/
export class ActorSheet5e extends ActorSheet {
export default class ActorSheet5e extends ActorSheet {
constructor(...args) {
super(...args);
@ -19,7 +19,8 @@ export class ActorSheet5e extends ActorSheet {
this._filters = {
inventory: new Set(),
powerbook: new Set(),
features: new Set()
features: new Set(),
effects: new Set()
};
}
@ -31,12 +32,20 @@ export class ActorSheet5e extends ActorSheet {
scrollY: [
".inventory .inventory-list",
".features .inventory-list",
".powerbook .inventory-list"
".powerbook .inventory-list",
".effects .inventory-list"
],
tabs: [{navSelector: ".tabs", contentSelector: ".sheet-body", initial: "description"}]
});
}
/* -------------------------------------------- */
/** @override */
get template() {
if ( !game.user.isGM && this.actor.limited ) return "systems/sw5e/templates/actors/limited-sheet.html";
return `systems/sw5e/templates/actors/${this.actor.data.type}-sheet.html`;
}
/* -------------------------------------------- */
@ -53,6 +62,7 @@ export class ActorSheet5e extends ActorSheet {
cssClass: isOwner ? "editable" : "locked",
isCharacter: this.entity.data.type === "character",
isNPC: this.entity.data.type === "npc",
isVehicle: this.entity.data.type === 'vehicle',
config: CONFIG.SW5E,
};
@ -74,12 +84,14 @@ export class ActorSheet5e extends ActorSheet {
abl.label = CONFIG.SW5E.abilities[a];
}
// Update skill labels
for ( let [s, skl] of Object.entries(data.actor.data.skills)) {
skl.ability = data.actor.data.abilities[skl.ability].label.substring(0, 3);
skl.icon = this._getProficiencyIcon(skl.value);
skl.hover = CONFIG.SW5E.proficiencyLevels[skl.value];
skl.label = CONFIG.SW5E.skills[s];
// Skills
if (data.actor.data.skills) {
for ( let [s, skl] of Object.entries(data.actor.data.skills)) {
skl.ability = CONFIG.SW5E.abilityAbbreviations[skl.ability];
skl.icon = this._getProficiencyIcon(skl.value);
skl.hover = CONFIG.SW5E.proficiencyLevels[skl.value];
skl.label = CONFIG.SW5E.skills[s];
}
}
// Update traits
@ -88,17 +100,25 @@ export class ActorSheet5e extends ActorSheet {
// Prepare owned items
this._prepareItems(data);
// Prepare active effects
this._prepareEffects(data);
// Return data to the sheet
return data
}
/* -------------------------------------------- */
/**
* Prepare the data structure for traits data like languages, resistances & vulnerabilities, and proficiencies
* @param {object} traits The raw traits data object from the actor data
* @private
*/
_prepareTraits(traits) {
const map = {
"dr": CONFIG.SW5E.damageTypes,
"di": CONFIG.SW5E.damageTypes,
"dv": CONFIG.SW5E.damageTypes,
"dr": CONFIG.SW5E.damageResistanceTypes,
"di": CONFIG.SW5E.damageResistanceTypes,
"dv": CONFIG.SW5E.damageResistanceTypes,
"ci": CONFIG.SW5E.conditionTypes,
"languages": CONFIG.SW5E.languages,
"armorProf": CONFIG.SW5E.armorProficiencies,
@ -127,6 +147,43 @@ export class ActorSheet5e extends ActorSheet {
/* -------------------------------------------- */
/**
* Prepare the data structure for Active Effects which are currently applied to the Actor.
* @param {object} data The object of rendering data which is being prepared
* @private
*/
_prepareEffects(data) {
// Define effect header categories
const categories = {
temporary: {
label: "Temporary Effects",
effects: []
},
passive: {
label: "Passive Effects",
effects: []
},
inactive: {
label: "Inactive Effects",
effects: []
}
};
// Iterate over active effects, classifying them into categories
for ( let e of this.actor.effects ) {
e._getSourceName(); // Trigger a lookup for the source name
if ( e.data.disabled ) categories.inactive.effects.push(e);
else if ( e.isTemporary ) categories.temporary.effects.push(e);
else categories.passive.effects.push(e);
}
// Add the prepared categories of effects to the rendering data
return data.effects = categories;
}
/* -------------------------------------------- */
/**
* Insert a power into the powerbook object when rendering the character sheet
* @param {Object} data The Actor data being prepared
@ -153,18 +210,18 @@ export class ActorSheet5e extends ActorSheet {
};
// Format a powerbook entry for a certain indexed level
const registerSection = (sl, i, label, level={}) => {
const registerSection = (sl, i, label, {prepMode="prepared", value, max, override}={}) => {
powerbook[i] = {
order: i,
label: label,
usesSlots: i > 0,
canCreate: owner && (i >= 1),
canCreate: owner,
canPrepare: (data.actor.type === "character") && (i >= 1),
powers: [],
uses: useLabels[i] || level.value || 0,
slots: useLabels[i] || level.max || 0,
override: level.override || 0,
dataset: {"type": "power", "level": i},
uses: useLabels[i] || value || 0,
slots: useLabels[i] || max || 0,
override: override || 0,
dataset: {"type": "power", "level": prepMode in sections ? 1 : i, "preparation.mode": prepMode},
prop: sl
};
};
@ -177,7 +234,7 @@ export class ActorSheet5e extends ActorSheet {
return max;
}, 0);
// Structure the powerbook for every level up to the maximum which has a slot
// Level-based powercasters have cantrips and leveled slots
if ( maxLevel > 0 ) {
registerSection("power0", 0, CONFIG.SW5E.powerLevels[0]);
for (let lvl = 1; lvl <= maxLevel; lvl++) {
@ -185,9 +242,18 @@ export class ActorSheet5e extends ActorSheet {
registerSection(sl, lvl, CONFIG.SW5E.powerLevels[lvl], levels[sl]);
}
}
// Pact magic users have cantrips and a pact magic section
if ( levels.pact && levels.pact.max ) {
registerSection("power0", 0, CONFIG.SW5E.powerLevels[0]);
registerSection("pact", sections.pact, CONFIG.SW5E.powerPreparationModes.pact, levels.pact);
if ( !powerbook["0"] ) registerSection("power0", 0, CONFIG.SW5E.powerLevels[0]);
const l = levels.pact;
const config = CONFIG.SW5E.powerPreparationModes.pact;
registerSection("pact", sections.pact, config, {
prepMode: "pact",
value: l.value,
max: l.max,
override: l.override
});
}
// Iterate over every power item, adding powers to the powerbook by section
@ -196,17 +262,24 @@ export class ActorSheet5e extends ActorSheet {
let s = power.data.level || 0;
const sl = `power${s}`;
// Powercasting mode specific headings
// Specialized powercasting modes (if they exist)
if ( mode in sections ) {
s = sections[mode];
if ( !powerbook[s] ){
registerSection(sl, s, CONFIG.SW5E.powerPreparationModes[mode], levels[mode]);
const l = levels[mode] || {};
const config = CONFIG.SW5E.powerPreparationModes[mode];
registerSection(mode, s, config, {
prepMode: mode,
value: l.value,
max: l.max,
override: l.override
});
}
}
// Higher-level power headings
// Sections for higher-level powers which the caster "should not" have, but power items exist for
else if ( !powerbook[s] ) {
registerSection(sl, s, CONFIG.SW5E.powerLevels[s], levels[sl]);
registerSection(sl, s, CONFIG.SW5E.powerLevels[s], {levels: levels[sl]});
}
// Add the power to the relevant heading
@ -252,7 +325,7 @@ export class ActorSheet5e extends ActorSheet {
// Equipment-specific filters
if ( filters.has("equipped") ) {
if (data.equipped && data.equipped !== true) return false;
if ( data.equipped !== true ) return false;
}
return true;
});
@ -295,8 +368,10 @@ export class ActorSheet5e extends ActorSheet {
// Editable Only Listeners
if ( this.isEditable ) {
// Relative updates for numeric fields
html.find('input[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this));
// Input focus and update
const inputs = html.find("input");
inputs.focus(ev => ev.currentTarget.select());
inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this));
// Ability Proficiency
html.find('.ability-proficiency').click(this._onToggleAbilityProficiency.bind(this));
@ -316,6 +391,10 @@ export class ActorSheet5e extends ActorSheet {
html.find('.item-delete').click(this._onItemDelete.bind(this));
html.find('.item-uses input').click(ev => ev.target.select()).change(this._onUsesChange.bind(this));
html.find('.slot-max-override').click(this._onPowerSlotOverride.bind(this));
// Active Effect management
html.find(".effect-control").click(this._onManageActiveEffect.bind(this));
}
// Owner Only Listeners
@ -328,14 +407,6 @@ export class ActorSheet5e extends ActorSheet {
// Roll Skill Checks
html.find('.skill-name').click(this._onRollSkillCheck.bind(this));
// Item Dragging
let handler = ev => this._onDragItemStart(ev);
html.find('li.item').each((i, li) => {
if ( li.classList.contains("inventory-header") ) return;
li.setAttribute("draggable", true);
li.addEventListener("dragstart", handler, false);
});
// Item Rolling
html.find('.item .item-image').click(event => this._onItemRoll(event));
html.find('.item .item-recharge').click(event => this._onItemRecharge(event));
@ -424,37 +495,9 @@ export class ActorSheet5e extends ActorSheet {
/* -------------------------------------------- */
/** @override */
async _onDrop (event) {
event.preventDefault();
// Get dropped data
let data;
try {
data = JSON.parse(event.dataTransfer.getData('text/plain'));
} catch (err) {
return false;
}
// Handle a polymorph
if (data && (data.type === "Actor")) {
if (game.user.isGM || (game.settings.get('sw5e', 'allowPolymorphing') && this.actor.owner)) {
return this._onDropPolymorph(event, data);
}
}
// Call parent on drop logic
return super._onDrop(event);
}
/* -------------------------------------------- */
/**
* Handle dropping an Actor on the sheet to trigger a Polymorph workflow
* @param {DragEvent} event The drop event
* @param {Object} data The data transfer
* @private
*/
async _onDropPolymorph(event, data) {
async _onDropActor(event, data) {
const canPolymorph = game.user.isGM || (this.actor.owner && game.settings.get('sw5e', 'allowPolymorphing'));
if ( !canPolymorph ) return false;
// Get the target actor
let sourceActor = null;
@ -521,6 +564,23 @@ export class ActorSheet5e extends ActorSheet {
}).render(true);
}
/* -------------------------------------------- */
/** @override */
async _onDropItemCreate(itemData) {
// Create a Consumable power scroll on the Inventory tab
if ( (itemData.type === "power") && (this._tabs[0].active === "inventory") ) {
const scroll = await Item5e.createScrollFromPower(itemData);
itemData = scroll.data;
}
// Create the owned item as normal
// TODO remove conditional logic in 0.7.x
if (isNewerVersion(game.data.version, "0.6.9")) return super._onDropItemCreate(itemData);
else return this.actor.createEmbeddedEntity("OwnedItem", itemData);
}
/* -------------------------------------------- */
/**
@ -634,7 +694,7 @@ export class ActorSheet5e extends ActorSheet {
const header = event.currentTarget;
const type = header.dataset.type;
const itemData = {
name: `New ${type.capitalize()}`,
name: game.i18n.format("SW5E.ItemNew", {type: type.capitalize()}),
type: type,
data: duplicate(header.dataset)
};
@ -671,6 +731,28 @@ export class ActorSheet5e extends ActorSheet {
/* -------------------------------------------- */
/**
* Manage Active Effect instances through the Actor Sheet via effect control buttons.
* @param {MouseEvent} event The left-click event on the effect control
* @private
*/
_onManageActiveEffect(event) {
event.preventDefault();
const a = event.currentTarget;
const li = a.closest(".effect");
const effect = this.actor.effects.get(li.dataset.effectId);
switch ( a.dataset.action ) {
case "edit":
return effect.sheet.render(true);
case "delete":
return effect.delete();
case "toggle":
return effect.update({disabled: !effect.data.disabled});
}
}
/* -------------------------------------------- */
/**
* Handle rolling an Ability check, either a test or a saving throw
* @param {Event} event The originating click event
@ -736,11 +818,8 @@ export class ActorSheet5e extends ActorSheet {
event.preventDefault();
const a = event.currentTarget;
const label = a.parentElement.querySelector("label");
const options = {
name: label.getAttribute("for"),
title: label.innerText,
choices: CONFIG.SW5E[a.dataset.options]
};
const choices = CONFIG.SW5E[a.dataset.options];
const options = { name: a.dataset.target, title: label.innerText, choices };
new TraitSelector(this.actor, options).render(true)
}
@ -760,4 +839,90 @@ export class ActorSheet5e extends ActorSheet {
});
return buttons;
}
}
/* -------------------------------------------- */
/* DEPRECATED */
/* -------------------------------------------- */
/**
* TODO: Remove once 0.7.x is release
* @deprecated since 0.7.0
*/
async _onDrop (event) {
event.preventDefault();
// Get dropped data
let data;
try {
data = JSON.parse(event.dataTransfer.getData('text/plain'));
} catch (err) {
return false;
}
if ( !data ) return false;
// Handle the drop with a Hooked function
const allowed = Hooks.call("dropActorSheetData", this.actor, this, data);
if ( allowed === false ) return;
// Case 1 - Dropped Item
if ( data.type === "Item" ) {
return this._onDropItem(event, data);
}
// Case 2 - Dropped Actor
if ( data.type === "Actor" ) {
return this._onDropActor(event, data);
}
}
/* -------------------------------------------- */
/**
* TODO: Remove once 0.7.x is release
* @deprecated since 0.7.0
*/
async _onDropItem(event, data) {
if ( !this.actor.owner ) return false;
let itemData = await this._getItemDropData(event, data);
// Handle item sorting within the same Actor
const actor = this.actor;
let sameActor = (data.actorId === actor._id) || (actor.isToken && (data.tokenId === actor.token.id));
if (sameActor) return this._onSortItem(event, itemData);
// Create a new item
this._onDropItemCreate(itemData);
}
/* -------------------------------------------- */
/**
* TODO: Remove once 0.7.x is release
* @deprecated since 0.7.0
*/
async _getItemDropData(event, data) {
let itemData = null;
// Case 1 - Import from a Compendium pack
if (data.pack) {
const pack = game.packs.get(data.pack);
if (pack.metadata.entity !== "Item") return;
itemData = await pack.getEntry(data.id);
}
// Case 2 - Data explicitly provided
else if (data.data) {
itemData = data.data;
}
// Case 3 - Import from World entity
else {
let item = game.items.get(data.id);
if (!item) return;
itemData = item.data;
}
// Return a copy of the extracted data
return duplicate(itemData);
}
}

View file

@ -1,37 +1,21 @@
import { ActorSheet5e } from "../sheets/base.js";
import ActorSheet5e from "../sheets/base.js";
/**
* An Actor sheet for NPC type characters in the D&D5E system.
* An Actor sheet for NPC type characters in the SW5E system.
* Extends the base ActorSheet5e class.
* @type {ActorSheet5e}
* @extends {ActorSheet5e}
*/
export class ActorSheet5eNPC extends ActorSheet5e {
export default class ActorSheet5eNPC extends ActorSheet5e {
/**
* Define default rendering options for the NPC sheet
* @return {Object}
*/
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["sw5e", "sheet", "actor", "npc"],
width: 600,
height: 658
height: 680
});
}
/* -------------------------------------------- */
/* Rendering */
/* -------------------------------------------- */
/**
* Get the correct HTML template path to use for rendering this particular sheet
* @type {String}
*/
get template() {
if ( !game.user.isGM && this.actor.limited ) return "systems/sw5e/templates/actors/limited-sheet.html";
return "systems/sw5e/templates/actors/npc-sheet.html";
}
/* -------------------------------------------- */
/**
@ -42,16 +26,16 @@ export class ActorSheet5eNPC extends ActorSheet5e {
// Categorize Items as Features and Powers
const features = {
weapons: { label: "Attacks", items: [] , hasActions: true, dataset: {type: "weapon", "weapon-type": "natural"} },
actions: { label: "Actions", items: [] , hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
passive: { label: "Features", items: [], dataset: {type: "feat"} },
equipment: { label: "Inventory", items: [], dataset: {type: "loot"}}
weapons: { label: game.i18n.localize("SW5E.AttackPl"), items: [] , hasActions: true, dataset: {type: "weapon", "weapon-type": "natural"} },
actions: { label: game.i18n.localize("SW5E.ActionPl"), items: [] , hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
passive: { label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"} },
equipment: { label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}}
};
// Start by classifying items into groups for rendering
let [powers, other] = data.items.reduce((arr, item) => {
item.img = item.img || DEFAULT_TOKEN;
item.isStack = item.data.quantity ? item.data.quantity > 1 : false;
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
item.hasUses = item.data.uses && (item.data.uses.max > 0);
item.isOnCooldown = item.data.recharge && !!item.data.recharge.value && (item.data.recharge.charged === false);
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
@ -86,9 +70,7 @@ export class ActorSheet5eNPC extends ActorSheet5e {
/* -------------------------------------------- */
/**
* Add some extra data when rendering the sheet to reduce the amount of logic required within the template.
*/
/** @override */
getData() {
const data = super.getData();
@ -103,12 +85,7 @@ export class ActorSheet5eNPC extends ActorSheet5e {
/* Object Updates */
/* -------------------------------------------- */
/**
* This method is called upon form submission after form data is validated
* @param event {Event} The initial triggering submission event
* @param formData {Object} The object of validated form data with which to update the object
* @private
*/
/** @override */
_updateObject(event, formData) {
// Format NPC Challenge Rating
@ -126,14 +103,9 @@ export class ActorSheet5eNPC extends ActorSheet5e {
/* Event Listeners and Handlers */
/* -------------------------------------------- */
/**
* Activate event listeners using the prepared sheet HTML
* @param html {HTML} The prepared HTML object ready to be rendered into the DOM
*/
/** @override */
activateListeners(html) {
super.activateListeners(html);
// Rollable Health Formula
html.find(".health .rollable").click(this._onRollHealthFormula.bind(this));
}
@ -152,4 +124,4 @@ export class ActorSheet5eNPC extends ActorSheet5e {
AudioHelper.play({src: CONFIG.sounds.dice});
this.actor.update({"data.attributes.hp.value": hp, "data.attributes.hp.max": hp});
}
}
}

View file

@ -0,0 +1,381 @@
import ActorSheet5e from "./base.js";
/**
* An Actor sheet for Vehicle type actors.
* Extends the base ActorSheet5e class.
* @type {ActorSheet5e}
*/
export default class ActorSheet5eVehicle extends ActorSheet5e {
/**
* Define default rendering options for the Vehicle sheet.
* @returns {Object}
*/
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["sw5e", "sheet", "actor", "vehicle"],
width: 605,
height: 680
});
}
/* -------------------------------------------- */
/**
* Creates a new cargo entry for a vehicle Actor.
*/
static get newCargo() {
return {
name: '',
quantity: 1
};
}
/* -------------------------------------------- */
/**
* Compute the total weight of the vehicle's cargo.
* @param {Number} totalWeight The cumulative item weight from inventory items
* @param {Object} actorData The data object for the Actor being rendered
* @returns {{max: number, value: number, pct: number}}
* @private
*/
_computeEncumbrance(totalWeight, actorData) {
// Compute currency weight
const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0);
totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
// Vehicle weights are an order of magnitude greater.
totalWeight /= CONFIG.SW5E.encumbrance.vehicleWeightMultiplier;
// Compute overall encumbrance
const enc = {
max: actorData.data.attributes.capacity.cargo,
value: Math.round(totalWeight * 10) / 10
};
enc.pct = Math.min(enc.value * 100 / enc.max, 99);
return enc;
}
/* -------------------------------------------- */
/**
* Prepare items that are mounted to a vehicle and require one or more crew
* to operate.
* @private
*/
_prepareCrewedItem(item) {
// Determine crewed status
const isCrewed = item.data.crewed;
item.toggleClass = isCrewed ? 'active' : '';
item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? 'Crewed' : 'Uncrewed'}`);
// Handle crew actions
if (item.type === 'feat' && item.data.activation.type === 'crew') {
item.crew = item.data.activation.cost;
item.cover = game.i18n.localize(`SW5E.${item.data.cover ? 'CoverTotal' : 'None'}`);
if (item.data.cover === .5) item.cover = '½';
else if (item.data.cover === .75) item.cover = '¾';
else if (item.data.cover === null) item.cover = '—';
if (item.crew < 1 || item.crew === null) item.crew = '—';
}
// Prepare vehicle weapons
if (item.type === 'equipment' || item.type === 'weapon') {
item.threshold = item.data.hp.dt ? item.data.hp.dt : '—';
}
}
/* -------------------------------------------- */
/**
* Organize Owned Items for rendering the Vehicle sheet.
* @private
*/
_prepareItems(data) {
const cargoColumns = [{
label: game.i18n.localize('SW5E.Quantity'),
css: 'item-qty',
property: 'quantity',
editable: 'Number'
}];
const equipmentColumns = [{
label: game.i18n.localize('SW5E.Quantity'),
css: 'item-qty',
property: 'data.quantity'
}, {
label: game.i18n.localize('SW5E.AC'),
css: 'item-ac',
property: 'data.armor.value'
}, {
label: game.i18n.localize('SW5E.HP'),
css: 'item-hp',
property: 'data.hp.value',
editable: 'Number'
}, {
label: game.i18n.localize('SW5E.Threshold'),
css: 'item-threshold',
property: 'threshold'
}];
const features = {
actions: {
label: game.i18n.localize('SW5E.ActionPl'),
items: [],
crewable: true,
dataset: {type: 'feat', 'activation.type': 'crew'},
columns: [{
label: game.i18n.localize('SW5E.VehicleCrew'),
css: 'item-crew',
property: 'crew'
}, {
label: game.i18n.localize('SW5E.Cover'),
css: 'item-cover',
property: 'cover'
}]
},
equipment: {
label: game.i18n.localize('SW5E.ItemTypeEquipment'),
items: [],
crewable: true,
dataset: {type: 'equipment', 'armor.type': 'vehicle'},
columns: equipmentColumns
},
passive: {
label: game.i18n.localize('SW5E.Features'),
items: [],
dataset: {type: 'feat'}
},
reactions: {
label: game.i18n.localize('SW5E.ReactionPl'),
items: [],
dataset: {type: 'feat', 'activation.type': 'reaction'}
},
weapons: {
label: game.i18n.localize('SW5E.ItemTypeWeaponPl'),
items: [],
crewable: true,
dataset: {type: 'weapon', 'weapon-type': 'siege'},
columns: equipmentColumns
}
};
const cargo = {
crew: {
label: game.i18n.localize('SW5E.VehicleCrew'),
items: data.data.cargo.crew,
css: 'cargo-row crew',
editableName: true,
dataset: {type: 'crew'},
columns: cargoColumns
},
passengers: {
label: game.i18n.localize('SW5E.VehiclePassengers'),
items: data.data.cargo.passengers,
css: 'cargo-row passengers',
editableName: true,
dataset: {type: 'passengers'},
columns: cargoColumns
},
cargo: {
label: game.i18n.localize('SW5E.VehicleCargo'),
items: [],
dataset: {type: 'loot'},
columns: [{
label: game.i18n.localize('SW5E.Quantity'),
css: 'item-qty',
property: 'data.quantity',
editable: 'Number'
}, {
label: game.i18n.localize('SW5E.Price'),
css: 'item-price',
property: 'data.price',
editable: 'Number'
}, {
label: game.i18n.localize('SW5E.Weight'),
css: 'item-weight',
property: 'data.weight',
editable: 'Number'
}]
}
};
let totalWeight = 0;
for (const item of data.items) {
this._prepareCrewedItem(item);
if (item.type === 'weapon') features.weapons.items.push(item);
else if (item.type === 'equipment') features.equipment.items.push(item);
else if (item.type === 'loot') {
totalWeight += (item.data.weight || 0) * item.data.quantity;
cargo.cargo.items.push(item);
}
else if (item.type === 'feat') {
if (!item.data.activation.type || item.data.activation.type === 'none') {
features.passive.items.push(item);
}
else if (item.data.activation.type === 'reaction') features.reactions.items.push(item);
else features.actions.items.push(item);
}
}
data.features = Object.values(features);
data.cargo = Object.values(cargo);
data.data.attributes.encumbrance = this._computeEncumbrance(totalWeight, data);
}
/* -------------------------------------------- */
/* Event Listeners and Handlers */
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
html.find('.item-toggle').click(this._onToggleItem.bind(this));
html.find('.item-hp input')
.click(evt => evt.target.select())
.change(this._onHPChange.bind(this));
html.find('.item:not(.cargo-row) input[data-property]')
.click(evt => evt.target.select())
.change(this._onEditInSheet.bind(this));
html.find('.cargo-row input')
.click(evt => evt.target.select())
.change(this._onCargoRowChange.bind(this));
if (this.actor.data.data.attributes.actions.stations) {
html.find('.counter.actions, .counter.action-thresholds').hide();
}
}
/* -------------------------------------------- */
/**
* Handle saving a cargo row (i.e. crew or passenger) in-sheet.
* @param event {Event}
* @returns {Promise<Actor>|null}
* @private
*/
_onCargoRowChange(event) {
event.preventDefault();
const target = event.currentTarget;
const row = target.closest('.item');
const idx = Number(row.dataset.itemId);
const property = row.classList.contains('crew') ? 'crew' : 'passengers';
// Get the cargo entry
const cargo = duplicate(this.actor.data.data.cargo[property]);
const entry = cargo[idx];
if (!entry) return null;
// Update the cargo value
const key = target.dataset.property || 'name';
const type = target.dataset.dtype;
let value = target.value;
if (type === 'Number') value = Number(value);
entry[key] = value;
// Perform the Actor update
return this.actor.update({[`data.cargo.${property}`]: cargo});
}
/* -------------------------------------------- */
/**
* Handle editing certain values like quantity, price, and weight in-sheet.
* @param event {Event}
* @returns {Promise<Item>}
* @private
*/
_onEditInSheet(event) {
event.preventDefault();
const itemID = event.currentTarget.closest('.item').dataset.itemId;
const item = this.actor.items.get(itemID);
const property = event.currentTarget.dataset.property;
const type = event.currentTarget.dataset.dtype;
let value = event.currentTarget.value;
switch (type) {
case 'Number': value = parseInt(value); break;
case 'Boolean': value = value === 'true'; break;
}
return item.update({[`${property}`]: value});
}
/* -------------------------------------------- */
/**
* Handle creating a new crew or passenger row.
* @param event {Event}
* @returns {Promise<Actor|Item>}
* @private
*/
_onItemCreate(event) {
event.preventDefault();
const target = event.currentTarget;
const type = target.dataset.type;
if (type === 'crew' || type === 'passengers') {
const cargo = duplicate(this.actor.data.data.cargo[type]);
cargo.push(this.constructor.newCargo);
return this.actor.update({[`data.cargo.${type}`]: cargo});
}
return super._onItemCreate(event);
}
/* -------------------------------------------- */
/**
* Handle deleting a crew or passenger row.
* @param event {Event}
* @returns {Promise<Actor|Item>}
* @private
*/
_onItemDelete(event) {
event.preventDefault();
const row = event.currentTarget.closest('.item');
if (row.classList.contains('cargo-row')) {
const idx = Number(row.dataset.itemId);
const type = row.classList.contains('crew') ? 'crew' : 'passengers';
const cargo = duplicate(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx);
return this.actor.update({[`data.cargo.${type}`]: cargo});
}
return super._onItemDelete(event);
}
/* -------------------------------------------- */
/**
* Special handling for editing HP to clamp it within appropriate range.
* @param event {Event}
* @returns {Promise<Item>}
* @private
*/
_onHPChange(event) {
event.preventDefault();
const itemID = event.currentTarget.closest('.item').dataset.itemId;
const item = this.actor.items.get(itemID);
const hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max);
event.currentTarget.value = hp;
return item.update({'data.hp.value': hp});
}
/* -------------------------------------------- */
/**
* Handle toggling an item's crewed status.
* @param event {Event}
* @returns {Promise<Item>}
* @private
*/
_onToggleItem(event) {
event.preventDefault();
const itemID = event.currentTarget.closest('.item').dataset.itemId;
const item = this.actor.items.get(itemID);
const crewed = !!item.data.data.crewed;
return item.update({'data.crewed': !crewed});
}
};

View file

@ -2,7 +2,7 @@
* A specialized Dialog subclass for ability usage
* @type {Dialog}
*/
export class AbilityUseDialog extends Dialog {
export default class AbilityUseDialog extends Dialog {
constructor(item, dialogData={}, options={}) {
super(dialogData, options);
this.options.classes = ["sw5e", "dialog"];
@ -25,40 +25,156 @@ export class AbilityUseDialog extends Dialog {
* @return {Promise}
*/
static async create(item) {
if ( !item.isOwned ) throw new Error("You cannot display an ability usage dialog for an unowned item");
const uses = item.data.data.uses;
const recharge = item.data.data.recharge;
// Prepare data
const actorData = item.actor.data.data;
const itemData = item.data.data;
const uses = itemData.uses || {};
const quantity = itemData.quantity || 0;
const recharge = itemData.recharge || {};
const recharges = !!recharge.value;
// Render the ability usage template
const html = await renderTemplate("systems/sw5e/templates/apps/ability-use.html", {
// Prepare dialog form data
const data = {
item: item.data,
canUse: recharges ? recharge.charged : uses.value > 0,
consume: true,
uses: uses,
recharges: !!recharge.value,
isCharged: recharge.charged,
title: game.i18n.format("SW5E.AbilityUseHint", item.data),
note: this._getAbilityUseNote(item.data, uses, recharge),
hasLimitedUses: uses.max || recharges,
canUse: recharges ? recharge.charged : (quantity > 0 && !uses.value) || uses.value > 0,
hasPlaceableTemplate: game.user.can("TEMPLATE_CREATE") && item.hasAreaTarget,
perLabel: CONFIG.SW5E.limitedUsePeriods[uses.per]
});
errors: []
};
if ( item.data.type === "power" ) this._getPowerData(actorData, itemData, data);
// Render the ability usage template
const html = await renderTemplate("systems/sw5e/templates/apps/ability-use.html", data);
// Create the Dialog and return as a Promise
const icon = data.isPower ? "fa-magic" : "fa-fist-raised";
const label = game.i18n.localize("SW5E.AbilityUse" + (data.isPower ? "Cast" : "Use"));
return new Promise((resolve) => {
let formData = null;
const dlg = new this(item, {
title: `${item.name}: Ability Configuration`,
title: `${item.name}: Usage Configuration`,
content: html,
buttons: {
use: {
icon: '<i class="fas fa-fist-raised"></i>',
label: "Use Ability",
callback: html => formData = new FormData(html[0].querySelector("#ability-use-form"))
icon: `<i class="fas ${icon}"></i>`,
label: label,
callback: html => resolve(new FormData(html[0].querySelector("form")))
}
},
default: "use",
close: () => resolve(formData)
close: () => resolve(null)
});
dlg.render(true);
});
}
/* -------------------------------------------- */
/* Helpers */
/* -------------------------------------------- */
/**
* Get dialog data related to limited power slots
* @private
*/
static _getPowerData(actorData, itemData, data) {
// Determine whether the power may be up-cast
const lvl = itemData.level;
const canUpcast = (lvl > 0) && CONFIG.SW5E.powerUpcastModes.includes(itemData.preparation.mode);
// If can't upcast, return early and don't bother calculating available power slots
if (!canUpcast) {
data = mergeObject(data, { isPower: true, canUpcast });
return;
}
// Determine the levels which are feasible
let lmax = 0;
const powerLevels = Array.fromRange(10).reduce((arr, i) => {
if ( i < lvl ) return arr;
const label = CONFIG.SW5E.powerLevels[i];
const l = actorData.powers["power"+i] || {max: 0, override: null};
let max = parseInt(l.override || l.max || 0);
let slots = Math.clamped(parseInt(l.value || 0), 0, max);
if ( max > 0 ) lmax = i;
arr.push({
level: i,
label: i > 0 ? game.i18n.format('SW5E.PowerLevelSlot', {level: label, n: slots}) : label,
canCast: max > 0,
hasSlots: slots > 0
});
return arr;
}, []).filter(sl => sl.level <= lmax);
// If this character has pact slots, present them as an option for casting the power.
const pact = actorData.powers.pact;
if (pact.level >= lvl) {
powerLevels.push({
level: 'pact',
label: `${game.i18n.format('SW5E.PowerLevelPact', {level: pact.level, n: pact.value})}`,
canCast: true,
hasSlots: pact.value > 0
});
}
const canCast = powerLevels.some(l => l.hasSlots);
// Return merged data
data = mergeObject(data, { isPower: true, canUpcast, powerLevels });
if ( !canCast ) data.errors.push("SW5E.PowerCastNoSlots");
}
/* -------------------------------------------- */
/**
* Get the ability usage note that is displayed
* @private
*/
static _getAbilityUseNote(item, uses, recharge) {
// Zero quantity
const quantity = item.data.quantity;
if ( quantity <= 0 ) return game.i18n.localize("SW5E.AbilityUseUnavailableHint");
// Abilities which use Recharge
if ( !!recharge.value ) {
return game.i18n.format(recharge.charged ? "SW5E.AbilityUseChargedHint" : "SW5E.AbilityUseRechargeHint", {
type: item.type,
})
}
// Does not use any resource
if ( !uses.per || !uses.max ) return "";
// Consumables
if ( item.type === "consumable" ) {
let str = "SW5E.AbilityUseNormalHint";
if ( uses.value > 1 ) str = "SW5E.AbilityUseConsumableChargeHint";
else if ( item.data.quantity === 1 && uses.autoDestroy ) str = "SW5E.AbilityUseConsumableDestroyHint";
else if ( item.data.quantity > 1 ) str = "SW5E.AbilityUseConsumableQuantityHint";
return game.i18n.format(str, {
type: item.data.consumableType,
value: uses.value,
quantity: item.data.quantity,
});
}
// Other Items
else {
return game.i18n.format("SW5E.AbilityUseNormalHint", {
type: item.type,
value: uses.value,
max: uses.max,
per: CONFIG.SW5E.limitedUsePeriods[uses.per]
});
}
}
/* -------------------------------------------- */
static _handleSubmit(formData, item) {
}
}

View file

@ -1,5 +1,9 @@
export class ActorSheetFlags extends BaseEntitySheet {
static get defaultOptions() {
/**
* An application class which provides advanced configuration for special character flags which modify an Actor
* @extends {BaseEntitySheet}
*/
export default class ActorSheetFlags extends BaseEntitySheet {
static get defaultOptions() {
const options = super.defaultOptions;
return mergeObject(options, {
id: "actor-flags",
@ -68,10 +72,10 @@ export class ActorSheetFlags extends BaseEntitySheet {
{name: "data.bonuses.mwak.damage", label: "SW5E.BonusMWDamage"},
{name: "data.bonuses.rwak.attack", label: "SW5E.BonusRWAttack"},
{name: "data.bonuses.rwak.damage", label: "SW5E.BonusRWDamage"},
{name: "data.bonuses.msak.attack", label: "SW5E.BonusMSAttack"},
{name: "data.bonuses.msak.damage", label: "SW5E.BonusMSDamage"},
{name: "data.bonuses.rsak.attack", label: "SW5E.BonusRSAttack"},
{name: "data.bonuses.rsak.damage", label: "SW5E.BonusRSDamage"},
{name: "data.bonuses.mpak.attack", label: "SW5E.BonusMPAttack"},
{name: "data.bonuses.mpak.damage", label: "SW5E.BonusMPDamage"},
{name: "data.bonuses.rpak.attack", label: "SW5E.BonusRPAttack"},
{name: "data.bonuses.rpak.damage", label: "SW5E.BonusRPDamage"},
{name: "data.bonuses.abilities.check", label: "SW5E.BonusAbilityCheck"},
{name: "data.bonuses.abilities.save", label: "SW5E.BonusAbilitySave"},
{name: "data.bonuses.abilities.skill", label: "SW5E.BonusAbilitySkill"},
@ -91,7 +95,7 @@ export class ActorSheetFlags extends BaseEntitySheet {
*/
async _updateObject(event, formData) {
const actor = this.object;
const updateData = expandObject(formData);
let updateData = expandObject(formData);
// Unset any flags which are "false"
let unset = false;
@ -106,7 +110,18 @@ export class ActorSheetFlags extends BaseEntitySheet {
}
}
// Apply the changes
// Clear any bonuses which are whitespace only
for ( let b of Object.values(updateData.data.bonuses ) ) {
for ( let [k, v] of Object.entries(b) ) {
b[k] = v.trim();
}
}
// Diff the data against any applied overrides and apply
// TODO: Remove this logical gate once 0.7.x is release channel
if ( !isNewerVersion("0.7.1", game.data.version) ){
updateData = diffObject(this.object.data, updateData);
}
await actor.update(updateData, {diff: false});
}
}

69
module/apps/long-rest.js Normal file
View file

@ -0,0 +1,69 @@
/**
* A helper Dialog subclass for completing a long rest
* @extends {Dialog}
*/
export default class LongRestDialog extends Dialog {
constructor(actor, dialogData = {}, options = {}) {
super(dialogData, options);
this.actor = actor;
}
/* -------------------------------------------- */
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
template: "systems/sw5e/templates/apps/long-rest.html",
classes: ["sw5e", "dialog"]
});
}
/* -------------------------------------------- */
/** @override */
getData() {
const data = super.getData();
const variant = game.settings.get("sw5e", "restVariant");
data.promptNewDay = variant !== "gritty"; // It's always a new day when resting 1 week
data.newDay = variant === "normal"; // It's probably a new day when resting normally (8 hours)
return data;
}
/* -------------------------------------------- */
/**
* A helper constructor function which displays the Long Rest confirmation dialog and returns a Promise once it's
* workflow has been resolved.
* @param {Actor5e} actor
* @return {Promise}
*/
static async longRestDialog({ actor } = {}) {
return new Promise((resolve, reject) => {
const dlg = new this(actor, {
title: "Long Rest",
buttons: {
rest: {
icon: '<i class="fas fa-bed"></i>',
label: "Rest",
callback: html => {
let newDay = false;
if (game.settings.get("sw5e", "restVariant") === "normal")
newDay = html.find('input[name="newDay"]')[0].checked;
else if(game.settings.get("sw5e", "restVariant") === "gritty")
newDay = true;
resolve(newDay);
}
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Cancel",
callback: reject
}
},
default: 'rest',
close: reject
});
dlg.render(true);
});
}
}

View file

@ -1,8 +1,10 @@
import LongRestDialog from "./long-rest.js";
/**
* A helper Dialog subclass for rolling Hit Dice on short rest
* @type {Dialog}
* @extends {Dialog}
*/
export class ShortRestDialog extends Dialog {
export default class ShortRestDialog extends Dialog {
constructor(actor, dialogData={}, options={}) {
super(dialogData, options);
@ -34,6 +36,8 @@ export class ShortRestDialog extends Dialog {
/** @override */
getData() {
const data = super.getData();
// Determine Hit Dice
data.availableHD = this.actor.data.items.reduce((hd, item) => {
if ( item.type === "class" ) {
const d = item.data;
@ -45,6 +49,11 @@ export class ShortRestDialog extends Dialog {
}, {});
data.canRoll = this.actor.data.data.attributes.hd > 0;
data.denomination = this._denom;
// Determine rest type
const variant = game.settings.get("sw5e", "restVariant");
data.promptNewDay = variant !== "epic"; // It's never a new day when only resting 1 minute
data.newDay = false; // It may be a new day, but not by default
return data;
}
@ -56,7 +65,6 @@ export class ShortRestDialog extends Dialog {
super.activateListeners(html);
let btn = html.find("#roll-hd");
btn.click(this._onRollHitDie.bind(this));
super.activateListeners(html);
}
/* -------------------------------------------- */
@ -83,21 +91,27 @@ export class ShortRestDialog extends Dialog {
* @return {Promise}
*/
static async shortRestDialog({actor}={}) {
return new Promise(resolve => {
return new Promise((resolve, reject) => {
const dlg = new this(actor, {
title: "Short Rest",
buttons: {
rest: {
icon: '<i class="fas fa-bed"></i>',
label: "Rest",
callback: () => resolve(true)
callback: html => {
let newDay = false;
if (game.settings.get("sw5e", "restVariant") === "gritty")
newDay = html.find('input[name="newDay"]')[0].checked;
resolve(newDay);
}
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Cancel",
callback: () => resolve(false)
callback: reject
}
}
},
close: reject
});
dlg.render(true);
});
@ -108,31 +122,12 @@ export class ShortRestDialog extends Dialog {
/**
* A helper constructor function which displays the Long Rest confirmation dialog and returns a Promise once it's
* workflow has been resolved.
* @deprecated
* @param {Actor5e} actor
* @return {Promise}
*/
static async longRestDialog({actor}={}) {
const content = `<p>Take a long rest?</p><p>On a long rest you will recover hit points, half your maximum hit dice,
class resources, limited use item charges, and power slots.</p>`;
return new Promise((resolve, reject) => {
new Dialog({
title: "Long Rest",
content: content,
buttons: {
rest: {
icon: '<i class="fas fa-bed"></i>',
label: "Rest",
callback: resolve
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Cancel",
callback: reject
},
},
default: 'rest',
close: reject
}, {classes: ["sw5e", "dialog"]}).render(true);
});
console.warn("WARNING! ShortRestDialog.longRestDialog has been deprecated, use LongRestDialog.longRestDialog instead.");
return LongRestDialog.longRestDialog(...arguments);
}
}

View file

@ -1,102 +0,0 @@
/**
* A specialized Dialog subclass for casting a spell item at a certain level
* @type {Dialog}
*/
export class SpellCastDialog extends Dialog {
constructor(actor, item, dialogData={}, options={}) {
super(dialogData, options);
this.options.classes = ["dnd5e", "dialog"];
/**
* Store a reference to the Actor entity which is casting the spell
* @type {Actor5e}
*/
this.actor = actor;
/**
* Store a reference to the Item entity which is the spell being cast
* @type {Item5e}
*/
this.item = item;
}
/* -------------------------------------------- */
/* Rendering */
/* -------------------------------------------- */
/**
* A constructor function which displays the Spell Cast Dialog app for a given Actor and Item.
* Returns a Promise which resolves to the dialog FormData once the workflow has been completed.
* @param {Actor5e} actor
* @param {Item5e} item
* @return {Promise}
*/
static async create(actor, item) {
const ad = actor.data.data;
const id = item.data.data;
// Determine whether the spell may be upcast
const lvl = id.level;
const canUpcast = (lvl > 0) && CONFIG.DND5E.spellUpcastModes.includes(id.preparation.mode);
// Determine the levels which are feasible
let lmax = 0;
const spellLevels = Array.fromRange(10).reduce((arr, i) => {
if ( i < lvl ) return arr;
const l = ad.spells["spell"+i] || {max: 0, override: null};
let max = parseInt(l.override || l.max || 0);
let slots = Math.clamped(parseInt(l.value || 0), 0, max);
if ( max > 0 ) lmax = i;
arr.push({
level: i,
label: i > 0 ? `${CONFIG.DND5E.spellLevels[i]} (${slots} Slots)` : CONFIG.DND5E.spellLevels[i],
canCast: canUpcast && (max > 0),
hasSlots: slots > 0
});
return arr;
}, []).filter(sl => sl.level <= lmax);
const pact = ad.spells.pact;
if (pact.level >= lvl) {
// If this character has pact slots, present them as an option for
// casting the spell.
spellLevels.push({
level: 'pact',
label: game.i18n.localize('DND5E.SpellLevelPact')
+ ` (${game.i18n.localize('DND5E.Level')} ${pact.level}) `
+ `(${pact.value} ${game.i18n.localize('DND5E.Slots')})`,
canCast: canUpcast,
hasSlots: pact.value > 0
});
}
const canCast = spellLevels.some(l => l.hasSlots);
// Render the Spell casting template
const html = await renderTemplate("systems/dnd5e/templates/apps/spell-cast.html", {
item: item.data,
canCast: canCast,
canUpcast: canUpcast,
spellLevels,
hasPlaceableTemplate: game.user.can("TEMPLATE_CREATE") && item.hasAreaTarget
});
// Create the Dialog and return as a Promise
return new Promise((resolve, reject) => {
const dlg = new this(actor, item, {
title: `${item.name}: Spell Configuration`,
content: html,
buttons: {
cast: {
icon: '<i class="fas fa-magic"></i>',
label: "Cast",
callback: html => resolve(new FormData(html[0].querySelector("#spell-config-form")))
}
},
default: "cast",
close: reject
});
dlg.render(true);
});
}
}

View file

@ -2,7 +2,7 @@
* A specialized form used to select from a checklist of attributes, traits, or properties
* @extends {FormApplication}
*/
export class TraitSelector extends FormApplication {
export default class TraitSelector extends FormApplication {
/** @override */
static get defaultOptions() {

View file

@ -1,32 +1,40 @@
/**
* Measure the distance between two pixel coordinates
* See BaseGrid.measureDistance for more details
*
* @param {Object} p0 The origin coordinate {x, y}
* @param {Object} p1 The destination coordinate {x, y}
* @param {boolean} gridSpaces Enforce grid distance (if true) vs. direct point-to-point (if false)
* @return {number} The distance between p1 and p0
*/
export const measureDistance = function(p0, p1, {gridSpaces=true}={}) {
if ( !gridSpaces ) return BaseGrid.prototype.measureDistance.bind(this)(p0, p1, {gridSpaces});
let gs = canvas.dimensions.size,
ray = new Ray(p0, p1),
nx = Math.abs(Math.ceil(ray.dx / gs)),
ny = Math.abs(Math.ceil(ray.dy / gs));
/** @override */
export const measureDistances = function(segments, options={}) {
if ( !options.gridSpaces ) return BaseGrid.prototype.measureDistances.call(this, segments, options);
// Get the number of straight and diagonal moves
let nDiagonal = Math.min(nx, ny),
nStraight = Math.abs(ny - nx);
// Track the total number of diagonals
let nDiagonal = 0;
const rule = this.parent.diagonalRule;
const d = canvas.dimensions;
// Alternative DMG Movement
if ( this.parent.diagonalRule === "5105" ) {
let nd10 = Math.floor(nDiagonal / 2);
let spaces = (nd10 * 2) + (nDiagonal - nd10) + nStraight;
return spaces * canvas.dimensions.distance;
}
// Iterate over measured segments
return segments.map(s => {
let r = s.ray;
// Standard PHB Movement
else return (nStraight + nDiagonal) * canvas.scene.data.gridDistance;
// Determine the total distance traveled
let nx = Math.abs(Math.ceil(r.dx / d.size));
let ny = Math.abs(Math.ceil(r.dy / d.size));
// Determine the number of straight and diagonal moves
let nd = Math.min(nx, ny);
let ns = Math.abs(ny - nx);
nDiagonal += nd;
// Alternative DMG Movement
if (rule === "5105") {
let nd10 = Math.floor(nDiagonal / 2) - Math.floor((nDiagonal - nd) / 2);
let spaces = (nd10 * 2) + (nd - nd10) + ns;
return spaces * canvas.dimensions.distance;
}
// Euclidean Measurement
else if (rule === "EUCL") {
return Math.round(Math.hypot(nx, ny) * canvas.scene.data.gridDistance);
}
// Standard PHB Movement
else return (ns + nd) * canvas.scene.data.gridDistance;
});
};
/* -------------------------------------------- */
@ -39,8 +47,8 @@ const _TokenGetBarAttribute = Token.prototype.getBarAttribute;
export const getBarAttribute = function(...args) {
const data = _TokenGetBarAttribute.bind(this)(...args);
if ( data && (data.attribute === "attributes.hp") ) {
data.value += parseInt(data['temp'] || 0);
data.max += parseInt(data['tempmax'] || 0);
data.value += parseInt(getProperty(this.actor.data, "data.attributes.hp.temp") || 0);
data.max += parseInt(getProperty(this.actor.data, "data.attributes.hp.tempmax") || 0);
}
return data;
};

View file

@ -1,19 +1,19 @@
import {Actor5e} from "./actor/entity.js";
/**
* Highlight critical success or failure on d20 rolls
*/
export const highlightCriticalSuccessFailure = function(message, html, data) {
if ( !message.isRoll || !message.isRollVisible || !message.roll.parts.length ) return;
if ( !message.isRoll || !message.isContentVisible ) return;
// Highlight rolls where the first part is a d20 roll
const roll = message.roll;
let d = roll.parts[0];
const isD20Roll = d instanceof Die && (d.faces === 20) && (d.results.length === 1);
if ( !isD20Roll ) return;
if ( !roll.dice.length ) return;
const d = roll.dice[0];
// Ensure it is not a modified roll
const isModifiedRoll = ("success" in d.rolls[0]) || d.options.marginSuccess || d.options.marginFailure;
// Ensure it is an un-modified d20 roll
const isD20 = (d.faces === 20) && ( d.results.length === 1 );
if ( !isD20 ) return;
const isModifiedRoll = ("success" in d.results[0]) || d.options.marginSuccess || d.options.marginFailure;
if ( isModifiedRoll ) return;
// Highlight successes and failures
@ -33,6 +33,7 @@ export const highlightCriticalSuccessFailure = function(message, html, data) {
export const displayChatActionButtons = function(message, html, data) {
const chatCard = html.find(".sw5e.chat-card");
if ( chatCard.length > 0 ) {
html.find(".flavor-text").remove();
// If the user is the message author or the actor owner, proceed
let actor = game.actors.get(data.message.speaker.actor);
@ -60,32 +61,55 @@ export const displayChatActionButtons = function(message, html, data) {
* @return {Array} The extended options Array including new context choices
*/
export const addChatMessageContextOptions = function(html, options) {
let canApply = li => canvas.tokens.controlledTokens.length && li.find(".dice-roll").length;
let canApply = li => {
const message = game.messages.get(li.data("messageId"));
return message.isRoll && message.isContentVisible && canvas.tokens.controlled.length;
};
options.push(
{
name: game.i18n.localize("SW5E.ChatContextDamage"),
icon: '<i class="fas fa-user-minus"></i>',
condition: canApply,
callback: li => Actor5e.applyDamage(li, 1)
callback: li => applyChatCardDamage(li, 1)
},
{
name: game.i18n.localize("SW5E.ChatContextHealing"),
icon: '<i class="fas fa-user-plus"></i>',
condition: canApply,
callback: li => Actor5e.applyDamage(li, -1)
callback: li => applyChatCardDamage(li, -1)
},
{
name: game.i18n.localize("SW5E.ChatContextDoubleDamage"),
icon: '<i class="fas fa-user-injured"></i>',
condition: canApply,
callback: li => Actor5e.applyDamage(li, 2)
callback: li => applyChatCardDamage(li, 2)
},
{
name: game.i18n.localize("SW5E.ChatContextHalfDamage"),
icon: '<i class="fas fa-user-shield"></i>',
condition: canApply,
callback: li => Actor5e.applyDamage(li, 0.5)
callback: li => applyChatCardDamage(li, 0.5)
}
);
return options;
};
};
/* -------------------------------------------- */
/**
* Apply rolled dice damage to the token or tokens which are currently controlled.
* This allows for damage to be scaled by a multiplier to account for healing, critical hits, or resistance
*
* @param {HTMLElement} roll The chat entry which contains the roll data
* @param {Number} multiplier A damage multiplier to apply to the rolled damage.
* @return {Promise}
*/
function applyChatCardDamage(roll, multiplier) {
const amount = roll.find('.dice-total').text();
return Promise.all(canvas.tokens.controlled.map(t => {
const a = t.actor;
return a.applyDamage(amount, multiplier);
}));
}
/* -------------------------------------------- */

35
module/classFeatures.js Normal file
View file

@ -0,0 +1,35 @@
export const ClassFeatures = {
"berserker": {
"archetypes": {
"addicted-approach": {
"label": "Addicted Approach",
"source": "PHB",
"features": {
"3": ["Compendium.sw5e.archetypes.PCwepUZqHYlxr4T3", "Compendium.sw5e.classfeatures.efOA0nrvUqKJOOeP", "Compendium.sw5e.classfeatures.nT6AfpQXSZ4IeChO"],
"6": ["Compendium.sw5e.classfeatures.GbJDWzoTKWL7sEpR"],
"10": ["Compendium.sw5e.classfeatures.3jqPPd5qJBBnonPw"],
"14": ["Compendium.sw5e.classfeatures.xzRNHB2M2HdOZzr7"]
}
}
},
"features": {
"1": ["Compendium.sw5e.classfeatures.IDt6duVrBzL8euRc", "Compendium.sw5e.classfeatures.rPOLy96fW96N2UPg"],
"2": ["Compendium.sw5e.classfeatures.DlYiCiG39R0goG9u", "Compendium.sw5e.classfeatures.FbSpxpXm1xONn0na", "Compendium.sw5e.classfeatures.KDiQ8O2evV2Z1YTo", "Compendium.sw5e.classfeatures.Q1JyHnVs9iIEBs91", "Compendium.sw5e.classfeatures.ROdICoWR82v6A2Rf", "Compendium.sw5e.classfeatures.cdCx5Hvq2rYRMzRj", "Compendium.sw5e.classfeatures.dTdbL8dypa6BAdnP", "Compendium.sw5e.classfeatures.h1uDhP1tEOuvjRw6", "Compendium.sw5e.classfeatures.hMiA075EKBBOL2cv", "Compendium.sw5e.classfeatures.sgJdISZMtwv08WPJ", "Compendium.sw5e.classfeatures.v4CZJ8LBMl5PYZCO"],
"3": ["Compendium.sw5e.classfeatures.kzwSN9SabKgWZZvU"],
"4": ["Compendium.sw5e.classfeatures.9oyy0MMqEws2qoil"],
"5": ["Compendium.sw5e.classfeatures.dPWmHiWmpnhHTsgd"],
"7": ["Compendium.sw5e.classfeatures.Cid5ujSdnooH0vMm", "Compendium.sw5e.classfeatures.WTBhKJgkArQI3Tgv", "Compendium.sw5e.classfeatures.oiT3TJxzRWPKAX9E", "Compendium.sw5e.classfeatures.pMEmIt3NWThbee8k", "Compendium.sw5e.classfeatures.qWV5YogZcpZ3Y3xj"],
"9": ["Compendium.sw5e.classfeatures.bi8G8H5Ur9B3BAyM"],
"11": ["Compendium.sw5e.classfeatures.eWbTifdXJvvXT4CV"],
"13": ["Compendium.sw5e.classfeatures.Hg8zYh1iXL0DGUVq", "Compendium.sw5e.classfeatures.QRnYiJmRk18ekE9v", "Compendium.sw5e.classfeatures.sfEr8ZBFVddlfLeF", "Compendium.sw5e.classfeatures.yGC9VzT840qQWxca"],
"15": ["Compendium.sw5e.classfeatures.YHPUv9lN3nCapAgP"],
"18": ["Compendium.sw5e.classfeatures.fFKNqUAWh0ZOhvRc"],
"20": ["Compendium.sw5e.classfeatures.IWTDawTUf79eWbEV"]
}
},
"consular": {
"features": {
"20": ["Compendium.sw5e.classfeatures.gSGeitc98ItAwhfF"]
}
}
};

View file

@ -1,6 +1,6 @@
/**
* Override the default Initiative formula to customize special behaviors of the D&D5e system.
* Override the default Initiative formula to customize special behaviors of the SW5e system.
* Apply advantage, proficiency, or bonuses where appropriate
* Apply the dexterity score as a decimal tiebreaker if requested
* See Combat._getInitiativeFormula for more detail.
@ -9,8 +9,59 @@ export const _getInitiativeFormula = function(combatant) {
const actor = combatant.actor;
if ( !actor ) return "1d20";
const init = actor.data.data.attributes.init;
const parts = ["1d20", init.mod, (init.prof !== 0) ? init.prof : null, (init.bonus !== 0) ? init.bonus : null];
if ( actor.getFlag("sw5e", "initiativeAdv") ) parts[0] = "2d20kh";
if ( CONFIG.Combat.initiative.tiebreaker ) parts.push(actor.data.data.abilities.dex.value / 100);
let nd = 1;
let mods = "";
if (actor.getFlag("sw5e", "halflingLucky")) mods += "r=1";
if (actor.getFlag("sw5e", "initiativeAdv")) {
nd = 2;
mods += "kh";
}
const parts = [`${nd}d20${mods}`, init.mod, (init.prof !== 0) ? init.prof : null, (init.bonus !== 0) ? init.bonus : null];
// Optionally apply Dexterity tiebreaker
const tiebreaker = game.settings.get("sw5e", "initiativeDexTiebreaker");
if ( tiebreaker ) parts.push(actor.data.data.abilities.dex.value / 100);
return parts.filter(p => p !== null).join(" + ");
};
/* -------------------------------------------- */
/**
* TODO: A temporary shim until 0.7.x becomes stable
* @override
*/
TokenConfig.getTrackedAttributes = function(data, _path=[]) {
// Track the path and record found attributes
const attributes = {
"bar": [],
"value": []
};
// Recursively explore the object
for ( let [k, v] of Object.entries(data) ) {
let p = _path.concat([k]);
// Check objects for both a "value" and a "max"
if ( v instanceof Object ) {
const isBar = ("value" in v) && ("max" in v);
if ( isBar ) attributes.bar.push(p);
else {
const inner = this.getTrackedAttributes(data[k], p);
attributes.bar.push(...inner.bar);
attributes.value.push(...inner.value);
}
}
// Otherwise identify values which are numeric or null
else if ( Number.isNumeric(v) || (v === null) ) {
attributes.value.push(p);
}
}
return attributes;
};

View file

@ -1,839 +0,0 @@
// Namespace D&D5e Configuration Values
export const SW5E = {};
// ASCII Artwork
SW5E.ASCII = `_______________________________
_
| |
___| |_ __ _ _ ____ ____ _ _ __ ___
/ __| __/ _\ | |__\ \ /\ / / _\ | |__/ __|
\__ \ || (_) | | \ V V / (_) | | \__ \
|___/\__\__/_|_| \_/\_/ \__/_|_| |___/
_______________________________`;
/**
* The set of Ability Scores used within the system
* @type {Object}
*/
SW5E.abilities = {
"str": "SW5E.AbilityStr",
"dex": "SW5E.AbilityDex",
"con": "SW5E.AbilityCon",
"int": "SW5E.AbilityInt",
"wis": "SW5E.AbilityWis",
"cha": "SW5E.AbilityCha"
};
/* -------------------------------------------- */
/**
* Character alignment options
* @type {Object}
*/
SW5E.alignments = {
'll': "SW5E.AlignmentLL",
'nl': "SW5E.AlignmentNL",
'cl': "SW5E.AlignmentCL",
'lb': "SW5E.AlignmentLB",
'bn': "SW5E.AlignmentBN",
'cb': "SW5E.AlignmentCB",
'ld': "SW5E.AlignmentLD",
'nd': "SW5E.AlignmentND",
'cd': "SW5E.AlignmentCD"
};
SW5E.weaponProficiencies = {
"sim": "SW5E.WeaponSimpleProficiency",
"bla": "SW5E.WeaponBlasterProficiency"
};
SW5E.toolProficiencies = {
"armor": "SW5E.ToolArmormech",
"arms": "SW5E.ToolArmstech",
"arti": "SW5E.ToolArtificer",
"art": "SW5E.ToolArtist",
"astro": "SW5E.ToolAstrotech",
"bio": "SW5E.ToolBiotech",
"con": "SW5E.ToolConstructor",
"cyb": "SW5E.ToolCybertech",
"jew": "SW5E.ToolJeweler",
"sur": "SW5E.ToolSurveyor",
"syn": "SW5E.ToolSynthweaver",
"tin": "SW5E.ToolTinker",
"ant": "SW5E.ToolAntitoxkit",
"arc": "SW5E.ToolArchaeologistKit",
"aud": "SW5E.ToolAudiotechKit",
"bioa": "SW5E.ToolBioanalysisKit",
"brew": "SW5E.ToolBrewerKit",
"chef": "SW5E.ToolChefKit",
"demo": "SW5E.ToolDemolitionKit",
"disg": "SW5E.ToolDisguiseKit",
"forg": "SW5E.ToolForgeryKit",
"mech": "SW5E.ToolMechanicKit",
"game": "SW5E.ToolGamingSet",
"poi": "SW5E.ToolPoisonKit",
"scav": "SW5E.ToolScavengingKit",
"secur": "SW5E.ToolSecurityKit",
"slic": "SW5E.ToolSlicerKit",
"spice": "SW5E.ToolSpiceKit",
"music": "SW5E.ToolMusicalInstrument",
"vehicle": "SW5E.ToolVehicle"
};
/* -------------------------------------------- */
/**
* This Object defines the various lengths of time which can occur in D&D5e
* @type {Object}
*/
SW5E.timePeriods = {
"inst": "SW5E.TimeInst",
"turn": "SW5E.TimeTurn",
"round": "SW5E.TimeRound",
"minute": "SW5E.TimeMinute",
"hour": "SW5E.TimeHour",
"day": "SW5E.TimeDay",
"month": "SW5E.TimeMonth",
"year": "SW5E.TimeYear",
"perm": "SW5E.TimePerm",
"spec": "SW5E.Special"
};
/* -------------------------------------------- */
/**
* This describes the ways that an ability can be activated
* @type {Object}
*/
SW5E.abilityActivationTypes = {
"none": "SW5E.None",
"action": "SW5E.Action",
"bonus": "SW5E.BonusAction",
"reaction": "SW5E.Reaction",
"minute": SW5E.timePeriods.minute,
"hour": SW5E.timePeriods.hour,
"day": SW5E.timePeriods.day,
"special": SW5E.timePeriods.spec,
"legendary": "SW5E.LegAct",
"lair": "SW5E.LairAct"
};
/* -------------------------------------------- */
// Creature Sizes
SW5E.actorSizes = {
"tiny": "SW5E.SizeTiny",
"sm": "SW5E.SizeSmall",
"med": "SW5E.SizeMedium",
"lg": "SW5E.SizeLarge",
"huge": "SW5E.SizeHuge",
"grg": "SW5E.SizeGargantuan"
};
SW5E.tokenSizes = {
"tiny": 1,
"sm": 1,
"med": 1,
"lg": 2,
"huge": 3,
"grg": 4
};
/* -------------------------------------------- */
/**
* Classification types for item action types
* @type {Object}
*/
SW5E.itemActionTypes = {
"mwak": "SW5E.ActionMWAK",
"rwak": "SW5E.ActionRWAK",
"mpak": "SW5E.ActionMPAK",
"rpak": "SW5E.ActionRPAK",
"save": "SW5E.ActionSave",
"heal": "SW5E.ActionHeal",
"abil": "SW5E.ActionAbil",
"util": "SW5E.ActionUtil",
"other": "SW5E.ActionOther"
};
/* -------------------------------------------- */
SW5E.itemCapacityTypes = {
"items": "SW5E.ItemContainerCapacityItems",
"weight": "SW5E.ItemContainerCapacityWeight"
};
/* -------------------------------------------- */
/**
* Enumerate the lengths of time over which an item can have limited use ability
* @type {Object}
*/
SW5E.limitedUsePeriods = {
"sr": "SW5E.ShortRest",
"lr": "SW5E.LongRest",
"day": "SW5E.Day",
"charges": "SW5E.Charges"
};
/* -------------------------------------------- */
/**
* The set of equipment types for armor, clothing, and other objects which can ber worn by the character
* @type {Object}
*/
SW5E.equipmentTypes = {
"light": "SW5E.EquipmentLight",
"medium": "SW5E.EquipmentMedium",
"heavy": "SW5E.EquipmentHeavy",
"bonus": "SW5E.EquipmentBonus",
"natural": "SW5E.EquipmentNatural",
"shield": "SW5E.EquipmentShield",
"clothing": "SW5E.EquipmentClothing",
"trinket": "SW5E.EquipmentTrinket"
};
/* -------------------------------------------- */
/**
* The set of Armor Proficiencies which a character may have
* @type {Object}
*/
SW5E.armorProficiencies = {
"lgt": SW5E.equipmentTypes.light,
"med": SW5E.equipmentTypes.medium,
"hvy": SW5E.equipmentTypes.heavy,
"shl": "SW5E.EquipmentShieldProficiency"
};
/* -------------------------------------------- */
/**
* Enumerate the valid consumable types which are recognized by the system
* @type {Object}
*/
SW5E.consumableTypes = {
"adrenal": "SW5E.ConsumableAdrenal",
"poison": "SW5E.ConsumablePoison",
"explosive": "SW5E.ConsumableExplosive",
"food": "SW5E.ConsumableFood",
"medpac": "SW5E.ConsumableMedpac",
"technology": "SW5E.ConsumableTechnology",
"ammunition": "SW5E.ConsumableAmmunition",
"trinket": "SW5E.ConsumableTrinket"
};
/* -------------------------------------------- */
/**
* The valid currency denominations supported by the 5e system
* @type {Object}
*/
SW5E.currencies = {
"CR": "SW5E.CurrencyCR",
};
/* -------------------------------------------- */
// Damage Types
SW5E.damageTypes = {
"acid": "SW5E.DamageAcid",
"cold": "SW5E.DamageCold",
"energy": "SW5E.DamageEnergy",
"fire": "SW5E.DamageFire",
"force": "SW5E.DamageForce",
"ion": "SW5E.DamageIon",
"kinetic": "SW5E.DamageKinetic",
"lightning": "SW5E.DamageLightning",
"necrotic": "SW5E.DamageNecrotic",
"poison": "SW5E.DamagePoison",
"psychic": "SW5E.DamagePsychic",
"Sonic": "SW5E.DamageSonic"
};
/* -------------------------------------------- */
// armor Types
SW5E.armorpropertiesTypes = {
"Absorptive": "SW5E.ArmorProperAbsorptive",
"Agile": "SW5E.ArmorProperAgile",
"Anchor": "SW5E.ArmorProperAnchor",
"Avoidant": "SW5E.ArmorProperAvoidant",
"Barbed": "SW5E.ArmorProperBarbed",
"Charging": "SW5E.ArmorProperCharging",
"Concealing": "SW5E.ArmorProperConcealing",
"Cumbersome": "SW5E.ArmorProperCumbersome",
"Gauntleted": "SW5E.ArmorProperGauntleted",
"Imbalanced": "SW5E.ArmorProperImbalanced",
"Impermeable": "SW5E.ArmorProperImpermeable",
"Insulated": "SW5E.ArmorProperInsulated",
"Interlocking": "SW5E.ArmorProperInterlocking",
"Lambent": "SW5E.ArmorProperLambent",
"Lightweight": "SW5E.ArmorProperLightweight",
"Magnetic": "SW5E.ArmorProperMagnetic",
"Obscured": "SW5E.ArmorProperObscured",
"Powered": "SW5E.ArmorProperPowered",
"Reactive": "SW5E.ArmorProperReactive",
"Regulated": "SW5E.ArmorProperRegulated",
"Reinforced": "SW5E.ArmorProperReinforced",
"Responsive": "SW5E.ArmorProperResponsive",
"Rigid": "SW5E.ArmorProperRigid",
"Silent": "SW5E.ArmorProperSilent",
"Spiked": "SW5E.ArmorProperSpiked",
"Steadfast": "SW5E.ArmorProperSteadfast",
"Versatile": "SW5E.ArmorProperVersatile"
};
/* -------------------------------------------- */
SW5E.distanceUnits = {
"none": "SW5E.None",
"self": "SW5E.DistSelf",
"touch": "SW5E.DistTouch",
"ft": "SW5E.DistFt",
"mi": "SW5E.DistMi",
"spec": "SW5E.Special",
"any": "SW5E.DistAny"
};
/* -------------------------------------------- */
/**
* Configure aspects of encumbrance calculation so that it could be configured by modules
* @type {Object}
*/
SW5E.encumbrance = {
currencyPerWeight: 50,
strMultiplier: 15
};
/* -------------------------------------------- */
/**
* This Object defines the types of single or area targets which can be applied in D&D5e
* @type {Object}
*/
SW5E.targetTypes = {
"none": "SW5E.None",
"self": "SW5E.TargetSelf",
"creature": "SW5E.TargetCreature",
"droid": "SW5E.TargetDroid",
"ally": "SW5E.TargetAlly",
"enemy": "SW5E.TargetEnemy",
"object": "SW5E.TargetObject",
"space": "SW5E.TargetSpace",
"radius": "SW5E.TargetRadius",
"sphere": "SW5E.TargetSphere",
"cylinder": "SW5E.TargetCylinder",
"cone": "SW5E.TargetCone",
"square": "SW5E.TargetSquare",
"cube": "SW5E.TargetCube",
"line": "SW5E.TargetLine",
"wall": "SW5E.TargetWall",
};
/* -------------------------------------------- */
/**
* Map the subset of target types which produce a template area of effect
* The keys are SW5E target types and the values are MeasuredTemplate shape types
* @type {Object}
*/
SW5E.areaTargetTypes = {
cone: "cone",
cube: "rect",
cylinder: "circle",
line: "ray",
radius: "circle",
sphere: "circle",
square: "rect",
wall: "ray"
};
/* -------------------------------------------- */
// Healing Types
SW5E.healingTypes = {
"healing": "SW5E.Healing",
"temphp": "SW5E.HealingTemp"
};
/* -------------------------------------------- */
/**
* Enumerate the denominations of hit dice which can apply to classes in the D&D5E system
* @type {Array.<string>}
*/
SW5E.hitDieTypes = ["d6", "d8", "d10", "d12"];
/* -------------------------------------------- */
/**
* Character senses options
* @type {Object}
*/
SW5E.senses = {
"bs": "SW5E.SenseBS",
"dv": "SW5E.SenseDV",
"ts": "SW5E.SenseTS",
"tr": "SW5E.SenseTR"
};
/* -------------------------------------------- */
/**
* The set of skill which can be trained in D&D5e
* @type {Object}
*/
SW5E.skills = {
"acr": "SW5E.SkillAcr",
"ani": "SW5E.SkillAni",
"ath": "SW5E.SkillAth",
"dec": "SW5E.SkillDec",
"ins": "SW5E.SkillIns",
"itm": "SW5E.SkillItm",
"inv": "SW5E.SkillInv",
"lor": "SW5E.SkillLor",
"med": "SW5E.SkillMed",
"nat": "SW5E.SkillNat",
"prc": "SW5E.SkillPrc",
"prf": "SW5E.SkillPrf",
"per": "SW5E.SkillPer",
"pil": "SW5E.SkillPil",
"slt": "SW5E.SkillSlt",
"ste": "SW5E.SkillSte",
"sur": "SW5E.SkillSur",
"tec": "SW5E.SkillTec",
};
/* -------------------------------------------- */
SW5E.powerPreparationModes = {
"always": "SW5E.PowerPrepAlways",
"atwill": "SW5E.PowerPrepAtWill",
"innate": "SW5E.PowerPrepInnate",
"prepared": "SW5E.PowerPrepPrepared"
};
SW5E.powerUpcastModes = ["always"];
SW5E.powerProgression = {
"none": "SW5E.PowerNone",
"full": "SW5E.PowerProgFull",
"artificer": "SW5E.PowerProgArt"
};
/* -------------------------------------------- */
/**
* The available choices for how power damage scaling may be computed
* @type {Object}
*/
SW5E.powerScalingModes = {
"none": "SW5E.PowerNone",
"atwill": "SW5E.PowerAtWill",
"level": "SW5E.PowerLevel"
};
/* -------------------------------------------- */
// Weapon Types
SW5E.weaponTypes = {
"simpleVW": "SW5E.WeaponSimpleVW",
"simpleB": "SW5E.WeaponSimpleB",
"simpleLW": "SW5E.WeaponSimpleLW",
"martialVW": "SW5E.WeaponMartialVW",
"martialB": "SW5E.WeaponMartialB",
"martialLW": "SW5E.WeaponMartialLW",
"natural": "SW5E.WeaponNatural",
"improv": "SW5E.WeaponImprov",
"ammo": "SW5E.WeaponAmmo"
};
/* -------------------------------------------- */
/**
* Define the set of weapon property flags which can exist on a weapon
* @type {Object}
*/
SW5E.weaponProperties = {
"amm": "SW5E.WeaponPropertiesAmm",
"bur": "SW5E.WeaponPropertiesBur",
"def": "SW5E.WeaponPropertiesDef",
"dex": "SW5E.WeaponPropertiesDex",
"drm": "SW5E.WeaponPropertiesDrm",
"dgd": "SW5E.WeaponPropertiesDgd",
"dis": "SW5E.WeaponPropertiesDis",
"dpt": "SW5E.WeaponPropertiesDpt",
"dou": "SW5E.WeaponPropertiesDou",
"hvy": "SW5E.WeaponPropertiesHvy",
"hid": "SW5E.WeaponPropertiesHid",
"fin": "SW5E.WeaponPropertiesFin",
"fix": "SW5E.WeaponPropertiesFix",
"foc": "SW5E.WeaponPropertiesFoc",
"ken": "SW5E.WeaponPropertiesKen",
"lgt": "SW5E.WeaponPropertiesLgt",
"lum": "SW5E.WeaponPropertiesLum",
"pic": "SW5E.WeaponPropertiesPic",
"rap": "SW5E.WeaponPropertiesRap",
"rch": "SW5E.WeaponPropertiesRch",
"rel": "SW5E.WeaponPropertiesRel",
"ret": "SW5E.WeaponPropertiesRet",
"shk": "SW5E.WeaponPropertiesShk",
"spc": "SW5E.WeaponPropertiesSpc",
"str": "SW5E.WeaponPropertiesStr",
"thr": "SW5E.WeaponPropertiesThr",
"two": "SW5E.WeaponPropertiesTwo",
"ver": "SW5E.WeaponPropertiesVer",
"vic": "SW5E.WeaponPropertiesVic"
};
// Power Components
SW5E.powerComponents = {
"V": "SW5E.ComponentVerbal",
"S": "SW5E.ComponentSomatic",
"M": "SW5E.ComponentMaterial"
};
// Power Schools
SW5E.powerSchools = {
"lgt": "SW5E.SchoolLgt",
"uni": "SW5E.SchoolUni",
"drk": "SW5E.SchoolDrk",
"tec": "SW5E.SchoolTec",
"enh": "SW5E.SchoolEnh"
};
// Power Levels
SW5E.powerLevels = {
0: "SW5E.PowerLevel0",
1: "SW5E.PowerLevel1",
2: "SW5E.PowerLevel2",
3: "SW5E.PowerLevel3",
4: "SW5E.PowerLevel4",
5: "SW5E.PowerLevel5",
6: "SW5E.PowerLevel6",
7: "SW5E.PowerLevel7",
8: "SW5E.PowerLevel8",
9: "SW5E.PowerLevel9"
};
/**
* Define the standard slot progression by character level.
* The entries of this array represent the power slot progression for a full power-caster.
* @type {Array[]}
*/
SW5E.SPELL_SLOT_TABLE = [
[2],
[3],
[4, 2],
[4, 3],
[4, 3, 2],
[4, 3, 3],
[4, 3, 3, 1],
[4, 3, 3, 2],
[4, 3, 3, 3, 1],
[4, 3, 3, 3, 2],
[4, 3, 3, 3, 2, 1],
[4, 3, 3, 3, 2, 1],
[4, 3, 3, 3, 2, 1, 1],
[4, 3, 3, 3, 2, 1, 1],
[4, 3, 3, 3, 2, 1, 1, 1],
[4, 3, 3, 3, 2, 1, 1, 1],
[4, 3, 3, 3, 2, 1, 1, 1, 1],
[4, 3, 3, 3, 3, 1, 1, 1, 1],
[4, 3, 3, 3, 3, 2, 1, 1, 1],
[4, 3, 3, 3, 3, 2, 2, 1, 1]
];
/* -------------------------------------------- */
// Polymorph options.
SW5E.polymorphSettings = {
keepPhysical: 'SW5E.PolymorphKeepPhysical',
keepMental: 'SW5E.PolymorphKeepMental',
keepSaves: 'SW5E.PolymorphKeepSaves',
keepSkills: 'SW5E.PolymorphKeepSkills',
mergeSaves: 'SW5E.PolymorphMergeSaves',
mergeSkills: 'SW5E.PolymorphMergeSkills',
keepClass: 'SW5E.PolymorphKeepClass',
keepFeats: 'SW5E.PolymorphKeepFeats',
keepPowers: 'SW5E.PolymorphKeepPowers',
keepItems: 'SW5E.PolymorphKeepItems',
keepBio: 'SW5E.PolymorphKeepBio',
keepVision: 'SW5E.PolymorphKeepVision'
};
/* -------------------------------------------- */
/**
* Skill, ability, and tool proficiency levels
* Each level provides a proficiency multiplier
* @type {Object}
*/
SW5E.proficiencyLevels = {
0: "SW5E.NotProficient",
1: "SW5E.Proficient",
0.5: "SW5E.HalfProficient",
2: "SW5E.Expertise"
};
/* -------------------------------------------- */
// Condition Types
SW5E.conditionTypes = {
"blinded": "SW5E.ConBlinded",
"charmed": "SW5E.ConCharmed",
"deafened": "SW5E.ConDeafened",
"exhaustion": "SW5E.ConExhaustion",
"frightened": "SW5E.ConFrightened",
"grappled": "SW5E.ConGrappled",
"incapacitated": "SW5E.ConIncapacitated",
"invisible": "SW5E.ConInvisible",
"paralyzed": "SW5E.ConParalyzed",
"petrified": "SW5E.ConPetrified",
"poisoned": "SW5E.ConPoisoned",
"prone": "SW5E.ConProne",
"restrained": "SW5E.ConRestrained",
"shocked": "SW5E.ConShocked",
"stunned": "SW5E.ConStunned",
"unconscious": "SW5E.ConUnconscious"
};
// Languages
SW5E.languages = {
"aleena": "SW5E.LanguagesAleena",
"antarian": "SW5E.LanguagesAntarian",
"anzellan": "SW5E.LanguagesAnzellan",
"aqualish": "SW5E.LanguagesAqualish",
"ardennian": "SW5E.LanguagesArdennian",
"balosur": "SW5E.LanguagesBalosur",
"barabel": "SW5E.LanguagesBarabel",
"basic": "SW5E.LanguagesBasic",
"besalisk": "SW5E.LanguagesBesalisk",
"binary": "SW5E.LanguagesBinary",
"bith": "SW5E.LanguagesBith",
"bocce": "SW5E.LanguagesBocce",
"bothese": "SW5E.LanguagesBothese",
"catharese": "SW5E.LanguagesCartharese",
"cerean": "SW5E.LanguagesCerean",
"chadra-fan": "SW5E.LanguagesChadra-Fan",
"chagri": "SW5E.LanguagesChagri",
"cheunh": "SW5E.LanguagesCheunh",
"chevin": "SW5E.LanguagesChevin",
"chironan": "SW5E.LanguagesChironan",
"clawdite": "SW5E.LanguagesClawdite",
"codruese": "SW5E.LanguagesCodruese",
"colicoid": "SW5E.LanguagesColicoid",
"dashadi": "SW5E.LanguagesDashadi",
"defel": "SW5E.LanguagesDefel",
"devaronese": "SW5E.LanguagesDevaronese",
"dosh": "SW5E.LanguagesDosh",
"draethos": "SW5E.LanguagesDraethos",
"durese": "SW5E.LanguagesDurese",
"dug": "SW5E.LanguagesDug",
"ewokese": "SW5E.LanguagesEwokese",
"falleen": "SW5E.LanguagesFalleen",
"felucianese": "SW5E.LanguagesFelucianese",
"gamorrese": "SW5E.LanguagesGamorrese",
"gand": "SW5E.LanguagesGand",
"geonosian": "SW5E.LanguagesGeonosian",
"givin": "SW5E.LanguagesGivin",
"gran": "SW5E.LanguagesGran",
"gungan": "SW5E.LanguagesGungan",
"hapan": "SW5E.LanguagesHapan",
"harchese": "SW5E.LanguagesHarchese",
"herglese": "SW5E.LanguagesHerglese",
"honoghran": "SW5E.LanguagesHonoghran",
"huttese": "SW5E.LanguagesHuttese",
"iktotchese": "SW5E.LanguagesIktotchese",
"ithorese": "SW5E.LanguagesIthorese",
"jawaese": "SW5E.LanguagesJawaese",
"kaleesh": "SW5E.LanguagesKaleesh",
"kaminoan": "SW5E.LanguagesKaminoan",
"karkaran": "SW5E.LanguagesKarkaran",
"keldor": "SW5E.LanguagesKelDor",
"kharan": "SW5E.LanguagesKharan",
"killik": "SW5E.LanguagesKillik",
"klatooinian": "SW5E.LanguagesKlatooinian",
"kubazian": "SW5E.LanguagesKubazian",
"kushiban": "SW5E.LanguagesKushiban",
"kyuzo": "SW5E.LanguagesKyuzo",
"lannik": "SW5E.LanguagesLannik",
"lasat": "SW5E.LanguagesLasat",
"lowickese": "SW5E.LanguagesLowickese",
"mandoa": "SW5E.LanguagesMandoa",
"miralukese": "SW5E.LanguagesMiralukese",
"mirialan": "SW5E.LanguagesMirialan",
"moncal": "SW5E.LanguagesMonCal",
"mustafarian": "SW5E.LanguagesMustafarian",
"muun": "SW5E.LanguagesMuun",
"nautila": "SW5E.LanguagesNautila",
"ortolan": "SW5E.LanguagesOrtolan",
"pakpak": "SW5E.LanguagesPakPak",
"pyke": "SW5E.LanguagesPyke",
"quarrenese": "SW5E.LanguagesQuarrenese",
"rakata": "SW5E.LanguagesRakata",
"rattataki": "SW5E.LanguagesRattataki",
"rishii": "SW5E.LanguagesRishii",
"rodese": "SW5E.LanguagesRodese",
"selkatha": "SW5E.LanguagesSelkatha",
"semblan": "SW5E.LanguagesSemblan",
"shistavanen": "SW5E.LanguagesShistavanen",
"shyriiwook": "SW5E.LanguagesShyriiwook",
"sith": "SW5E.LanguagesSith",
"squibbian": "SW5E.LanguagesSquibbian",
"sriluurian": "SW5E.LanguagesSriluurian",
"ssi-ruuvi": "SW5E.LanguagesSsi-ruuvi",
"sullustese": "SW5E.LanguagesSullustese",
"talzzi": "SW5E.LanguagesTalzzi",
"thisspiasian": "SW5E.LanguagesThisspiasian",
"togorese": "SW5E.LanguagesTogorese",
"togruti": "SW5E.LanguagesTogruti",
"toydarian": "SW5E.LanguagesToydarian",
"tusken": "SW5E.LanguagesTusken",
"twi'leki": "SW5E.LanguagesTwileki",
"ugnaught": "SW5E.LanguagesUgnaught",
"umbaran": "SW5E.LanguagesUmbaran",
"utapese": "SW5E.LanguagesUtapese",
"verpine": "SW5E.LanguagesVerpine",
"vong": "SW5E.LanguagesVong",
"voss": "SW5E.LanguagesVoss",
"yevethan": "SW5E.LanguagesYevethan",
"zabraki": "SW5E.LanguagesZabraki",
"zygerrian": "SW5E.LanguagesZygerrian"
};
// Character Level XP Requirements
SW5E.CHARACTER_EXP_LEVELS = [
0, 300, 900, 2700, 6500, 14000, 23000, 34000, 48000, 64000, 85000, 100000,
120000, 140000, 165000, 195000, 225000, 265000, 305000, 355000]
;
// Challenge Rating XP Levels
SW5E.CR_EXP_LEVELS = [
10, 200, 450, 700, 1100, 1800, 2300, 2900, 3900, 5000, 5900, 7200, 8400, 10000, 11500, 13000, 15000, 18000,
20000, 22000, 25000, 33000, 41000, 50000, 62000, 75000, 90000, 105000, 120000, 135000, 155000
];
// Configure Optional Character Flags
SW5E.characterFlags = {
"detailOriented": {
name: "Detail Oriented",
hint: "You have advantage on Intelligence (Investigation) checks within 5 feet.",
section: "Racial Traits",
type: Boolean
},
"keenSenses": {
name: "Keen Hearing and Smell",
hint: "You have advantage on Wisdom (Perception) checks that involve hearing or smell.",
section: "Racial Traits",
type: Boolean
},
"naturallyStealthy": {
name: "Naturally Stealthy",
hint: "You can attempt to hide even when you are obscured only by a creature that is your size or larger than you.",
section: "Racial Traits",
type: Boolean
},
"nimbleEscape": {
name: "Nimble Escape",
hint: "You can take the Disengage or Hide action as a bonus action.",
section: "Racial Traits",
type: Boolean
},
"powerfulBuild": {
name: "Powerful Build",
hint: "You count as one size larger when determining your carrying capacity and the weight you can push, drag, or lift.",
section: "Racial Traits",
type: Boolean
},
"programmer": {
name: "Programmer",
hint: "Whenever you make an Intelligence (Technology) check related to computers, you are considered to have expertise in the Technology skill.",
section: "Racial Traits",
type: Boolean
},
"techResistance": {
name: "Tech Resistance",
hint: "You have advantage on Dexterity and Intelligence saving throws against tech powers.",
section: "Racial Traits",
type: Boolean
},
"unarmedCombatant": {
name: "Unarmed Combatant",
hint: "Your unarmed strikes deal 1d4 kinetic damage. You can use your choice of your Strength or Dexterity modifier for the attack and damage rolls. You must use the same modifier for both rolls.",
section: "Racial Traits",
type: Boolean
},
"undersized": {
name: "Undersized",
hint: "You cant use heavy shields, martial weapons with the two-handed property unless it also has the light property, and if a martial weapon has the versatile property, you can only wield it in two hands.",
section: "Racial Traits",
type: Boolean
},
"initiativeAdv": {
name: "Advantage on Initiative",
hint: "Provided by feats or magical items.",
section: "Feats",
type: Boolean
},
"initiativeAlert": {
name: "Alert Feat",
hint: "Provides +5 to Initiative.",
section: "Feats",
type: Boolean
},
"jackOfAllTrades": {
name: "Jack of All Trades",
hint: "Half-Proficiency to Ability Checks in which you are not already Proficient.",
section: "Feats",
type: Boolean
},
"observantFeat": {
name: "Observant Feat",
hint: "Provides a +5 to passive Perception and Investigation.",
skills: ['prc','inv'],
section: "Feats",
type: Boolean
},
"remarkableAthlete": {
name: "Remarkable Athlete.",
hint: "Half-Proficiency (rounded-up) to physical Ability Checks and Initiative.",
abilities: ['str','dex','con'],
section: "Feats",
type: Boolean
},
"weaponCriticalThreshold": {
name: "Critical Hit Threshold",
hint: "Allow for expanded critical range; for example Improved or Superior Critical",
section: "Feats",
type: Number,
placeholder: 20
}
};

View file

@ -1,125 +1,156 @@
export class Dice5e {
/**
* A standardized helper function for managing core 5e "d20 rolls"
*
* Holding SHIFT, ALT, or CTRL when the attack is rolled will "fast-forward".
* This chooses the default options of a normal attack with no bonus, Advantage, or Disadvantage respectively
*
* @param {Array} parts The dice roll component parts, excluding the initial d20
* @param {Object} data Actor or item data against which to parse the roll
* @param {Event|object} event The triggering event which initiated the roll
* @param {string} rollMode A specific roll mode to apply as the default for the resulting roll
* @param {string|null} template The HTML template used to render the roll dialog
* @param {string|null} title The dice roll UI window title
* @param {Object} speaker The ChatMessage speaker to pass when creating the chat
* @param {string|null} flavor Flavor text to use in the posted chat message
* @param {Boolean} fastForward Allow fast-forward advantage selection
* @param {Function} onClose Callback for actions to take when the dialog form is closed
* @param {Object} dialogOptions Modal dialog options
* @param {boolean} advantage Apply advantage to the roll (unless otherwise specified)
* @param {boolean} disadvantage Apply disadvantage to the roll (unless otherwise specified)
* @param {number} critical The value of d20 result which represents a critical success
* @param {number} fumble The value of d20 result which represents a critical failure
* @param {number} targetValue Assign a target value against which the result of this roll should be compared
* @param {boolean} elvenAccuracy Allow Elven Accuracy to modify this roll?
* @param {boolean} halflingLucky Allow Halfling Luck to modify this roll?
*
* @return {Promise} A Promise which resolves once the roll workflow has completed
* @param {Array} parts The dice roll component parts, excluding the initial d20
* @param {Object} data Actor or item data against which to parse the roll
* @param {Event|object} event The triggering event which initiated the roll
* @param {string} rollMode A specific roll mode to apply as the default for the resulting roll
* @param {string|null} template The HTML template used to render the roll dialog
* @param {string|null} title The dice roll UI window title
* @param {Object} speaker The ChatMessage speaker to pass when creating the chat
* @param {string|null} flavor Flavor text to use in the posted chat message
* @param {Boolean} fastForward Allow fast-forward advantage selection
* @param {Function} onClose Callback for actions to take when the dialog form is closed
* @param {Object} dialogOptions Modal dialog options
* @param {boolean} advantage Apply advantage to the roll (unless otherwise specified)
* @param {boolean} disadvantage Apply disadvantage to the roll (unless otherwise specified)
* @param {number} critical The value of d20 result which represents a critical success
* @param {number} fumble The value of d20 result which represents a critical failure
* @param {number} targetValue Assign a target value against which the result of this roll should be compared
* @param {boolean} elvenAccuracy Allow Elven Accuracy to modify this roll?
* @param {boolean} halflingLucky Allow Halfling Luck to modify this roll?
* @param {boolean} reliableTalent Allow Reliable Talent to modify this roll?
* @param {boolean} chatMessage Automatically create a Chat Message for the result of this roll
* @param {object} messageData Additional data which is applied to the created Chat Message, if any
*
* @return {Promise} A Promise which resolves once the roll workflow has completed
*/
static async d20Roll({parts=[], data={}, event={}, rollMode=null, template=null, title=null, speaker=null,
flavor=null, fastForward=null, onClose, dialogOptions,
advantage=null, disadvantage=null, critical=20, fumble=1, targetValue=null,
elvenAccuracy=false, halflingLucky=false}={}) {
// Handle input arguments
flavor = flavor || title;
speaker = speaker || ChatMessage.getSpeaker();
export async function d20Roll({parts=[], data={}, event={}, rollMode=null, template=null, title=null, speaker=null,
flavor=null, fastForward=null, dialogOptions,
advantage=null, disadvantage=null, critical=20, fumble=1, targetValue=null,
elvenAccuracy=false, halflingLucky=false, reliableTalent=false,
chatMessage=true, messageData={}}={}) {
// Prepare Message Data
messageData.flavor = flavor || title;
messageData.speaker = speaker || ChatMessage.getSpeaker();
const messageOptions = {rollMode: rollMode || game.settings.get("core", "rollMode")};
parts = parts.concat(["@bonus"]);
rollMode = rollMode || game.settings.get("core", "rollMode");
let rolled = false;
// Define inner roll function
const _roll = function(parts, adv, form=null) {
// Determine the d20 roll and modifiers
// Handle fast-forward events
let adv = 0;
fastForward = fastForward ?? (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey));
if (fastForward) {
if ( advantage || event.altKey ) adv = 1;
else if ( disadvantage || event.ctrlKey || event.metaKey ) adv = -1;
}
// Define the inner roll function
const _roll = (parts, adv, form) => {
// Determine the d20 roll and modifiers
let nd = 1;
let mods = halflingLucky ? "r=1" : "";
// Handle advantage
if ( adv === 1 ) {
if (adv === 1) {
nd = elvenAccuracy ? 3 : 2;
flavor += ` (${game.i18n.localize("SW5E.Advantage")})`;
mods += "kh";
messageData.flavor += ` (${game.i18n.localize("SW5E.Advantage")})`;
if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].advantage = true;
mods += "kh";
}
// Handle disadvantage
else if ( adv === -1 ) {
else if (adv === -1) {
nd = 2;
flavor += ` (${game.i18n.localize("SW5E.Disadvantage")})`;
mods += "kl";
messageData.flavor += ` (${game.i18n.localize("SW5E.Disadvantage")})`;
if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].disadvantage = true;
mods += "kl";
}
// Include the d20 roll
parts.unshift(`${nd}d20${mods}`);
// Prepend the d20 roll
let formula = `${nd}d20${mods}`;
if (reliableTalent) formula = `{${nd}d20${mods},10}kh`;
parts.unshift(formula);
// Optionally include a situational bonus
if ( form !== null ) data['bonus'] = form.bonus.value;
if ( !data["bonus"] ) parts.pop();
if ( form ) {
data['bonus'] = form.bonus.value;
messageOptions.rollMode = form.rollMode.value;
}
if (!data["bonus"]) parts.pop();
// Optionally include an ability score selection (used for tool checks)
const ability = form ? form.ability : null;
if ( ability && ability.value ) {
if (ability && ability.value) {
data.ability = ability.value;
const abl = data.abilities[data.ability];
if ( abl ) {
data.mod = abl.mod;
flavor += ` (${CONFIG.SW5E.abilities[data.ability]})`;
}
if (abl) {
data.mod = abl.mod;
messageData.flavor += ` (${CONFIG.SW5E.abilities[data.ability]})`;
}
}
// Execute the roll and flag critical thresholds on the d20
let roll = new Roll(parts.join(" + "), data).roll();
const d20 = roll.parts[0];
d20.options.critical = critical;
d20.options.fumble = fumble;
if ( targetValue ) d20.options.target = targetValue;
// Convert the roll to a chat message and return the roll
rollMode = form ? form.rollMode.value : rollMode;
roll.toMessage({
speaker: speaker,
flavor: flavor
}, { rollMode });
rolled = true;
return roll;
};
// Determine whether the roll can be fast-forward
if ( fastForward === null ) {
fastForward = event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey);
// Execute the roll
let roll = new Roll(parts.join(" + "), data);
try {
roll.roll();
} catch (err) {
console.error(err);
ui.notifications.error(`Dice roll evaluation failed: ${err.message}`);
return null;
}
// Optionally allow fast-forwarding to specify advantage or disadvantage
if ( fastForward ) {
if ( advantage || event.altKey ) return _roll(parts, 1);
else if ( disadvantage || event.ctrlKey || event.metaKey ) return _roll(parts, -1);
else return _roll(parts, 0);
// Flag d20 options for any 20-sided dice in the roll
for (let d of roll.dice) {
if (d.faces === 20) {
d.options.critical = critical;
d.options.fumble = fumble;
if (targetValue) d.options.target = targetValue;
}
}
// If reliable talent was applied, add it to the flavor text
if (reliableTalent && roll.dice[0].total < 10) {
messageData.flavor += ` (${game.i18n.localize("SW5E.FlagsReliableTalent")})`;
}
return roll;
};
// Create the Roll instance
const roll = fastForward ? _roll(parts, adv) :
await _d20RollDialog({template, title, parts, data, rollMode: messageOptions.rollMode, dialogOptions, roll: _roll});
// Create a Chat Message
if ( roll && chatMessage ) roll.toMessage(messageData, messageOptions);
return roll;
}
/* -------------------------------------------- */
/**
* Present a Dialog form which creates a d20 roll once submitted
* @return {Promise<Roll>}
* @private
*/
async function _d20RollDialog({template, title, parts, data, rollMode, dialogOptions, roll}={}) {
// Render modal dialog
template = template || "systems/sw5e/templates/chat/roll-dialog.html";
let dialogData = {
formula: parts.join(" + "),
data: data,
rollMode: rollMode,
rollModes: CONFIG.rollModes,
rollModes: CONFIG.Dice.rollModes,
config: CONFIG.SW5E
};
const html = await renderTemplate(template, dialogData);
// Create the Dialog window
let roll;
return new Promise(resolve => {
new Dialog({
title: title,
@ -127,26 +158,24 @@ export class Dice5e {
buttons: {
advantage: {
label: game.i18n.localize("SW5E.Advantage"),
callback: html => roll = _roll(parts, 1, html[0].children[0])
callback: html => resolve(roll(parts, 1, html[0].querySelector("form")))
},
normal: {
label: game.i18n.localize("SW5E.Normal"),
callback: html => roll = _roll(parts, 0, html[0].children[0])
callback: html => resolve(roll(parts, 0, html[0].querySelector("form")))
},
disadvantage: {
label: game.i18n.localize("SW5E.Disadvantage"),
callback: html => roll = _roll(parts, -1, html[0].children[0])
callback: html => resolve(roll(parts, -1, html[0].querySelector("form")))
}
},
default: "normal",
close: html => {
if (onClose) onClose(html, parts, data);
resolve(rolled ? roll : false)
}
close: () => resolve(null)
}, dialogOptions).render(true);
})
});
}
/* -------------------------------------------- */
/**
@ -169,83 +198,103 @@ export class Dice5e {
* @param {Boolean} fastForward Allow fast-forward advantage selection
* @param {Function} onClose Callback for actions to take when the dialog form is closed
* @param {Object} dialogOptions Modal dialog options
* @param {boolean} chatMessage Automatically create a Chat Message for the result of this roll
* @param {object} messageData Additional data which is applied to the created Chat Message, if any
*
* @return {Promise} A Promise which resolves once the roll workflow has completed
*/
static async damageRoll({parts, actor, data, event={}, rollMode=null, template, title, speaker, flavor,
allowCritical=true, critical=false, fastForward=null, onClose, dialogOptions}) {
// Handle input arguments
flavor = flavor || title;
speaker = speaker || ChatMessage.getSpeaker();
rollMode = game.settings.get("core", "rollMode");
let rolled = false;
export async function damageRoll({parts, actor, data, event={}, rollMode=null, template, title, speaker, flavor,
allowCritical=true, critical=false, fastForward=null, dialogOptions, chatMessage=true, messageData={}}={}) {
// Prepare Message Data
messageData.flavor = flavor || title;
messageData.speaker = speaker || ChatMessage.getSpeaker();
const messageOptions = {rollMode: rollMode || game.settings.get("core", "rollMode")};
parts = parts.concat(["@bonus"]);
fastForward = fastForward ?? (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey));
// Define inner roll function
const _roll = function(parts, crit, form) {
data['bonus'] = form ? form.bonus.value : 0;
// Optionally include a situational bonus
if ( form ) {
data['bonus'] = form.bonus.value;
messageOptions.rollMode = form.rollMode.value;
}
if (!data["bonus"]) parts.pop();
// Create the damage roll
let roll = new Roll(parts.join("+"), data);
// Modify the damage formula for critical hits
if ( crit === true ) {
let add = (actor && actor.getFlag("sw5e", "savageAttacks")) ? 1 : 0;
let mult = 2;
roll.alter(add, mult);
flavor = `${flavor} (${game.i18n.localize("SW5E.Critical")})`;
}
// Convert the roll to a chat message
rollMode = form ? form.rollMode.value : rollMode;
roll.toMessage({
speaker: speaker,
flavor: flavor
}, { rollMode });
rolled = true;
return roll;
};
// Determine whether the roll can be fast-forward
if ( fastForward === null ) {
fastForward = event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey);
// Modify the damage formula for critical hits
if ( crit === true ) {
let add = (actor && actor.getFlag("sw5e", "savageAttacks")) ? 1 : 0;
let mult = 2;
// TODO Backwards compatibility - REMOVE LATER
if (isNewerVersion(game.data.version, "0.6.9")) roll.alter(mult, add);
else roll.alter(add, mult);
messageData.flavor += ` (${game.i18n.localize("SW5E.Critical")})`;
if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].critical = true;
}
// Modify the roll and handle fast-forwarding
if ( fastForward ) return _roll(parts, critical || event.altKey);
else parts = parts.concat(["@bonus"]);
// Execute the roll
try {
return roll.roll();
} catch(err) {
console.error(err);
ui.notifications.error(`Dice roll evaluation failed: ${err.message}`);
return null;
}
};
// Render modal dialog
template = template || "systems/sw5e/templates/chat/roll-dialog.html";
let dialogData = {
formula: parts.join(" + "),
data: data,
rollMode: rollMode,
rollModes: CONFIG.rollModes
};
const html = await renderTemplate(template, dialogData);
// Create the Roll instance
const roll = fastForward ? _roll(parts, critical || event.altKey) : await _damageRollDialog({
template, title, parts, data, allowCritical, rollMode: messageOptions.rollMode, dialogOptions, roll: _roll
});
// Create a Chat Message
if ( roll && chatMessage ) roll.toMessage(messageData, messageOptions);
return roll;
// Create the Dialog window
let roll;
return new Promise(resolve => {
new Dialog({
title: title,
content: html,
buttons: {
critical: {
condition: allowCritical,
label: game.i18n.localize("SW5E.CriticalHit"),
callback: html => roll = _roll(parts, true, html[0].children[0])
},
normal: {
label: game.i18n.localize(allowCritical ? "SW5E.Normal" : "SW5E.Roll"),
callback: html => roll = _roll(parts, false, html[0].children[0])
},
},
default: "normal",
close: html => {
if (onClose) onClose(html, parts, data);
resolve(rolled ? roll : false);
}
}, dialogOptions).render(true);
});
}
}
/* -------------------------------------------- */
/**
* Present a Dialog form which creates a damage roll once submitted
* @return {Promise<Roll>}
* @private
*/
async function _damageRollDialog({template, title, parts, data, allowCritical, rollMode, dialogOptions, roll}={}) {
// Render modal dialog
template = template || "systems/sw5e/templates/chat/roll-dialog.html";
let dialogData = {
formula: parts.join(" + "),
data: data,
rollMode: rollMode,
rollModes: CONFIG.Dice.rollModes
};
const html = await renderTemplate(template, dialogData);
// Create the Dialog window
return new Promise(resolve => {
new Dialog({
title: title,
content: html,
buttons: {
critical: {
condition: allowCritical,
label: game.i18n.localize("SW5E.CriticalHit"),
callback: html => resolve(roll(parts, true, html[0].querySelector("form")))
},
normal: {
label: game.i18n.localize(allowCritical ? "SW5E.Normal" : "SW5E.Roll"),
callback: html => resolve(roll(parts, false, html[0].querySelector("form")))
},
},
default: "normal",
close: () => resolve(null)
}, dialogOptions).render(true);
});
}

60
module/macros.js Normal file
View file

@ -0,0 +1,60 @@
/* -------------------------------------------- */
/* Hotbar Macros */
/* -------------------------------------------- */
/**
* Create a Macro from an Item drop.
* Get an existing item macro if one exists, otherwise create a new one.
* @param {Object} data The dropped data
* @param {number} slot The hotbar slot to use
* @returns {Promise}
*/
export async function create5eMacro(data, slot) {
if ( data.type !== "Item" ) return;
if (!( "data" in data ) ) return ui.notifications.warn("You can only create macro buttons for owned Items");
const item = data.data;
// Create the macro command
const command = `game.sw5e.rollItemMacro("${item.name}");`;
let macro = game.macros.entities.find(m => (m.name === item.name) && (m.command === command));
if ( !macro ) {
macro = await Macro.create({
name: item.name,
type: "script",
img: item.img,
command: command,
flags: {"sw5e.itemMacro": true}
});
}
game.user.assignHotbarMacro(macro, slot);
return false;
}
/* -------------------------------------------- */
/**
* Create a Macro from an Item drop.
* Get an existing item macro if one exists, otherwise create a new one.
* @param {string} itemName
* @return {Promise}
*/
export function rollItemMacro(itemName) {
const speaker = ChatMessage.getSpeaker();
let actor;
if ( speaker.token ) actor = game.actors.tokens[speaker.token];
if ( !actor ) actor = game.actors.get(speaker.actor);
// Get matching items
const items = actor ? actor.items.filter(i => i.name === itemName) : [];
if ( items.length > 1 ) {
ui.notifications.warn(`Your controlled Actor ${actor.name} has more than one Item with name ${itemName}. The first matched item will be chosen.`);
} else if ( items.length === 0 ) {
return ui.notifications.warn(`Your controlled Actor does not have an item named ${itemName}`);
}
const item = items[0];
// Trigger the item roll
if ( item.data.type === "power" ) return actor.usePower(item);
return item.roll();
}

View file

@ -136,6 +136,34 @@ export const migrateActorData = function(actor) {
return updateData;
};
/* -------------------------------------------- */
/**
* Scrub an Actor's system data, removing all keys which are not explicitly defined in the system template
* @param {Object} actorData The data object for an Actor
* @return {Object} The scrubbed Actor data
*/
function cleanActorData(actorData) {
// Scrub system data
const model = game.system.model.Actor[actorData.type];
actorData.data = filterObject(actorData.data, model);
// Scrub system flags
const allowedFlags = CONFIG.SW5E.allowedActorFlags.reduce((obj, f) => {
obj[f] = null;
return obj;
}, {});
if ( actorData.flags.sw5e ) {
actorData.flags.sw5e = filterObject(actorData.flags.sw5e, allowedFlags);
}
// Return the scrubbed data
return actorData;
}
/* -------------------------------------------- */
/**
@ -222,3 +250,33 @@ const _migrateRemoveDeprecated = function(ent, updateData) {
updateData[`data.${parts.join(".")}`] = null;
}
};
/* -------------------------------------------- */
/**
* A general tool to purge flags from all entities in a Compendium pack.
* @param {Compendium} pack The compendium pack to clean
* @private
*/
export async function purgeFlags(pack) {
const cleanFlags = (flags) => {
const flags5e = flags.sw5e || null;
return flags5e ? {sw5e: flags5e} : {};
};
await pack.configure({locked: false});
const content = await pack.getContent();
for ( let entity of content ) {
const update = {_id: entity.id, flags: cleanFlags(entity.data.flags)};
if ( pack.entity === "Actor" ) {
update.items = entity.data.items.map(i => {
i.flags = cleanFlags(i.flags);
return i;
})
}
await pack.updateEntity(update, {recursive: false});
console.log(`Purged flags from ${entity.name}`);
}
await pack.configure({locked: true});
}

View file

@ -4,7 +4,7 @@ import { SW5E } from "../config.js";
* A helper class for building MeasuredTemplates for 5e powers and abilities
* @extends {MeasuredTemplate}
*/
export class AbilityTemplate extends MeasuredTemplate {
export default class AbilityTemplate extends MeasuredTemplate {
/**
* A factory method to create an AbilityTemplate instance using provided data from an Item5e instance
@ -37,8 +37,8 @@ export class AbilityTemplate extends MeasuredTemplate {
templateData.width = target.value;
templateData.direction = 45;
break;
case "ray": // 5e rays are most commonly 5ft wide
templateData.width = 5;
case "ray": // 5e rays are most commonly 1 square (5 ft) in width
templateData.width = target.width ?? canvas.dimensions.distance;
break;
default:
break;
@ -52,9 +52,8 @@ export class AbilityTemplate extends MeasuredTemplate {
/**
* Creates a preview of the power template
* @param {Event} event The initiating click event
*/
drawPreview(event) {
drawPreview() {
const initialLayer = canvas.activeLayer;
this.draw();
this.layer.activate();

View file

@ -11,6 +11,23 @@ export const registerSystemSettings = function() {
default: 0
});
/**
* Register resting variants
*/
game.settings.register("sw5e", "restVariant", {
name: "SETTINGS.5eRestN",
hint: "SETTINGS.5eRestL",
scope: "world",
config: true,
default: "normal",
type: String,
choices: {
"normal": "SETTINGS.5eRestPHB",
"gritty": "SETTINGS.5eRestGritty",
"epic": "SETTINGS.5eRestEpic",
}
});
/**
* Register diagonal movement rule setting
*/
@ -23,7 +40,8 @@ export const registerSystemSettings = function() {
type: String,
choices: {
"555": "SETTINGS.5eDiagPHB",
"5105": "SETTINGS.5eDiagDMG"
"5105": "SETTINGS.5eDiagDMG",
"EUCL": "SETTINGS.5eDiagEuclidean",
},
onChange: rule => canvas.grid.diagonalRule = rule
});
@ -31,21 +49,14 @@ export const registerSystemSettings = function() {
/**
* Register Initiative formula setting
*/
function _set5eInitiative(tiebreaker) {
CONFIG.Combat.initiative.tiebreaker = tiebreaker;
CONFIG.Combat.initiative.decimals = tiebreaker ? 2 : 0;
if ( ui.combat && ui.combat._rendered ) ui.combat.render();
}
game.settings.register("sw5e", "initiativeDexTiebreaker", {
name: "SETTINGS.5eInitTBN",
hint: "SETTINGS.5eInitTBL",
scope: "world",
config: true,
default: false,
type: Boolean,
onChange: enable => _set5eInitiative(enable)
type: Boolean
});
_set5eInitiative(game.settings.get("sw5e", "initiativeDexTiebreaker"));
/**
* Require Currency Carrying Weight

View file

@ -13,11 +13,13 @@ export const preloadHandlebarsTemplates = async function() {
"systems/sw5e/templates/actors/parts/actor-inventory.html",
"systems/sw5e/templates/actors/parts/actor-features.html",
"systems/sw5e/templates/actors/parts/actor-powerbook.html",
"systems/sw5e/templates/actors/parts/actor-effects.html",
// Item Sheet Partials
"systems/sw5e/templates/items/parts/item-action.html",
"systems/sw5e/templates/items/parts/item-activation.html",
"systems/sw5e/templates/items/parts/item-description.html"
"systems/sw5e/templates/items/parts/item-description.html",
"systems/sw5e/templates/items/parts/item-mountable.html"
];
// Load the template parts

3101
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -1,6 +1,6 @@
{
"Actor": {
"types": ["character", "npc"],
"types": ["character", "npc", "vehicle"],
"templates": {
"common": {
"abilities": {
@ -31,7 +31,7 @@
},
"attributes": {
"ac": {
"min": 0
"value": 10
},
"hp": {
"value": 10,
@ -43,19 +43,47 @@
"init": {
"value": 0,
"bonus": 0
},
"speed": {
"value": "30 ft",
"special": ""
},
"powercasting": "int"
}
},
"details": {
"alignment": "",
"biography": {
"value": "",
"public": ""
}
},
"traits": {
"size": "med",
"di": {
"value": [],
"custom": ""
},
"dr": {
"value": [],
"custom": ""
},
"dv": {
"value": [],
"custom": ""
},
"ci": {
"value": [],
"custom": ""
}
},
"currency": {
"gc": 0
}
},
"creature": {
"attributes": {
"powercasting": "int",
"speed": {
"value": "30 ft",
"special": ""
}
},
"details": {
"alignment": "",
"species": ""
},
"skills": {
@ -133,81 +161,51 @@
}
},
"traits": {
"size": "med",
"senses": "",
"languages": {
"value": [],
"custom": ""
},
"di": {
"value": [],
"custom": ""
},
"dr": {
"value": [],
"custom": ""
},
"dv": {
"value": [],
"custom": ""
},
"ci": {
"value": [],
"custom": ""
}
},
"currency": {
"gc": 0
},
"powers": {
"power1": {
"value": 0,
"max": 0,
"override": null
},
"power2": {
"value": 0,
"max": 0,
"override": null
},
"power3": {
"value": 0,
"max": 0,
"override": null
},
"power4": {
"value": 0,
"max": 0,
"override": null
},
"power5": {
"value": 0,
"max": 0,
"override": null
},
"power6": {
"value": 0,
"max": 0,
"override": null
},
"power7": {
"value": 0,
"max": 0,
"override": null
},
"power8": {
"value": 0,
"max": 0,
"override": null
},
"power9": {
"value": 0,
"max": 0,
"override": null
},
"pact": {
"value": 0,
"max": 0,
"override": null
}
},
@ -240,12 +238,16 @@
}
},
"character": {
"templates": ["common"],
"templates": ["common", "creature"],
"attributes": {
"death": {
"success": 0,
"failure": 0
},
"encumbrance": {
"value": null,
"max": null
},
"exhaustion": 0,
"inspiration": 0
},
@ -256,6 +258,7 @@
"min": 0,
"max": 300
},
"appearance": "",
"trait": "",
"ideal": "",
"bond": "",
@ -297,7 +300,7 @@
}
},
"npc": {
"templates": ["common"],
"templates": ["common", "creature"],
"details": {
"type": "",
"environment": "",
@ -322,13 +325,64 @@
"initiative": 0
}
}
},
"vehicle": {
"templates": ["common"],
"abilities": {
"int": {
"value": 0
},
"wis": {
"value": 0
},
"cha": {
"value": 0
}
},
"attributes": {
"ac": {
"value": null,
"motionless": ""
},
"actions": {
"stations": false,
"value": 0,
"thresholds": {
"2": null,
"1": null,
"0": null
}
},
"hp": {
"value": null,
"max": null,
"dt": null,
"mt": null
},
"capacity": {
"creature": "",
"cargo": 0
},
"speed": ""
},
"traits": {
"size": "lg",
"dimensions": "",
"di": {
"value": ["poison", "psychic"]
},
"ci": {
"value": ["blinded", "charmed", "deafened", "frightened", "paralyzed", "petrified", "poisoned", "stunned",
"unconscious"]
}
},
"cargo": {
"crew": [],
"passengers": []
}
}
},
"Item": {
<<<<<<< Updated upstream
"types": ["weapon", "equipment", "consumable", "tool", "loot", "class", "power", "feat", "species", "backpack"],
"templates": {
=======
"types": ["weapon", "equipment", "consumable", "tool", "loot", "class", "power", "feat", "species", "backpack", "archetype", "classfeature", "background"],
"templates": {
"archetypeDescription": {
@ -389,7 +443,6 @@
"value": ""
}
},
>>>>>>> Stashed changes
"itemDescription": {
"description": {
"value": "",
@ -398,8 +451,6 @@
},
"source": ""
},
<<<<<<< Updated upstream
=======
"classDescription": {
"className": "",
"description": {
@ -409,7 +460,6 @@
"value": ""
}
},
>>>>>>> Stashed changes
"speciesDescription": {
"data": "$characteristics-table",
"description": {
@ -440,6 +490,7 @@
},
"target": {
"value": null,
"width": null,
"units": "",
"type": ""
},
@ -452,6 +503,11 @@
"value": 0,
"max": 0,
"per": null
},
"consume": {
"type": "",
"target": null,
"amount": null
}
},
"action": {
@ -470,6 +526,17 @@
"dc": null,
"scaling": "power"
}
},
"mountable": {
"armor": {
"value": 10
},
"hp": {
"value": 0,
"max": 0,
"dt": null,
"conditions": ""
}
}
},
"archetype": {
@ -505,31 +572,28 @@
},
"powercasting": "none"
},
<<<<<<< Updated upstream
=======
"classfeature": {
"templates": ["itemDescription", "activatedEffect", "action"],
"className": ""
},
>>>>>>> Stashed changes
"consumable": {
"templates": ["itemDescription", "physicalItem", "activatedEffect", "action"],
"consumableType": "potion",
"uses": {
"value": 0,
"max": 0,
"per": null,
"autoUse": true,
"autoDestroy": true
"autoDestroy": false
}
},
"equipment": {
"templates": ["itemDescription", "physicalItem", "activatedEffect", "action"],
"templates": ["itemDescription", "physicalItem", "activatedEffect", "action", "mountable"],
"armor": {
"type": "light",
"value": 10,
"dex": null
},
"speed": {
"value": null,
"conditions": ""
},
"strength": 0,
"stealth": false,
"proficient": true
@ -547,10 +611,6 @@
},
"species": {
"templates": ["speciesDescription"]
<<<<<<< Updated upstream
=======
>>>>>>> Stashed changes
},
"tool": {
"templates": ["itemDescription", "physicalItem"],
@ -581,13 +641,13 @@
"prepared": false
},
"scaling": {
"mode": null,
"mode": "none",
"formula": null
}
},
"weapon": {
"templates": ["itemDescription", "physicalItem" ,"activatedEffect", "action"],
"weaponType": "simpleM",
"templates": ["itemDescription", "physicalItem" ,"activatedEffect", "action", "mountable"],
"weaponType": "simpleVW",
"properties": {},
"proficient": true
}

View file

@ -1,488 +0,0 @@
{
"Actor": {
"types": ["character", "npc"],
"templates": {
"common": {
"abilities": {
"str": {
"value": 10,
"proficient": 0
},
"dex": {
"value": 10,
"proficient": 0
},
"con": {
"value": 10,
"proficient": 0
},
"int": {
"value": 10,
"proficient": 0
},
"wis": {
"value": 10,
"proficient": 0
},
"cha": {
"value": 10,
"proficient": 0
}
},
"attributes": {
"ac": {
"min": 0
},
"hp": {
"value": 10,
"min": 0,
"max": 10,
"temp": 0,
"tempmax": 0
},
"init": {
"value": 0,
"bonus": 0
},
"speed": {
"value": "30 ft",
"special": ""
},
"powercasting": "int"
},
"details": {
"alignment": "",
"biography": {
"value": "",
"public": ""
},
"race": ""
},
"skills": {
"acr": {
"value": 0,
"ability": "dex"
},
"ani": {
"value": 0,
"ability": "wis"
},
"ath": {
"value": 0,
"ability": "str"
},
"dec": {
"value": 0,
"ability": "cha"
},
"ins": {
"value": 0,
"ability": "wis"
},
"itm": {
"value": 0,
"ability": "cha"
},
"inv": {
"value": 0,
"ability": "int"
},
"lor": {
"value": 0,
"ability": "int"
},
"med": {
"value": 0,
"ability": "wis"
},
"nat": {
"value": 0,
"ability": "int"
},
"pil": {
"value": 0,
"ability": "int"
},
"prc": {
"value": 0,
"ability": "wis"
},
"prf": {
"value": 0,
"ability": "cha"
},
"per": {
"value": 0,
"ability": "cha"
},
"slt": {
"value": 0,
"ability": "dex"
},
"ste": {
"value": 0,
"ability": "dex"
},
"sur": {
"value": 0,
"ability": "wis"
},
"tec": {
"value": 0,
"ability": "int"
}
},
"traits": {
"size": "med",
"senses": "",
"languages": {
"value": [],
"custom": ""
},
"di": {
"value": [],
"custom": ""
},
"dr": {
"value": [],
"custom": ""
},
"dv": {
"value": [],
"custom": ""
},
"ci": {
"value": [],
"custom": ""
}
},
"currency": {
"gc": 0
},
"powers": {
"power1": {
"value": 0,
"max": 0,
"override": null
},
"power2": {
"value": 0,
"max": 0,
"override": null
},
"power3": {
"value": 0,
"max": 0,
"override": null
},
"power4": {
"value": 0,
"max": 0,
"override": null
},
"power5": {
"value": 0,
"max": 0,
"override": null
},
"power6": {
"value": 0,
"max": 0,
"override": null
},
"power7": {
"value": 0,
"max": 0,
"override": null
},
"power8": {
"value": 0,
"max": 0,
"override": null
},
"power9": {
"value": 0,
"max": 0,
"override": null
},
"pact": {
"value": 0,
"max": 0,
"override": null
}
},
"bonuses": {
"mwak": {
"attack": "",
"damage": ""
},
"rwak": {
"attack": "",
"damage": ""
},
"mpak": {
"attack": "",
"damage": ""
},
"rpak": {
"attack": "",
"damage": ""
},
"abilities": {
"check": "",
"save": "",
"skill": ""
},
"power": {
"dc": ""
}
}
}
},
"character": {
"templates": ["common"],
"attributes": {
"death": {
"success": 0,
"failure": 0
},
"exhaustion": 0,
"inspiration": 0
},
"details": {
"background": "",
"xp": {
"value": 0,
"min": 0,
"max": 300
},
"trait": "",
"ideal": "",
"bond": "",
"flaw": ""
},
"resources": {
"primary": {
"value": 0,
"max": 0,
"sr": 0,
"lr": 0
},
"secondary": {
"value": 0,
"max": 0,
"sr": 0,
"lr": 0
},
"tertiary": {
"value": 0,
"max": 0,
"sr": 0,
"lr": 0
}
},
"traits": {
"weaponProf": {
"value": [],
"custom": ""
},
"armorProf": {
"value": [],
"custom": ""
},
"toolProf": {
"value": [],
"custom": ""
}
}
},
"npc": {
"templates": ["common"],
"details": {
"type": "",
"environment": "",
"cr": 1,
"powerLevel": 0,
"xp": {
"value": 10
},
"source": ""
},
"resources": {
"legact": {
"value": 0,
"max": 0
},
"legres": {
"value": 0,
"max": 0
},
"lair": {
"value": 0,
"initiative": 0
}
}
}
},
"Item": {
"types": ["weapon", "equipment", "consumable", "tool", "loot", "class", "power", "feat", "backpack"],
"templates": {
"itemDescription": {
"description": {
"value": "",
"chat": "",
"unidentified": ""
},
"source": ""
},
"physicalItem": {
"quantity": 1,
"weight": 0,
"price": 0,
"attuned": false,
"equipped": false,
"rarity": "",
"identified": true
},
"activatedEffect": {
"activation": {
"type": "",
"cost": 0,
"condition": ""
},
"duration": {
"value": null,
"units": ""
},
"target": {
"value": null,
"units": "",
"type": ""
},
"range": {
"value": null,
"long": null,
"units": ""
},
"uses": {
"value": 0,
"max": 0,
"per": null
}
},
"action": {
"ability": null,
"actionType": null,
"attackBonus": 0,
"chatFlavor": "",
"critical": null,
"damage": {
"parts": [],
"versatile": ""
},
"formula": "",
"save": {
"ability": "",
"dc": null,
"scaling": "power"
}
}
},
"backpack": {
"templates": ["itemDescription", "physicalItem"],
"capacity": {
"type": "weight",
"value": 0,
"weightless": false
},
"currency": {
"gc": 0
}
},
"class": {
"templates": ["itemDescription"],
"levels": 1,
"subclass": "",
"hitDice": "d6",
"hitDiceUsed": 0,
"skills": {
"number": 2,
"choices": [],
"value": []
},
"powercasting": "none"
},
"consumable": {
"templates": ["itemDescription", "physicalItem", "activatedEffect", "action"],
"consumableType": "potion",
"uses": {
"value": 0,
"max": 0,
"per": null,
"autoUse": true,
"autoDestroy": true
}
},
"equipment": {
"templates": ["itemDescription", "physicalItem", "activatedEffect", "action"],
"armor": {
"type": "light",
"value": 10,
"dex": null
},
"strength": 0,
"stealth": false,
"proficient": true
},
"feat": {
"templates": ["itemDescription", "activatedEffect", "action"],
"requirements": "",
"recharge": {
"value": null,
"charged": false
}
},
"loot": {
"templates": ["itemDescription", "physicalItem"]
},
"tool": {
"templates": ["itemDescription", "physicalItem"],
"ability": "int",
"chatFlavor": "",
"proficient": 0
},
"power": {
"templates": ["itemDescription", "activatedEffect", "action"],
"level": 1,
"school": "",
"components": {
"value": "",
"vocal": false,
"somatic": false,
"material": false,
"ritual": false,
"concentration": false
},
"materials": {
"value": "",
"consumed": false,
"cost": 0,
"supply": 0
},
"preparation": {
"mode": null,
"prepared": false
},
"scaling": {
"mode": null,
"formula": null
}
},
"weapon": {
"templates": ["itemDescription", "physicalItem" ,"activatedEffect", "action"],
"weaponType": "simpleM",
"properties": {},
"proficient": true
}
}
}

View file

@ -4,31 +4,33 @@
<header class="sheet-header flexrow">
<img class="profile" src="{{actor.img}}" title="{{actor.name}}" data-edit="img"/>
<div class="header-details flexrow">
<section class="header-details flexrow">
<h1 class="charname">
<input name="name" type="text" value="{{actor.name}}" placeholder="{{ localize 'SW5E.Name' }}"/>
</h1>
<div class="charlevel">
<div class="level {{#if disableExperience}}noxp{{/if}}">
<aside class="header-exp flexcol">
<div class="charlevel">
<label>{{ localize "SW5E.Level" }} {{data.details.level}}</label>
<span class="levels">{{classLabels}}</span>
</div>
{{#unless disableExperience}}
<div class="experience">
<input name="data.details.xp.value" type="text" value="{{data.details.xp.value}}"
data-dtype="Number" placeholder="0"/>
<span class="max"> / {{data.details.xp.max}}</span>
<div class="experience flexrow">
<input name="data.details.xp.value" type="text" value="{{data.details.xp.value}}" placeholder="0" data-dtype="Number"/>
<span class="sep">/</span>
<span class="max">{{data.details.xp.max}}</span>
</div>
<div class="xpbar">
<span class="bar" style="width: {{data.details.xp.pct}}%"></span>
</div>
{{/unless}}
</div>
</aside>
{{!-- Character Summary --}}
<ul class="summary">
<ul class="summary flexrow">
<li>
<input type="text" name="data.details.race" value="{{data.details.race}}" placeholder="{{ localize 'SW5E.Species' }}"/>
<input type="text" name="data.details.species" value="{{data.details.species}}" placeholder="{{ localize 'SW5E.Species' }}"/>
</li>
<li>
<input type="text" name="data.details.background" value="{{data.details.background}}" placeholder="{{ localize 'SW5E.Background' }}"/>
@ -36,6 +38,9 @@
<li>
<input type="text" name="data.details.alignment" value="{{data.details.alignment}}" placeholder="{{ localize 'SW5E.Alignment' }}"/>
</li>
<li class="proficiency">
<span>{{ localize "SW5E.Proficiency" }} {{numberFormat data.attributes.prof decimals=0 sign=true}}</span>
</li>
</ul>
{{!-- Header Attributes --}}
@ -43,29 +48,36 @@
<li class="attribute health">
<h4 class="attribute-name box-title">{{ localize "SW5E.Health" }}</h4>
<div class="attribute-value multiple">
<input name="data.attributes.hp.value" type="text" value="{{data.attributes.hp.value}}"
data-dtype="Number" placeholder="10"/>
<input name="data.attributes.hp.value" type="text" value="{{data.attributes.hp.value}}" placeholder="10" data-dtype="Number"/>
<span class="sep"> / </span>
<input name="data.attributes.hp.max" type="text" value="{{data.attributes.hp.max}}"
data-dtype="Number" placeholder="10"/>
<input name="data.attributes.hp.max" type="text" value="{{data.attributes.hp.max}}" placeholder="10" data-dtype="Number"/>
</div>
<footer class="attribute-footer">
<input name="data.attributes.hp.temp" type="text" class="temphp" placeholder="+{{ localize 'SW5E.Temp' }}"
value="{{data.attributes.hp.temp}}" data-dtype="{{data.attributes.hp.type}}"/>
value="{{data.attributes.hp.temp}}" data-dtype="Number"/>
<input name="data.attributes.hp.tempmax" type="text" class="temphp" placeholder="+{{ localize 'SW5E.Max' }}"
value="{{data.attributes.hp.tempmax}}" data-dtype="{{data.attributes.hp.type}}"/>
value="{{data.attributes.hp.tempmax}}" data-dtype="Number"/>
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{ localize "SW5E.HitDice" }}</h4>
<div class="attribute-value multiple">
<label class="hit-dice">{{data.attributes.hd}} <span class="sep"> / </span> {{data.details.level}}</label>
</div>
<footer class="attribute-footer">
<a class="rest short-rest">{{ localize "SW5E.RestS" }}</a>
<a class="rest long-rest">{{ localize "SW5E.RestL" }}</a>
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{ localize "SW5E.ArmorClass" }}</h4>
<div class="attribute-value">
<input name="data.attributes.ac.value" type="text" value="{{data.attributes.ac.value}}"
data-dtype="Number" placeholder="10"/>
<input name="data.attributes.ac.value" type="number" value="{{data.attributes.ac.value}}" placeholder="10"/>
</div>
<footer class="attribute-footer">
<span>{{ localize "SW5E.Proficiency" }}</span>
<span>{{numberFormat data.attributes.prof decimals=0 sign=true}}</span>
<span class="power-dc">{{localize "SW5E.PowerDC"}} {{data.attributes.powerdc}}</span>
</footer>
</li>
@ -81,18 +93,19 @@
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{ localize "SW5E.HitDice" }}</h4>
<div class="attribute-value multiple">
<label class="hit-dice">{{data.attributes.hd}} / {{data.details.level}}</label>
<li class="attribute initiative">
<h4 class="attribute-name box-title">{{ localize "SW5E.Initiative" }}</h4>
<div class="attribute-value">
<span>{{numberFormat data.attributes.init.total decimals=0 sign=true}}</span>
</div>
<footer class="attribute-footer">
<a class="rest short-rest">{{ localize "SW5E.RestS" }}</a>
<a class="rest long-rest">{{ localize "SW5E.RestL" }}</a>
<span>{{ localize "SW5E.Modifier" }}</span>
<input name="data.attributes.init.value" type="number" placeholder="0"
value="{{numberFormat data.attributes.init.value decimals=0 sign=true}}"/>
</footer>
</li>
</ul>
</div>
</section>
</header>
{{!-- NPC Sheet Navigation --}}
@ -101,6 +114,7 @@
<a class="item" data-tab="inventory">{{ localize "SW5E.Inventory" }}</a>
<a class="item" data-tab="features">{{ localize "SW5E.Features" }}</a>
<a class="item" data-tab="powerbook">{{ localize "SW5E.Powerbook" }}</a>
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
<a class="item" data-tab="biography">{{ localize "SW5E.Biography" }}</a>
</nav>
@ -114,11 +128,11 @@
{{#each data.abilities as |ability id|}}
<li class="ability {{#if ability.proficient}}proficient{{/if}}" data-ability="{{id}}">
<h4 class="ability-name box-title rollable">{{ability.label}}</h4>
<input class="ability-score" name="data.abilities.{{id}}.value" type="text" value="{{ability.value}}" data-dtype="Number" placeholder="10"/>
<input class="ability-score" name="data.abilities.{{id}}.value" type="number" value="{{ability.value}}" placeholder="10"/>
<div class="ability-modifiers flexrow">
<span class="ability-mod" title="Modifier">{{numberFormat ability.mod decimals=0 sign=true}}</span>
<input type="hidden" name="data.abilities.{{id}}.proficient" value="{{ability.proficient}}" data-dtype="Number"/>
<a class="proficiency-toggle ability-proficiency" title="Proficiency">{{{ability.icon}}}</a>
<a class="proficiency-toggle ability-proficiency" title="{{ localize 'SW5E.Proficiency' }}">{{{ability.icon}}}</a>
<span class="ability-save" title="Saving Throw">{{numberFormat ability.save decimals=0 sign=true}}</span>
</div>
</li>
@ -133,13 +147,13 @@
<a class="proficiency-toggle skill-proficiency" title="{{skill.hover}}">{{{skill.icon}}}</a>
<h4 class="skill-name rollable">{{skill.label}}</h4>
<span class="skill-ability">{{skill.ability}}</span>
<span class="skill-mod">{{numberFormat skill.mod decimals=0 sign=true}}</span>
<span class="skill-mod">{{numberFormat skill.total decimals=0 sign=true}}</span>
<span class="skill-passive">({{skill.passive}})</span>
</li>
{{/each}}
</ul>
<section class="center-pane">
<section class="center-pane flexcol">
{{!-- Body Attributes --}}
<ul class="attributes flexrow">
@ -149,58 +163,44 @@
<input name="data.resources.{{res.name}}.label" type="text" value="{{res.label}}"
placeholder="{{res.placeholder}}" />
</h4>
<div class="attribute-value multiple">
<input name="data.resources.{{res.name}}.value" type="text" value="{{res.value}}"
data-dtype="Number" placeholder="0"/>
<span class="sep"> / </span>
<input name="data.resources.{{res.name}}.max" type="text" value="{{res.max}}"
data-dtype="Number" placeholder="0"/>
</div>
<footer class="attribute-footer">
<label class="checkbox">
<div class="attribute-value">
<label class="recharge checkbox">
{{ localize "SW5E.AbbreviationSR" }} <input name="data.resources.{{res.name}}.sr" type="checkbox" {{checked res.sr}}/>
</label>
<label class="checkbox">
<input name="data.resources.{{res.name}}.value" type="number" value="{{res.value}}" placeholder="0"/>
<span class="sep"> / </span>
<input name="data.resources.{{res.name}}.max" type="number" value="{{res.max}}" placeholder="0"/>
<label class="recharge checkbox">
{{ localize "SW5E.AbbreviationLR" }} <input name="data.resources.{{res.name}}.lr" type="checkbox" {{checked res.lr}}/>
</label>
</footer>
</div>
</li>
{{/each}}
<li class="attribute initiative">
<h4 class="attribute-name box-title">{{ localize "SW5E.Initiative" }}</h4>
<div class="attribute-value">
<span>{{numberFormat data.attributes.init.total decimals=0 sign=true}}</span>
</div>
<footer class="attribute-footer">
<span>{{ localize "SW5E.Modifier" }}</span>
<input name="data.attributes.init.value" type="text" placeholder="0" data-dtype="Number"
value="{{numberFormat data.attributes.init.value decimals=0 sign=true}}"/>
</footer>
</li>
</ul>
{{!-- Counters --}}
<div class="counters flexrow">
<div class="counters">
<div class="counter flexrow death-saves">
<h4 class="death-save rollable">{{ localize "SW5E.DeathSave" }}</h4>
<div class="counter-value">
<i class="fas fa-check"></i>
<input type="text" name="data.attributes.death.success" data-dtype="Number" placeholder="0"
<input name="data.attributes.death.success" type="number" placeholder="0"
value="{{data.attributes.death.success}}"/>
<i class="fas fa-times"></i>
<input type="text" name="data.attributes.death.failure" data-dtype="Number" placeholder="0"
<input name="data.attributes.death.failure" type="number" placeholder="0"
value="{{data.attributes.death.failure}}"/>
</div>
</div>
<div class="counter flexrow">
<div class="counter flexrow exhaustion">
<h4>{{ localize "SW5E.Exhaustion" }}</h4>
<div class="counter-value">
<input type="text" name="data.attributes.exhaustion" data-dtype="Number" placeholder="0"
<input name="data.attributes.exhaustion" type="number" placeholder="0"
value="{{data.attributes.exhaustion}}" />
</div>
</div>
<div class="counter flexrow">
<div class="counter flexrow inspiration">
<h4>{{ localize "SW5E.Inspiration" }}</h4>
<div class="counter-value">
<input type="checkbox" name="data.attributes.inspiration" data-dtype="Boolean"
@ -228,10 +228,29 @@
<div class="tab powerbook flexcol" data-group="primary" data-tab="powerbook">
{{> "systems/sw5e/templates/actors/parts/actor-powerbook.html"}}
</div>
{{!-- Effects Tab --}}
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
{{> "systems/sw5e/templates/actors/parts/actor-effects.html"}}
</div>
{{!-- Biography Tab --}}
<div class="tab biography flexcol" data-group="primary" data-tab="biography">
{{editor content=data.details.biography.value target="data.details.biography.value" button=true owner=owner editable=editable}}
<div class="tab flexrow" data-group="primary" data-tab="biography">
<div class="characteristics flexcol">
<label>{{ localize "SW5E.Appearance" }}</label>
<textarea name="data.details.appearance">{{data.details.appearance}}</textarea>
<label>{{ localize "SW5E.PersonalityTraits" }}</label>
<textarea name="data.details.trait">{{data.details.trait}}</textarea>
<label>{{ localize "SW5E.Ideals" }}</label>
<textarea name="data.details.ideal">{{data.details.ideal}}</textarea>
<label>{{ localize "SW5E.Bonds" }}</label>
<textarea name="data.details.bond">{{data.details.bond}}</textarea>
<label>{{ localize "SW5E.Flaws" }}</label>
<textarea name="data.details.flaw">{{data.details.flaw}}</textarea>
</div>
<div class="biography">
{{editor content=data.details.biography.value target="data.details.biography.value" button=true owner=owner editable=editable}}
</div>
</div>
</section>
</form>

View file

@ -1,21 +1,20 @@
<form class="{{cssClass}}" autocomplete="off">
<form class="{{cssClass}} flexcol limited" autocomplete="off">
<!-- HEADER -->
<header class="sheet-header">
<h1 class="charname">
<input name="name" type="text" value="{{actor.name}}" placeholder="{{ localize 'SW5E.Name' }}"/>
</h1>
{{!-- Sheet Header --}}
<header class="sheet-header flexrow">
<img class="profile" src="{{actor.img}}" title="{{actor.name}}" data-edit="img"/>
<section class="header-details flexrow">
<h1 class="charname">
<input name="name" type="text" value="{{actor.name}}" placeholder="{{ localize 'SW5E.Name' }}"/>
</h1>
</section>
</header>
<!-- BODY -->
{{!-- Sheet Body --}}
<section class="sheet-body">
<div class="tab biography">
{{editor content=data.details.biography.value target="data.details.biography.value" button=true owner=owner editable=editable}}
</div>
</section>
<!-- SIDEBAR -->
<section class="sheet-sidebar">
<img class="sheet-profile" src="{{actor.img}}" title="{{actor.name}}" height="220" width="220" data-edit="img"/>
</section>
</form>

View file

@ -4,33 +4,76 @@
<header class="sheet-header flexrow">
<img class="profile" src="{{actor.img}}" title="{{actor.name}}" data-edit="img"/>
<div class="header-details flexrow">
<section class="header-details flexrow">
<h1 class="charname">
<input name="name" type="text" value="{{actor.name}}" placeholder="{{ localize 'SW5E.Name' }}"/>
</h1>
<div class="charlevel">
<div class="level">
<aside class="header-exp flexcol">
<div class="cr">
<label>{{ localize "SW5E.AbbreviationCR" }}</label>
<input name="data.details.cr" type="text" value="{{labels.cr}}" placeholder="1"/>
</div>
<div class="experience">
<span>{{data.details.xp.value}} XP</span>
</div>
</div>
</aside>
<ul class="summary">
{{!-- Character Summary --}}
<ul class="summary flexrow">
<li>
<input type="text" name="data.details.type" value="{{data.details.type}}" placeholder="{{ localize 'SW5E.Type' }}"/>
<span>{{lookup config.actorSizes data.traits.size}}</span>
</li>
<li>
<input type="text" name="data.details.alignment" value="{{data.details.alignment}}" placeholder="{{ localize 'SW5E.Alignment' }}"/>
</li>
<li>
<input type="text" name="data.details.type" value="{{data.details.type}}" placeholder="{{ localize 'SW5E.Type' }}"/>
</li>
<li>
<input type="text" name="data.details.source" value="{{data.details.source}}" placeholder="{{ localize 'SW5E.Source' }}"/>
</li>
</ul>
</div>
{{!-- Header Attributes --}}
<ul class="attributes flexrow">
<li class="attribute health">
<h4 class="attribute-name box-title rollable">{{ localize "SW5E.Health" }}</h4>
<div class="attribute-value multiple">
<input name="data.attributes.hp.value" type="text" value="{{data.attributes.hp.value}}" placeholder="10" data-dtype="Number"/>
<span class="sep"> / </span>
<input name="data.attributes.hp.max" type="text" value="{{data.attributes.hp.max}}" placeholder="10" data-dtype="Number"/>
</div>
<footer class="attribute-footer">
<input name="data.attributes.hp.formula" class="hpformula" type="text" placeholder="{{ localize 'SW5E.HealthFormula' }}"
value="{{data.attributes.hp.formula}}"/>
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{ localize "SW5E.ArmorClass" }}</h4>
<div class="attribute-value">
<input name="data.attributes.ac.value" type="number" value="{{data.attributes.ac.value}}" placeholder="10"/>
</div>
<footer class="attribute-footer">
<span>{{ localize "SW5E.Proficiency" }}</span>
<span>{{numberFormat data.attributes.prof decimals=0 sign=true}}</span>
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{ localize "SW5E.Speed" }}</h4>
<div class="attribute-value">
<input name="data.attributes.speed.value" type="text"
value="{{data.attributes.speed.value}}" placeholder="0"/>
</div>
<footer class="attribute-footer">
<input type="text" class="speed" name="data.attributes.speed.special"
value="{{data.attributes.speed.special}}" placeholder="{{ localize 'SW5E.SpeedSpecial' }}"/>
</footer>
</li>
</ul>
</section>
</header>
{{!-- NPC Sheet Navigation --}}
@ -50,11 +93,11 @@
{{#each data.abilities as |ability id|}}
<li class="ability {{#if ability.proficient}}proficient{{/if}}" data-ability="{{id}}">
<h4 class="ability-name box-title rollable">{{ability.label}}</h4>
<input class="ability-score" name="data.abilities.{{id}}.value" type="text" value="{{ability.value}}" data-dtype="Number" placeholder="10"/>
<input class="ability-score" name="data.abilities.{{id}}.value" type="number" value="{{ability.value}}" placeholder="10"/>
<div class="ability-modifiers flexrow">
<span class="ability-mod" title="Modifier">{{numberFormat ability.mod decimals=0 sign=true}}</span>
<input type="hidden" name="data.abilities.{{id}}.proficient" value="{{ability.proficient}}" data-dtype="Number"/>
<a class="proficiency-toggle ability-proficiency" title="Proficiency">{{{ability.icon}}}</a>
<a class="proficiency-toggle ability-proficiency" title="{{ localize 'SW5E.Proficiency' }}">{{{ability.icon}}}</a>
<span class="ability-save" title="Saving Throw">{{numberFormat ability.save decimals=0 sign=true}}</span>
</div>
</li>
@ -69,76 +112,30 @@
<a class="proficiency-toggle skill-proficiency" title="{{skill.hover}}">{{{skill.icon}}}</a>
<h4 class="skill-name rollable">{{skill.label}}</h4>
<span class="skill-ability">{{skill.ability}}</span>
<span class="skill-mod">{{numberFormat skill.mod decimals=0 sign=true}}</span>
<span class="skill-mod">{{numberFormat skill.total decimals=0 sign=true}}</span>
<span class="skill-passive">({{skill.passive}})</span>
</li>
{{/each}}
</ul>
<section class="center-pane">
{{!-- Attributes --}}
<ul class="attributes flexrow">
<li class="attribute health">
<h4 class="attribute-name box-title rollable">{{ localize "SW5E.Health" }}</h4>
<div class="attribute-value multiple">
<input name="data.attributes.hp.value" type="text" value="{{data.attributes.hp.value}}"
data-dtype="Number" placeholder="10"/>
<span class="sep"> / </span>
<input name="data.attributes.hp.max" type="text" value="{{data.attributes.hp.max}}"
data-dtype="Number" placeholder="10"/>
</div>
<footer class="attribute-footer">
<input name="data.attributes.hp.formula" class="hpformula" type="text" placeholder="{{ localize 'SW5E.HealthFormula' }}"
value="{{data.attributes.hp.formula}}"/>
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{ localize "SW5E.ArmorClass" }}</h4>
<div class="attribute-value">
<input name="data.attributes.ac.value" type="text" value="{{data.attributes.ac.value}}"
data-dtype="Number" placeholder="10"/>
</div>
<footer class="attribute-footer">
<span>{{ localize "SW5E.Proficiency" }}</span>
<span>{{numberFormat data.attributes.prof decimals=0 sign=true}}</span>
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{ localize "SW5E.Speed" }}</h4>
<div class="attribute-value">
<input name="data.attributes.speed.value" type="text"
value="{{data.attributes.speed.value}}" placeholder="0"/>
</div>
<footer class="attribute-footer">
<input type="text" class="speed" name="data.attributes.speed.special"
value="{{data.attributes.speed.special}}" placeholder="{{ localize 'SW5E.SpeedSpecial' }}"/>
</footer>
</li>
</ul>
<section class="center-pane flexcol">
{{!-- Legendary Actions --}}
<div class="counters flexrow">
<div class="counters">
<div class="counter flexrow legendary">
<h4>{{ localize "SW5E.LegAct" }}</h4>
<div class="counter-value">
<input type="text" name="data.resources.legact.value" data-dtype="Number" placeholder="0"
value="{{data.resources.legact.value}}"/>
<input name="data.resources.legact.value" type="number" value="{{data.resources.legact.value}}" placeholder="0"/>
<span class="sep">/</span>
<input type="text" name="data.resources.legact.max" data-dtype="Number" placeholder="0"
value="{{data.resources.legact.max}}"/>
<input name="data.resources.legact.max" type="number" value="{{data.resources.legact.max}}" placeholder="0"/>
</div>
</div>
<div class="counter flexrow legendary">
<h4>{{ localize "SW5E.LegRes" }}</h4>
<div class="counter-value">
<input type="text" name="data.resources.legres.value" data-dtype="Number" placeholder="0"
value="{{data.resources.legres.value}}"/>
<input name="data.resources.legres.value" type="number" value="{{data.resources.legres.value}}" placeholder="0"/>
<span class="sep">/</span>
<input type="text" name="data.resources.legres.max" data-dtype="Number" placeholder="0"
value="{{data.resources.legres.max}}"/>
<input name="data.resources.legres.max" type="number" value="{{data.resources.legres.max}}" placeholder="0"/>
</div>
</div>
<div class="counter flexrow lair">
@ -146,9 +143,8 @@
<div class="counter-value">
<input name="data.resources.lair.value" type="checkbox" value="{{data.resources.lair.value}}"
data-dtype="Boolean" {{checked data.resources.lair.value}}/>
<input name="data.resources.lair.initiative" type="text" value="{{data.resources.lair.initiative}}"
data-dtype="Number" placeholder="Init."/>
</div>
<input name="data.resources.lair.initiative" type="number" value="{{data.resources.lair.initiative}}" placeholder="20"/>
</div>
</div>
</div>

View file

@ -1,6 +1,6 @@
<div class="inventory-filters">
<div class="inventory-filters flexrow">
{{#if isCharacter}}
{{#unless isNPC}}
<ol class="currency flexrow">
<h3>
{{localize "SW5E.Currency"}}
@ -11,15 +11,16 @@
<input type="text" name="data.currency.{{k}}" value="{{v}}" data-dtype="Number"/>
{{/each}}
</ol>
{{/if}}
{{/unless}}
{{#unless isVehicle}}
<ul class="filter-list flexrow" data-filter="inventory">
<li class="filter-title">{{localize "SW5E.Filter"}}</li>
<li class="filter-item" data-filter="action">{{localize "SW5E.Action"}}</li>
<li class="filter-item" data-filter="bonus">{{localize "SW5E.BonusAction"}}</li>
<li class="filter-item" data-filter="reaction">{{localize "SW5E.Reaction"}}</li>
<li class="filter-item" data-filter="equipped">{{localize "SW5E.Equipped"}}</li>
</ul>
{{/unless}}
</div>
<ol class="inventory-list">
@ -27,16 +28,23 @@
<li class="inventory-header flexrow">
<h3 class="item-name flexrow">{{localize section.label}}</h3>
{{#if section.columns}}
{{#each section.columns}}
<div class="item-detail {{css}}">{{label}}</div>
{{/each}}
{{else}}
{{#if ../isCharacter}}
<div class="item-detail item-weight">{{localize "SW5E.Weight"}}</div>
{{/if}}
<div class="item-detail item-uses">{{localize "SW5E.Charges"}}</div>
<div class="item-detail item-action">{{localize "SW5E.Usage"}}</div>
{{/if}}
{{#if ../owner}}
<div class="item-controls">
<a class="item-control item-create" title='{{localize "SW5E.ItemCreate"}}' {{#each section.dataset as |v k|}}data-{{k}}="{{v}}"{{/each}}>
<a class="item-control item-create" title='{{localize "SW5E.ItemCreate"}}'
{{#each section.dataset as |v k|}}data-{{k}}="{{v}}"{{/each}}>
<i class="fas fa-plus"></i> {{localize "SW5E.Add"}}
</a>
</div>
@ -45,16 +53,35 @@
<ol class="item-list">
{{#each section.items as |item iid|}}
<li class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow rollable">
<div class="item-image" style="background-image: url({{item.img}})"></div>
<li class="item flexrow {{section.css}}"
data-item-id="{{#if section.editableName}}{{iid}}{{else}}{{item._id}}{{/if}}">
<div class="item-name flexrow rollable">
{{#if section.editableName}}
<input type="text" value="{{item.name}}">
{{else}}
<div class="item-image" style="background-image: url('{{item.img}}')"></div>
<h4>
{{item.name~}}
{{~#if item.isStack}} ({{item.data.quantity}}){{/if}}
{{~#if item.data.attuned}} <i class="fas fa-sun attuned" title={{localize "SW5E.Attuned"}}></i>{{/if}}
</h4>
{{/if}}
</div>
{{#if section.columns}}
{{#each section.columns}}
<div class="item-detail {{css}}">
{{#with (getProperty item property)}}
{{#if ../editable}}
<input type="text" value="{{this}}" placeholder="&mdash;"
data-dtype="{{../editable}}" data-property="{{../property}}">
{{else}}
{{this}}
{{/if}}
{{/with}}
</div>
{{/each}}
{{else}}
{{#if ../../isCharacter}}
<div class="item-detail item-weight">
{{#if item.totalWeight}}
@ -77,11 +104,16 @@
{{item.labels.activation}}
{{/if}}
</div>
{{/if}}
{{#if ../../owner}}
<div class="item-controls">
{{#unless @root.isVehicle}}
<a class="item-control item-toggle {{item.toggleClass}}" title='{{item.toggleTitle}}'><i class="fas fa-shield-alt"></i></a>
{{/unless}}
{{#unless section.editableName}}
<a class="item-control item-edit" title='{{localize "SW5E.ItemEdit"}}'><i class="fas fa-edit"></i></a>
{{/unless}}
<a class="item-control item-delete" title='{{localize "SW5E.ItemDelete"}}'><i class="fas fa-trash"></i></a>
</div>
{{/if}}
@ -92,7 +124,7 @@
</ol>
{{#if isCharacter }}
{{#unless isNPC}}
{{#with data.attributes.encumbrance}}
<div class="encumbrance {{#if encumbered}}encumbered{{/if}}">
<span class="encumbrance-bar" style="width:{{pct}}%"></span>
@ -103,4 +135,4 @@
<i class="encumbrance-breakpoint encumbrance-66 arrow-down"></i>
</div>
{{/with}}
{{/if}}
{{/unless}}

View file

@ -1,6 +1,12 @@
<div class="inventory-filters powerbook-filters">
<div class="inventory-filters powerbook-filters flexrow">
<div class="form-group powercasting-ability">
<h3>{{localize "SW5E.PowerAbility"}}</h3>
{{#unless isNPC}}
<label>{{localize "SW5E.PowerAbility"}}</label>
{{else}}
<label>{{localize "SW5E.Level"}}</label>
<input class="powercasting-level" type="text" name="data.details.powerLevel"
value="{{data.details.powerLevel}}" data-dtype="Number" placeholder="0"/>
{{/unless}}
<select name="data.attributes.powercasting" data-type="String">
{{#select data.attributes.powercasting}}
<option value="">{{localize "SW5E.None"}}</option>
@ -9,16 +15,9 @@
{{/each}}
{{/select}}
</select>
{{#if isNPC}}
<h3>{{localize "SW5E.PowercasterLevel"}}</h3>
<input class="powercasting-level" type="text" name="data.details.powerLevel"
value="{{data.details.powerLevel}}" data-dtype="Number" placeholder="0"/>
{{/if}}
<h3 class="power-dc">{{localize "SW5E.PowerDC"}} {{data.attributes.powerdc}}</h3>
</div>
<ul class="filter-list flexrow" data-filter="powerbook">
<li class="filter-title">{{localize "SW5E.Filter"}}</li>
<li class="filter-item" data-filter="action">{{localize "SW5E.Action"}}</li>
<li class="filter-item" data-filter="bonus">{{localize "SW5E.BonusAction"}}</li>
<li class="filter-item" data-filter="reaction">{{localize "SW5E.Reaction"}}</li>
@ -48,7 +47,7 @@
</a>
{{/if}}
{{ else }}
<span class="power-slots">{{{section.uses}}}</span>
<span>{{{section.uses}}}</span>
<span class="sep"> / </span>
<span class="power-max">{{{section.slots}}}</span>
{{/if}}
@ -71,9 +70,9 @@
{{#each section.powers as |item i|}}
<li class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow rollable">
<div class="item-image" style="background-image: url({{item.img}})"></div>
<div class="item-image" style="background-image: url('{{item.img}}')"></div>
<h4>{{item.name}}</h4>
{{#if item.data.uses.value }}
{{#if item.data.uses.per }}
<div class="item-detail power-uses">Uses {{item.data.uses.value}} / {{item.data.uses.max}}</div>
{{/if}}
</div>

View file

@ -10,95 +10,115 @@
</select>
</div>
{{#unless isVehicle}}
<div class="form-group {{#unless data.traits.senses}}inactive{{/unless}}">
<label>{{localize "SW5E.Senses"}}</label>
<input type="text" name="data.traits.senses" value="{{data.traits.senses}}" placeholder="{{ localize 'SW5E.None' }}"/>
</div>
<div class="form-group {{data.traits.languages.cssClass}}">
<label for="data.traits.languages">{{localize "SW5E.Languages"}}</label>
<label>{{localize "SW5E.Languages"}}</label>
<a class="trait-selector" data-options="languages" data-target="data.traits.languages">
<i class="fas fa-edit"></i>
</a>
<ul class="traits-list">
{{#each data.traits.languages.selected as |v k|}}
<li class="tag {{k}}">{{v}}</li>
{{/each}}
</ul>
<a class="trait-selector" data-options="languages"><i class="fas fa-edit"></i></a>
</div>
{{/unless}}
<div class="form-group {{data.traits.di.cssClass}}">
<label for="data.traits.di">{{localize "SW5E.DamImm"}}</label>
<label>{{localize "SW5E.DamImm"}}</label>
<a class="trait-selector" data-options="damageResistanceTypes" data-target="data.traits.di">
<i class="fas fa-edit"></i>
</a>
<ul class="traits-list">
{{#each data.traits.di.selected as |v k|}}
<li class="tag {{k}}">{{v}}</li>
{{/each}}
</ul>
<a class="trait-selector" data-options="damageTypes"><i class="fas fa-edit"></i></a>
</div>
<div class="form-group {{data.traits.dr.cssClass}}">
<label for="data.traits.dr">{{localize "SW5E.DamRes"}}</label>
<label>{{localize "SW5E.DamRes"}}</label>
<a class="trait-selector" data-options="damageResistanceTypes" data-target="data.traits.dr">
<i class="fas fa-edit"></i>
</a>
<ul class="traits-list">
{{#each data.traits.dr.selected as |v k|}}
<li class="tag {{k}}">{{v}}</li>
{{/each}}
</ul>
<a class="trait-selector" data-options="damageTypes"><i class="fas fa-edit"></i></a>
</div>
<div class="form-group {{data.traits.dv.cssClass}}">
<label for="data.traits.dv">{{localize "SW5E.DamVuln"}}</label>
<label>{{localize "SW5E.DamVuln"}}</label>
<a class="trait-selector" data-options="damageResistanceTypes" data-target="data.traits.dv">
<i class="fas fa-edit"></i>
</a>
<ul class="traits-list">
{{#each data.traits.dv.selected as |v k|}}
<li class="tag {{k}}">{{v}}</li>
{{/each}}
</ul>
<a class="trait-selector" data-options="damageTypes"><i class="fas fa-edit"></i></a>
</div>
<div class="form-group {{data.traits.ci.cssClass}}">
<label for="data.traits.ci">{{localize "SW5E.ConImm"}}</label>
<label>{{localize "SW5E.ConImm"}}</label>
<a class="trait-selector" data-options="conditionTypes" data-target="data.traits.ci">
<i class="fas fa-edit"></i>
</a>
<ul class="traits-list">
{{#each data.traits.ci.selected as |v k|}}
<li class="tag {{k}}">{{v}}</li>
{{/each}}
</ul>
<a class="trait-selector" data-options="conditionTypes"><i class="fas fa-edit"></i></a>
</div>
{{#if isCharacter}}
<div class="form-group {{data.traits.weaponProf.cssClass}}">
<label for="data.traits.weaponProf">{{localize "SW5E.TraitWeaponProf"}}</label>
<label>{{localize "SW5E.TraitWeaponProf"}}</label>
<a class="trait-selector" data-options="weaponProficiencies" data-target="data.traits.weaponProf">
<i class="fas fa-edit"></i>
</a>
<ul class="traits-list">
{{#each data.traits.weaponProf.selected as |v k|}}
<li class="tag {{k}}">{{v}}</li>
{{/each}}
</ul>
<a class="trait-selector" data-options="weaponProficiencies"><i class="fas fa-edit"></i></a>
</div>
<div class="form-group {{data.traits.armorProf.cssClass}}">
<label for="data.traits.armorProf">{{localize "SW5E.TraitArmorProf"}}</label>
<label>{{localize "SW5E.TraitArmorProf"}}</label>
<a class="trait-selector" data-options="armorProficiencies" data-target="data.traits.armorProf">
<i class="fas fa-edit"></i>
</a>
<ul class="traits-list">
{{#each data.traits.armorProf.selected as |v k|}}
<li class="tag {{k}}">{{v}}</li>
{{/each}}
</ul>
<a class="trait-selector" data-options="armorProficiencies"><i class="fas fa-edit"></i></a>
</div>
<div class="form-group {{data.traits.toolProf.cssClass}}">
<label for="data.traits.toolProf">{{localize "SW5E.TraitToolProf"}}</label>
<label>{{localize "SW5E.TraitToolProf"}}</label>
<a class="trait-selector" data-options="toolProficiencies" data-target="data.traits.toolProf">
<i class="fas fa-edit"></i>
</a>
<ul class="traits-list">
{{#each data.traits.toolProf.selected as |v k|}}
<li class="tag {{k}}">{{v}}</li>
{{/each}}
</ul>
<a class="trait-selector" data-options="toolProficiencies"><i class="fas fa-edit"></i></a>
</div>
{{/if}}
{{#unless isVehicle}}
<div class="form-group ">
<label>{{localize "SW5E.SpecialTraits"}}</label>
<a class="configure-flags"><i class="fas fa-cog"></i></a>
</div>
</div>
{{/unless}}
</div>

View file

@ -0,0 +1,159 @@
<form class="{{cssClass}} flexcol" autocomplete="off">
<header class="sheet-header flexrow">
<img class="profile" src="{{actor.img}}" title="{{actor.name}}" alt="{{actor.name}}"
data-edit="img">
<section class="header-details flexrow">
<h1 class="charnam">
<input name="name" type="text" value="{{actor.name}}"
placeholder="{{localize 'SW5E.Name'}}">
</h1>
<ul class="summary flexrow">
<li>
<span>{{lookup config.actorSizes data.traits.size}}</span>
</li>
<li>
<span>{{localize 'SW5E.Vehicle'}}</span>
</li>
<li>
<input type="text" name="data.traits.dimensions"
value="{{data.traits.dimensions}}"
placeholder="{{localize 'SW5E.Dimensions'}}">
</li>
<li>
<input type="text" name="data.details.source"
value="{{data.details.source}}"
placeholder="{{localize 'SW5E.Source'}}">
</li>
</ul>
<ul class="attributes flexrow">
<li class="attribute health">
<h4 class="attribute-name box-title">{{localize 'SW5E.Health'}}</h4>
<div class="attribute-value multiple">
<input name="data.attributes.hp.value" type="text" placeholder="&mdash;"
value="{{data.attributes.hp.value}}" data-dtype="Number">
<span class="sep"> &sol; </span>
<input name="data.attributes.hp.max" type="text" placeholder="&mdash;"
value="{{data.attributes.hp.max}}" data-dtype="Number">
</div>
<footer class="attribute-footer">
<input name="data.attributes.hp.dt" type="text" class="temphp"
placeholder="{{localize 'SW5E.Threshold'}}"
value="{{data.attributes.hp.dt}}" data-dtype="Number">
<input name="data.attributes.hp.mt" type="text" class="temphp"
placeholder="{{localize 'SW5E.VehicleMishap'}}"
value="{{data.attributes.hp.mt}}" data-dtype="Number">
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{localize 'SW5E.ArmorClass'}}</h4>
<div class="attribute-value">
<input name="data.attributes.ac.value" type="text" placeholder="&mdash;"
value="{{data.attributes.ac.value}}" data-dtype="Number">
</div>
<footer class="attribute-footer">
<input type="text" name="data.attributes.ac.motionless"
placeholder="&mdash;" value="{{data.attributes.ac.motionless}}">
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{localize 'SW5E.Speed'}}</h4>
<div class="attribute-value">
<input name="data.attributes.speed.value" type="text" placeholder="&mdash;"
value="{{data.attributes.speed.value}}">
</div>
</li>
</ul>
</section>
</header>
<nav class="sheet-navigation tabs" data-group="primary">
<a class="item active" data-tab="attributes">{{localize 'SW5E.Attributes'}}</a>
<a class="item" data-tab="features">{{localize 'SW5E.Features'}}</a>
<a class="item" data-tab="cargo">{{localize 'SW5E.VehicleCargoCrew'}}</a>
<a class="item" data-tab="biography">{{localize 'SW5E.Description'}}</a>
</nav>
<section class="sheet-body">
<div class="tab attributes flexrow" data-group="primary" data-tab="attributes">
<ul class="ability-scores flexrow">
{{#each data.abilities as |ability id|}}
<li class="ability" data-ability="{{id}}">
<h4 class="ability-name box-title rollable">{{ability.label}}</h4>
<input class="ability-score" name="data.abilities.{{id}}.value" type="text"
value="{{ability.value}}" data-dtype="Number" placeholder="0">
<div class="ability-modifiers flexrow">
<span class="ability-mod" title="{{localize 'SW5E.Modifier'}}">
{{numberFormat ability.mod decimals=0 sign=true}}
</span>
</div>
</li>
{{/each}}
</ul>
<section class="center-pane flexcol">
<div class="counters">
<div class="counter flexrow creature-cap">
<h4>{{localize 'SW5E.VehicleCreatureCapacity'}}</h4>
<div class="counter-value">
<input type="text" placeholder="&mdash;"
name="data.attributes.capacity.creature"
value="{{data.attributes.capacity.creature}}">
</div>
</div>
<div class="counter flexrow cargo-cap">
<h4>{{localize 'SW5E.VehicleCargoCapacity'}}</h4>
<div class="counter-value">
<input type="text" name="data.attributes.capacity.cargo" placeholder="0"
data-dtype="Number" value="{{data.attributes.capacity.cargo}}">
</div>
</div>
<div class="counter flexrow stations">
<h4>{{localize 'SW5E.VehicleActionStations'}}</h4>
<div class="counter-value">
<input name="data.attributes.actions.stations" type="checkbox"
data-dtype="Boolean" value="{{data.attributes.actions.stations}}"
{{checked data.attributes.actions.stations}}>
</div>
</div>
<div class="counter flexrow actions">
<h4>{{localize 'SW5E.ActionPl'}}</h4>
<div class="counter-value">
<input type="text" name="data.attributes.actions.value" placeholder="0"
data-dtype="Number" value="{{data.attributes.actions.value}}">
</div>
</div>
<div class="counter flexrow action-thresholds">
<h4>{{localize 'SW5E.VehicleActionThresholds'}}</h4>
<div class="counter-value">
<span class="sep">&lt;</span>
<input type="text" placeholder="&mdash;" data-dtype="Number"
value="{{data.attributes.actions.thresholds.[2]}}"
name="data.attributes.actions.thresholds.2">
<span class="sep">&lt;</span>
<input type="text" placeholder="&mdash;" data-dtype="Number"
value="{{data.attributes.actions.thresholds.[1]}}"
name="data.attributes.actions.thresholds.1">
<span class="sep">&lt;</span>
<input type="text" placeholder="&mdash;" data-dtype="Number"
value="{{data.attributes.actions.thresholds.[0]}}"
name="data.attributes.actions.thresholds.0">
</div>
</div>
</div>
{{> 'systems/sw5e/templates/actors/parts/actor-traits.html'}}
</section>
</div>
<div class="tab features flexcol" data-group="primary" data-tab="features">
{{> 'systems/sw5e/templates/actors/parts/actor-features.html' sections=features}}
</div>
<div class="tab cargo flexcol" data-group="primary" data-tab="cargo">
{{> 'systems/sw5e/templates/actors/parts/actor-inventory.html' sections=cargo}}
</div>
<div class="tab biography flexcol" data-group="primary" data-tab="biography">
{{editor content=data.details.biography.value target='data.details.biography.value'
button=true owner=owner editable=editable}}
</div>
</section>
</form>

View file

@ -1,26 +1,41 @@
<form id="ability-use-form">
<p>{{ localize "SW5E.AbilityUseHint" }} <strong>{{item.name}}</strong> {{ localize "SW5E.Ability" }}.</p>
<p>{{ title }}</p>
<p class="notes">{{note}}</p>
{{#each errors}}
<p class="notification error">{{localize this}}</p>
{{/each}}
<p type="note">
{{#if recharges}}
{{ localize "SW5E.AbilityUseRechargeHint" }} <strong>{{#if isCharged}}{{ localize "SW5E.AbilityUseCharged" }}{{else}}{{ localize "SW5E.AbilityUseDepleted" }}{{/if}}</strong>.</p>
{{else}}
{{ localize "SW5E.AbilityUseWarnStart" }} <strong>{{uses.value}} {{ localize "SW5E.of" }} {{uses.max}}</strong> {{ localize "SW5E.AbilityUseWarnEnd" }} {{perLabel}}.
{{/if}}
{{#unless canUse}}
{{ localize "SW5E.AbilityUseCantUse" }}
{{/unless}}
</p>
{{#if canUpcast}}
<div class="form-group">
<label>{{ localize "SW5E.PowerCastUpcast" }}</label>
<div class="form-fields">
<select name="level" {{#unless canUpcast}}disabled{{/unless}}>
{{#select item.data.level}}
{{#each powerLevels as |l|}}
<option value="{{l.level}}" {{#unless l.canCast}}disabled{{/unless}}>{{l.label}}</option>
{{/each}}
{{/select}}
</select>
</div>
</div>
<div class="form-group">
<label class="checkbox">{{ localize "SW5E.AbilityUseConsume" }} <input type="checkbox" name="consume" {{checked consume}}/></label>
{{#if hasPlaceableTemplate}}
<div class="form-group">
<label class="checkbox">{{ localize "SW5E.PlaceTemplate" }}
<input type="checkbox" name="placeTemplate" checked/>
</label>
</div>
{{/if}}
<label class="checkbox"><input type="checkbox" name="consumeSlot" checked/>{{ localize "SW5E.PowerCastConsume" }}</label>
</div>
{{/if}}
{{#if hasLimitedUses}}
<div class="form-group">
<label class="checkbox"><input type="checkbox" name="consumeUse" checked/>{{ localize "SW5E.AbilityUseConsume" }}</label>
</div>
{{/if}}
{{#if hasPlaceableTemplate}}
<div class="form-group">
<label class="checkbox">
<input type="checkbox" name="placeTemplate" checked/>
{{ localize "SW5E.PlaceTemplate" }}
</label>
</div>
{{/if}}
</form>

View file

@ -24,7 +24,7 @@
<input type="text" name="{{key}}" value="{{flag.value}}" placeholder="{{flag.placeholder}}" data-dtype="{{flag.type}}"/>
{{/if}}
<p class="notes">{{flag.hint}}</p>
<p class="notes">{{localize flag.hint}}</p>
</div>
{{/each}}
{{/each}}

View file

@ -0,0 +1,20 @@
<form id="long-rest" class="dialog-content" onsubmit="event.preventDefault();">
<p>Take a long rest? On a long rest you will recover hit points, half your maximum hit dice, class resources, limited use item charges, and power points.</p>
{{#if promptNewDay}}
<div class="form-group">
<label>Is New Day?</label>
<input type="checkbox" name="newDay" {{checked newDay}}/>
<p class="hint">Recover limited use abilities which recharge "per day"?</p>
</div>
{{/if}}
<div class="dialog-buttons">
{{#each buttons as |button id|}}
<button class="dialog-button" data-button="{{id}}">
{{{button.icon}}}
{{{button.label}}}
</button>
{{/each}}
</div>
</form>

View file

@ -18,6 +18,15 @@
<p class="notes">{{ localize "SW5E.ShortRestNoHD" }}</p>
{{/unless}}
</div>
{{#if promptNewDay}}
<div class="form-group">
<label>Is New Day?</label>
<input type="checkbox" name="newDay" {{checked newDay}}/>
<p class="hint">Recover limited use abilities which recharge "per day"?</p>
</div>
{{/if}}
<div class="dialog-buttons">
{{#each buttons as |button id|}}
<button class="dialog-button" data-button="{{id}}">

View file

@ -1,34 +0,0 @@
<form id="spell-config-form">
<p>{{ localize "DND5E.SpellCastHint" }} <strong>{{item.name}}</strong> {{ localize "DND5E.spell" }}.</p>
{{#unless canCast}}
<p class="notification error">{{ localize "DND5E.SpellCastNoSlots" }}</p>
{{/unless}}
<div class="form-group">
<label>{{ localize "DND5E.SpellCastUpcast" }}</label>
<div class="form-fields">
<select name="level">
{{#select item.data.level}}
{{#each spellLevels as |l|}}
<option value="{{l.level}}" {{#unless l.canCast}}disabled{{/unless}}>{{l.label}}</option>
{{/each}}
{{/select}}
</select>
</div>
</div>
<div class="form-group">
{{#if canUpcast}}
<label class="checkbox">{{ localize "DND5E.SpellCastConsume" }} <input type="checkbox" name="consume" {{checked canCast}}/></label>
{{/if}}
{{#if hasPlaceableTemplate}}
<div class="form-group">
<label class="checkbox">{{ localize "DND5E.PlaceTemplate" }}
<input type="checkbox" name="placeTemplate" checked/>
</label>
</div>
{{/if}}
</div>
</form>

View file

@ -9,9 +9,11 @@
</li>
{{/each}}
</ol>
{{#if allowCustom}}
<div class="form-group stacked">
<label>{{ localize "SW5E.TraitSelectorSpecial" }}</label>
<input type="text" name="custom" value="{{custom}}" data-dtype="String"/>
</div>
{{/if}}
<button type="submit" name="submit" value="1"><i class="far fa-save"></i> {{ localize "SW5E.Save"}}</button>
</form>

View file

@ -28,7 +28,7 @@
{{#if hasSave}}
<button data-action="save" data-ability="{{data.save.ability}}" disabled>
{{ localize "SW5E.Save" }} {{labels.save}}
{{ localize "SW5E.SavingThrow" }} {{labels.save}}
</button>
{{/if}}

View file

@ -14,7 +14,7 @@
<span class="item-status">{{itemStatus}}</span>
</div>
<ul class="summary">
<ul class="summary flexrow">
<li></li>
<li>
<input type="text" name="data.rarity" value="{{data.rarity}}" placeholder="{{ localize 'SW5E.Rarity' }}"/>

View file

@ -0,0 +1,73 @@
<form class="{{cssClass}} flexcol" autocomplete="off">
{{!-- Item Sheet Header --}}
<header class="sheet-header flexrow">
<img class="profile" src="{{item.img}}" title="{{item.name}}" data-edit="img"/>
<div class="header-details flexrow">
<h1 class="charname">
<input name="name" type="text" value="{{item.name}}" placeholder="{{ localize 'SW5E.ItemName' }}"/>
</h1>
<div class="item-subtitle">
<h4 class="item-type">{{itemType}}</h4>
<span class="item-status">{{itemStatus}}</span>
</div>
<ul class="summary">
<li>
{{labels.featType}}
</li>
<li>
<input type="text" name="data.requirements" value="{{data.requirements}}" placeholder="{{ localize 'SW5E.Requirements' }}"/>
</li>
<li>
<input type="text" name="data.source" value="{{data.source}}" placeholder="{{ localize 'SW5E.Source' }}"/>
</li>
</ul>
</div>
</header>
{{!-- Item Sheet Navigation --}}
<nav class="sheet-navigation tabs" data-group="primary">
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
<a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a>
</nav>
{{!-- Item Sheet Body --}}
<section class="sheet-body">
{{!-- Description Tab --}}
{{> "systems/sw5e/templates/items/parts/item-description.html"}}
{{!-- Details Tab --}}
<div class="tab details" data-group="primary" data-tab="details">
<h3 class="form-header">{{ localize "SW5E.FeatureUsage" }}</h3>
{{!-- Item Activation Template --}}
{{> "systems/sw5e/templates/items/parts/item-activation.html"}}
{{!-- Recharge Requirement --}}
{{#if data.activation.type}}
<div class="form-group recharge">
<label>{{ localize "SW5E.FeatureActionRecharge" }}</label>
<div class="form-fields">
<span>{{ localize "SW5E.FeatureRechargeOn" }}</span>
<input type="text" name="data.recharge.value" value="{{data.recharge.value}}"
data-dtype="Number" placeholder="{{ localize 'SW5E.FeatureRechargeResult' }}"/>
<label class="checkbox">
{{ localize "SW5E.Charged" }}
<input type="checkbox" name="data.recharge.charged" {{checked data.recharge.charged}}/>
</label>
</div>
</div>
{{/if}}
<h3 class="form-header">{{ localize "SW5E.FeatureAttack" }}</h3>
{{!-- Item Action Template --}}
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
</div>
</section>
</form>

View file

@ -14,7 +14,7 @@
<span class="item-status">{{itemStatus}}</span>
</div>
<ul class="summary">
<ul class="summary flexrow">
<li>
{{lookup config.equipmentTypes data.armor.type }}
</li>
@ -57,6 +57,7 @@
</select>
</div>
{{#unless isMountable}}
{{!-- Equipment Status --}}
<div class="form-group stacked">
<label>{{ localize "SW5E.ItemEquipmentStatus" }}</label>
@ -73,33 +74,17 @@
<input type="checkbox" name="data.attuned" {{checked data.attuned}}/> {{ localize "SW5E.Attuned" }}
</label>
</div>
{{#unless isWeapon }}
{{!-- ArmorProperties Formula --}}
<div class="form-group stacked weapon-properties">
<h4 class="armorproperties-header">
{{#unless isWeapon }}{{ localize "SW5E.ArmorProperties" }}{{ else }}{{ localize "SW5E.ItemWeaponProperties" }}{{/unless}}
<a class="armorproperties-control add-armorproperties"><i class="fas fa-plus"></i></a>
</h4>
<ol class="armorproperties-parts form-group">
{{#each data.armorproperties.parts as |part i| }}
<li class="armorproperties-part flexrow" data-armorproperties-part="{{i}}">
<select name="data.armorproperties.parts.{{i}}.1">
{{#select (lookup this "1") }}
<option value="">{{ localize "SW5E.None" }}</option>
{{#each ../config.armorpropertiesTypes as |name type|}}
<option value="{{type}}">{{name}}</option>
{{/each}}
{{/select}}
</select>
<input type="text" name="data.armorproperties.parts.{{i}}.0" value="{{lookup this "0"}}"/>
<a class="armorproperties-control delete-armorproperties"><i class="fas fa-minus"></i></a>
</li>
{{/each}}
</ol>
</div>
{{/unless}}
{{/unless}}
{{!-- Armor Properties --}}
<div class="form-group stacked armor-properties">
<label>{{ localize "SW5E.ArmorProperties" }}</label>
{{#each config.armorPropertiesTypes as |name prop|}}
<label class="checkbox">
<input type="checkbox" name="data.properties.{{prop}}" {{checked (lookup ../data.properties prop)}}/> {{ name }}
</label>
{{/each}}
</div>
{{!-- Armor Class --}}
<div class="form-group">
@ -109,6 +94,7 @@
</div>
</div>
{{#unless isMountable}}
{{!-- Dexterity Modifier --}}
<div class="form-group">
<label>{{ localize "SW5E.ItemEquipmentDexMod" }}</label>
@ -130,6 +116,21 @@
<label>{{ localize "SW5E.ItemEquipmentStealthDisav" }}</label>
<input type="checkbox" name="data.stealth" value="1" {{checked data.stealth}}/>
</div>
{{/unless}}
{{#if isMountable}}
{{> 'systems/sw5e/templates/items/parts/item-mountable.html'}}
<div class="form-group">
<label>{{localize 'SW5E.Speed'}}</label>
<div class="form-fields">
<input type="text" name="data.speed.value" value="{{data.speed.value}}"
placeholder="0" data-dtype="Number">
<span class="sep">{{localize 'SW5E.FeetAbbr'}}</span>
<input type="text" name="data.speed.conditions"
value="{{data.speed.conditions}}">
</div>
</div>
{{/if}}
<h3 class="form-header">{{ localize "SW5E.ItemEquipmentUsage" }}</h3>

View file

@ -14,7 +14,7 @@
<span class="item-status">{{itemStatus}}</span>
</div>
<ul class="summary">
<ul class="summary flexrow">
<li>
{{labels.featType}}
</li>

View file

@ -14,7 +14,7 @@
<span class="item-status">{{itemStatus}}</span>
</div>
<ul class="summary">
<ul class="summary flexrow">
<li>
<input type="text" name="data.rarity" value="{{data.rarity}}" placeholder="{{ localize 'SW5E.Rarity' }}"/>
</li>

View file

@ -23,6 +23,21 @@
</div>
</div>
{{#if isCrewed}}
<div class="form-group">
<label>{{localize 'SW5E.Cover'}}</label>
<div class="form-fields">
<select name="data.cover" data-dtype="Number">
{{#select data.cover}}
<option value="">&mdash;</option>
{{#each config.cover as |v k|}}
<option value="{{k}}">{{v}}</option>
{{/each}}
{{/select}}
</select>
</div>
</div>
{{/if}}
{{!-- Ability Target --}}
<div class="form-group input-select-select">
@ -48,6 +63,16 @@
</div>
</div>
{{!-- Ability Target Width --}}
{{#if isLine}}
<div class="form-group input-select-select">
<label>{{ localize "SW5E.TargetWidth" }}</label>
<div class="form-fields">
<input type="text" name="data.target.width" value="{{data.target.width}}" data-dtype="Number" placeholder="-"/>
</div>
</div>
{{/if}}
{{!-- Ability Range --}}
<div class="form-group input-select">
<label>{{ localize "SW5E.Range" }}</label>
@ -99,4 +124,28 @@
</select>
</div>
</div>
{{/if}}
{{!-- Consumption --}}
<div class="form-group uses-per">
<label>{{ localize "SW5E.ConsumeTitle" }}</label>
<div class="form-fields">
<select name="data.consume.type">
{{#select data.consume.type}}
<option value=""></option>
{{#each config.abilityConsumptionTypes as |name key|}}
<option value="{{key}}">{{name}}</option>
{{/each}}
{{/select}}
</select>
<select name="data.consume.target">
{{#select data.consume.target}}
<option value=""></option>
{{#each abilityConsumptionTargets as |name key|}}
<option value="{{key}}">{{name}}</option>
{{/each}}
{{/select}}
</select>
<input type="text" name="data.consume.amount" value="{{data.consume.amount}}" data-dtype="Number"/>
</div>
</div>
{{/if}}

View file

@ -0,0 +1,19 @@
<div class="form-group">
<label>{{localize 'SW5E.Health'}}</label>
<div class="form-fields">
<input type="text" name="data.hp.value" value="{{data.hp.value}}"
placeholder="0" data-dtype="Number">
<span class="sep">&sol;</span>
<input type="text" name="data.hp.max" value="{{data.hp.max}}" placeholder="0"
data-dtype="Number">
<input type="text" name="data.hp.dt" value="{{data.hp.dt}}" data-dtype="Number"
placeholder="{{localize 'SW5E.Threshold'}}">
</div>
</div>
<div class="form-group">
<label>{{localize 'SW5E.HealthConditions'}}</label>
<div class="form-fields">
<input type="text" name="data.hp.conditions" value="{{data.hp.conditions}}">
</div>
</div>

View file

@ -14,7 +14,7 @@
<span class="item-status">{{itemStatus}}</span>
</div>
<ul class="summary">
<ul class="summary flexrow">
<li>
{{labels.level}}
</li>
@ -68,12 +68,29 @@
</select>
</div>
{{!-- Preparation Mode --}}
<div class="form-group input-select">
<label>{{ localize "SW5E.PowerPreparationMode" }}</label>
<div class="form-fields">
<label class="checkbox prepared">
{{ localize "SW5E.PowerPrepared" }} <input type="checkbox" name="data.preparation.prepared" {{checked data.preparation.prepared}}/>
</label>
<select name="data.preparation.mode">
{{#select data.preparation.mode}}
<option value=""></option>
{{#each config.powerPreparationModes as |name key|}}
<option value="{{key}}">{{name}}</option>
{{/each}}
{{/select}}
</select>
</div>
</div>
{{!-- Concentration Mode --}}
<div class="form-group input-select">
<label>{{ localize "SW5E.PowerConcentrationMode" }}</label>
<div class="form-fields">
<label class="checkbox prepared">
{{ localize "SW5E.Concentrated" }} <input type="checkbox" name="data.preparation.prepared" {{checked data.preparation.prepared}}/>
<label class="checkbox">
<input type="checkbox" name="data.components.concentration" {{checked data.components.concentration}}/> {{ localize "SW5E.Concentrated" }}
</label>
</div>
</div>

View file

@ -14,7 +14,7 @@
<span class="item-status">{{itemStatus}}</span>
</div>
<ul class="summary">
<ul class="summary flexrow">
<li>
<input type="text" name="data.rarity" value="{{data.rarity}}" placeholder="{{ localize 'SW5E.Rarity' }}"/>
</li>

View file

@ -14,7 +14,7 @@
<span class="item-status">{{itemStatus}}</span>
</div>
<ul class="summary">
<ul class="summary flexrow">
<li>
{{lookup config.weaponTypes data.weaponType }}
</li>
@ -56,24 +56,27 @@
</select>
</div>
{{#unless isMountable}}
{{!-- Weapon Status --}}
<div class="form-group stacked">
<label>{{ localize "SW5E.ItemWeaponStatus" }}</label>
<label class="checkbox">
<input type="checkbox" name="data.proficient" {{checked data.proficient}}/> {{ localize "SW5E.Proficient" }}
</label>
<label class="checkbox">
<input type="checkbox" name="data.equipped" {{checked data.equipped}}/> {{ localize "SW5E.Equipped" }}
</label>
<label class="checkbox">
<input type="checkbox" name="data.identified" {{checked data.identified}}/> {{ localize "SW5E.Identified" }}
</label>
<label class="checkbox">
<input type="checkbox" name="data.attuned" {{checked data.attuned}}/> {{ localize "SW5E.Attuned" }}
</label>
<div class="form-fields">
<label class="checkbox">
<input type="checkbox" name="data.proficient" {{checked data.proficient}}/> {{ localize "SW5E.Proficient" }}
</label>
<label class="checkbox">
<input type="checkbox" name="data.equipped" {{checked data.equipped}}/> {{ localize "SW5E.Equipped" }}
</label>
<label class="checkbox">
<input type="checkbox" name="data.identified" {{checked data.identified}}/> {{ localize "SW5E.Identified" }}
</label>
<label class="checkbox">
<input type="checkbox" name="data.attuned" {{checked data.attuned}}/> {{ localize "SW5E.Attuned" }}
</label>
</div>
</div>
{{/unless}}
{{#if sss}}
{{!-- Weapon Properties --}}
<div class="form-group stacked weapon-properties">
<label>{{ localize "SW5E.ItemWeaponProperties" }}</label>
@ -81,37 +84,21 @@
<label class="checkbox">
<input type="checkbox" name="data.properties.{{prop}}" {{checked (lookup ../data.properties prop)}}/> {{ name }}
</label>
<label class="text"> <input type="text" maxlength="2" name="wpNum"/></label>
{{/each}}
</div>
{{/if}}
{{#if isWeapon }}
<div class="form-group stacked weapon-properties">
{{!-- weaponproperties Formula --}}
<h4 class="weaponproperties-header">
{{#unless isWeapon }}{{ localize "SW5E.ArmorProperties" }}{{ else }}{{ localize "SW5E.ItemWeaponProperties" }}{{/unless}}
<a class="weaponproperties-control add-weaponproperties"><i class="fas fa-plus"></i></a>
</h4>
<ol class="weaponproperties-parts form-group">
{{#each data.weaponproperties.parts as |part i| }}
<li class="weaponproperties-part flexrow" data-weaponproperties-part="{{i}}">
<select name="data.weaponproperties.parts.{{i}}.1">
{{#select (lookup this "1") }}
<option value="">{{ localize "SW5E.None" }}</option>
{{#each ../config.weaponProperties as |name type|}}
<option value="{{type}}">{{name}}</option>
{{/each}}
{{/select}}
</select>
<input type="text" name="data.weaponproperties.parts.{{i}}.0" value="{{lookup this "0"}}"/>
<a class="weaponproperties-control delete-weaponproperties"><i class="fas fa-minus"></i></a>
</li>
{{/each}}
</ol>
{{/if}}
{{#if isMountable}}
<div class="form-group">
<label>{{localize 'SW5E.ArmorClass'}}</label>
<div class="form-fields">
<input type="text" name="data.armor.value" value="{{data.armor.value}}"
data-dtype="Number">
</div>
</div>
</div>
{{> 'systems/sw5e/templates/items/parts/item-mountable.html'}}
{{/if}}
<h3 class="form-header">{{ localize "SW5E.ItemWeaponUsage" }}</h3>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB