forked from GitHub-Mirrors/foundry-sw5e
Compare commits
20 commits
Develop-VJ
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2212e3c7d8 | ||
![]() |
8c0ad582f7 | ||
![]() |
584767b352 | ||
![]() |
d1b123100e | ||
![]() |
0a9c9f8ef0 | ||
![]() |
ee7418f552 | ||
![]() |
7214e3d260 | ||
![]() |
fc09308d11 | ||
![]() |
5d879f99e2 | ||
![]() |
da5223cab8 | ||
![]() |
84ab6cf478 | ||
![]() |
d39fa6acf2 | ||
![]() |
0a4b6de0fa | ||
![]() |
c33982f97c | ||
![]() |
4f0c6addcf | ||
![]() |
7c03cd4b04 | ||
![]() |
fee77e2172 | ||
![]() |
87d615babc | ||
![]() |
04dcaf332d | ||
![]() |
4ee235566d |
71 changed files with 12008 additions and 10727 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -1,2 +1,3 @@
|
||||||
{
|
{
|
||||||
|
"editor.formatOnSave": true
|
||||||
}
|
}
|
45
lang/en.json
45
lang/en.json
|
@ -19,7 +19,6 @@
|
||||||
"ITEM.TypeLoot": "Loot",
|
"ITEM.TypeLoot": "Loot",
|
||||||
"ITEM.TypePower": "Power",
|
"ITEM.TypePower": "Power",
|
||||||
"ITEM.TypeSpecies": "Species",
|
"ITEM.TypeSpecies": "Species",
|
||||||
"ITEM.TypeStarship": "Starship",
|
|
||||||
"ITEM.TypeStarshipfeature": "Starship Feature",
|
"ITEM.TypeStarshipfeature": "Starship Feature",
|
||||||
"ITEM.TypeStarshipfeaturePl": "Starship Features",
|
"ITEM.TypeStarshipfeaturePl": "Starship Features",
|
||||||
"ITEM.TypeStarshipmod": "Starship Modification",
|
"ITEM.TypeStarshipmod": "Starship Modification",
|
||||||
|
@ -332,17 +331,7 @@
|
||||||
"SW5E.Default": "Default",
|
"SW5E.Default": "Default",
|
||||||
"SW5E.DefaultAbilityCheck": "Default Ability Check",
|
"SW5E.DefaultAbilityCheck": "Default Ability Check",
|
||||||
"SW5E.Deployment": "Deployment",
|
"SW5E.Deployment": "Deployment",
|
||||||
"SW5E.DeploymentAcceptSettings": "Deploy Actor",
|
|
||||||
"SW5E.DeploymentPl": "Deployments",
|
"SW5E.DeploymentPl": "Deployments",
|
||||||
"SW5E.DeploymentPromptTitle": "Deploying Actor",
|
|
||||||
"SW5E.DeploymentTypeCoordinator": "Coordinator",
|
|
||||||
"SW5E.DeploymentTypeCrew": "Crew",
|
|
||||||
"SW5E.DeploymentTypeGunner": "Gunner",
|
|
||||||
"SW5E.DeploymentTypeMechanic": "Mechanic",
|
|
||||||
"SW5E.DeploymentTypeOperator": "Operator",
|
|
||||||
"SW5E.DeploymentTypePilot": "Pilot",
|
|
||||||
"SW5E.DeploymentTypePassenger" : "Passenger",
|
|
||||||
"SW5E.DeploymentTypeTechnician": "Technician",
|
|
||||||
"SW5E.description": "A comprehensive game system for running games of Star Wars 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.Description": "Description",
|
"SW5E.Description": "Description",
|
||||||
"SW5E.DestructionSave": "Destruction Saves",
|
"SW5E.DestructionSave": "Destruction Saves",
|
||||||
|
@ -533,12 +522,11 @@
|
||||||
"SW5E.ForcePowerbook": "Force Powers",
|
"SW5E.ForcePowerbook": "Force Powers",
|
||||||
"SW5E.Formula": "Formula",
|
"SW5E.Formula": "Formula",
|
||||||
"SW5E.FuelCapacity": "Fuel Capacity",
|
"SW5E.FuelCapacity": "Fuel Capacity",
|
||||||
"SW5E.FuelCostsMod": "Fuel Costs Modifier",
|
|
||||||
"SW5E.FuelCostPerUnit": "Fuel Cost per Unit",
|
"SW5E.FuelCostPerUnit": "Fuel Cost per Unit",
|
||||||
|
"SW5E.FuelCostsMod": "Fuel Costs Modifier",
|
||||||
"SW5E.GrantedAbilities": "Granted Abilities",
|
"SW5E.GrantedAbilities": "Granted Abilities",
|
||||||
"SW5E.HalfProficient": "Half Proficient",
|
"SW5E.HalfProficient": "Half Proficient",
|
||||||
"SW5E.HardpointSizeMod": "Hardpoint Size Modifier",
|
"SW5E.HardpointSizeMod": "Hardpoint Size Modifier",
|
||||||
"SW5E.HardpointsPerRound": "Max Hardpoints Fired Per Round",
|
|
||||||
"SW5E.Healing": "Healing",
|
"SW5E.Healing": "Healing",
|
||||||
"SW5E.HealingTemp": "Healing (Temporary)",
|
"SW5E.HealingTemp": "Healing (Temporary)",
|
||||||
"SW5E.Health": "Health",
|
"SW5E.Health": "Health",
|
||||||
|
@ -555,9 +543,6 @@
|
||||||
"SW5E.HP": "Health",
|
"SW5E.HP": "Health",
|
||||||
"SW5E.HPFormula": "Health Formula",
|
"SW5E.HPFormula": "Health Formula",
|
||||||
"SW5E.HullDice": "Hull Dice",
|
"SW5E.HullDice": "Hull Dice",
|
||||||
"SW5E.HullDiceRoll": "Roll Hull Dice",
|
|
||||||
"SW5E.HullDiceUsed": "Hull Dice Used",
|
|
||||||
"SW5E.HullDiceWarn": "{name} has no available {formula} Hull Dice remaining!",
|
|
||||||
"SW5E.HullPoints": "Hull Points",
|
"SW5E.HullPoints": "Hull Points",
|
||||||
"SW5E.HullPointsFormula": "Hull Points Formula",
|
"SW5E.HullPointsFormula": "Hull Points Formula",
|
||||||
"SW5E.HyperdriveClass": "Hyperdrive Class",
|
"SW5E.HyperdriveClass": "Hyperdrive Class",
|
||||||
|
@ -633,7 +618,6 @@
|
||||||
"SW5E.ItemTypePowerPl": "Powers",
|
"SW5E.ItemTypePowerPl": "Powers",
|
||||||
"SW5E.ItemTypeSpecies": "Species",
|
"SW5E.ItemTypeSpecies": "Species",
|
||||||
"SW5E.ItemTypeSpeciesPl": "Species",
|
"SW5E.ItemTypeSpeciesPl": "Species",
|
||||||
"SW5E.ItemTypeStarship": "Starship",
|
|
||||||
"SW5E.ItemTypeStarshipMod": "Starship Modification",
|
"SW5E.ItemTypeStarshipMod": "Starship Modification",
|
||||||
"SW5E.ItemTypeStarshipModPl": "Starship Modifications",
|
"SW5E.ItemTypeStarshipModPl": "Starship Modifications",
|
||||||
"SW5E.ItemTypeTool": "Tool",
|
"SW5E.ItemTypeTool": "Tool",
|
||||||
|
@ -863,10 +847,10 @@
|
||||||
"SW5E.PowerCreate": "Create Power",
|
"SW5E.PowerCreate": "Create Power",
|
||||||
"SW5E.PowerDC": "Power DC",
|
"SW5E.PowerDC": "Power DC",
|
||||||
"SW5E.PowerDetails": "Power Details",
|
"SW5E.PowerDetails": "Power Details",
|
||||||
"SW5E.PowerDie": "Power Die",
|
|
||||||
"SW5E.PowerDiePl": "Power Dice",
|
|
||||||
"SW5E.PowerDieAlloc": "Power Die Allocation",
|
|
||||||
"SW5E.PowerDiceRecovery": "Power Dice Recovery",
|
"SW5E.PowerDiceRecovery": "Power Dice Recovery",
|
||||||
|
"SW5E.PowerDie": "Power Die",
|
||||||
|
"SW5E.PowerDieAlloc": "Power Die Allocation",
|
||||||
|
"SW5E.PowerDiePl": "Power Dice",
|
||||||
"SW5E.PowerEffects": "Power Effects",
|
"SW5E.PowerEffects": "Power Effects",
|
||||||
"SW5E.PowerfulCritical": "Powerful Critical",
|
"SW5E.PowerfulCritical": "Powerful Critical",
|
||||||
"SW5E.PowerLevel": "Power Level",
|
"SW5E.PowerLevel": "Power Level",
|
||||||
|
@ -916,23 +900,7 @@
|
||||||
"SW5E.Reaction": "Reaction",
|
"SW5E.Reaction": "Reaction",
|
||||||
"SW5E.ReactionPl": "Reactions",
|
"SW5E.ReactionPl": "Reactions",
|
||||||
"SW5E.Recharge": "Recharge",
|
"SW5E.Recharge": "Recharge",
|
||||||
"SW5E.RechargeRestHint": "Take a recharge rest? On a recharge rest you may spend remaining Hull Dice and recover Shields.",
|
|
||||||
"SW5E.RechargetRestNoHD": "No Hull Dice remaining",
|
|
||||||
"SW5E.RechargeRestNormal": "Recharge Rest (1 hour)",
|
|
||||||
"SW5E.RechargeRestOvernight": "Recharge Rest (New Day)",
|
|
||||||
"SW5E.RechargeRestResult": "{name} takes a recharge rest spending {dice} Hull Dice to recover {health} Hull Points.",
|
|
||||||
"SW5E.RechargeRestResultShort": "{name} takes a recharge rest.",
|
|
||||||
"SW5E.RechargeRestSelect": "Select Dice to Roll",
|
|
||||||
"SW5E.Refitting": "Refitting",
|
"SW5E.Refitting": "Refitting",
|
||||||
"SW5E.RefittingRest": "Refitting Rest",
|
|
||||||
"SW5E.RefittingRestEpic": "Refitting Rest (1 hour)",
|
|
||||||
"SW5E.RefittingRestGritty": "Refitting Rest (7 days)",
|
|
||||||
"SW5E.RefittingRestNormal": "Refitting Rest (8 hours)",
|
|
||||||
"SW5E.RefittingRestOvernight": "Refitting Rest (New Day)",
|
|
||||||
"SW5E.RefittingRestResult": "{name} takes a refitting rest.",
|
|
||||||
"SW5E.RefittingRestResultHD": "{name} takes a refitting rest and recovers {dice} Hull Dice.",
|
|
||||||
"SW5E.RefittingRestResultHP": "{name} takes a refitting rest and recovers {health} Hull Points.",
|
|
||||||
"SW5E.RefittingRestResultHPHD": "{name} takes a refitting rest and recovers {health} Hull Points and {dice} Hull Dice.",
|
|
||||||
"SW5E.Refuel": "Refuel",
|
"SW5E.Refuel": "Refuel",
|
||||||
"SW5E.RegenerationRateCoefficient": "Regeneration Rate Coefficient",
|
"SW5E.RegenerationRateCoefficient": "Regeneration Rate Coefficient",
|
||||||
"SW5E.RequiredMaterials": "Required Materials",
|
"SW5E.RequiredMaterials": "Required Materials",
|
||||||
|
@ -981,9 +949,6 @@
|
||||||
"SW5E.SheetClassNPCOld": "Old NPC Sheet",
|
"SW5E.SheetClassNPCOld": "Old NPC Sheet",
|
||||||
"SW5E.SheetClassVehicle": "Default Vehicle Sheet",
|
"SW5E.SheetClassVehicle": "Default Vehicle Sheet",
|
||||||
"SW5E.ShieldDice": "Shield Dice",
|
"SW5E.ShieldDice": "Shield Dice",
|
||||||
"SW5E.ShieldDiceRoll": "Roll Shield Dice",
|
|
||||||
"SW5E.ShieldDiceUsed": "Shield Dice Used",
|
|
||||||
"SW5E.ShieldDiceWarn": "{name} has no available {formula} Shield Dice remaining!",
|
|
||||||
"SW5E.ShieldPoints": "Shield Points",
|
"SW5E.ShieldPoints": "Shield Points",
|
||||||
"SW5E.ShieldPointsFormula": "Shield Points Formula",
|
"SW5E.ShieldPointsFormula": "Shield Points Formula",
|
||||||
"SW5E.ShieldRegen": "Regen",
|
"SW5E.ShieldRegen": "Regen",
|
||||||
|
@ -1074,7 +1039,7 @@
|
||||||
"SW5E.StarshipSkillDat": "Data",
|
"SW5E.StarshipSkillDat": "Data",
|
||||||
"SW5E.StarshipSkillHid": "Hide",
|
"SW5E.StarshipSkillHid": "Hide",
|
||||||
"SW5E.StarshipSkillImp": "Impress",
|
"SW5E.StarshipSkillImp": "Impress",
|
||||||
"SW5E.StarshipSkillInf": "Interfere",
|
"SW5E.StarshipSkillInt": "Interfere",
|
||||||
"SW5E.StarshipSkillMan": "Maneuvering",
|
"SW5E.StarshipSkillMan": "Maneuvering",
|
||||||
"SW5E.StarshipSkillMen": "Menace",
|
"SW5E.StarshipSkillMen": "Menace",
|
||||||
"SW5E.StarshipSkillPat": "Patch",
|
"SW5E.StarshipSkillPat": "Patch",
|
||||||
|
|
|
@ -671,77 +671,6 @@
|
||||||
.editor {
|
.editor {
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
.fuel {
|
|
||||||
flex: 0 0 12px;
|
|
||||||
background: #7a7971;
|
|
||||||
margin: 1px 15px 0 1px;
|
|
||||||
border: 1px solid #191813;
|
|
||||||
border-radius: 3px;
|
|
||||||
position: relative;
|
|
||||||
.fuel-bar {
|
|
||||||
position: absolute;
|
|
||||||
top: 1px;
|
|
||||||
left: 1px;
|
|
||||||
background: #6c8aa5;
|
|
||||||
height: 8px;
|
|
||||||
border: 1px solid #cde4ff;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
.fuel-label {
|
|
||||||
height: 10px;
|
|
||||||
padding: 0 5px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 12px;
|
|
||||||
text-align: right;
|
|
||||||
color: #EEE;
|
|
||||||
text-shadow: 0 0 5px #000;
|
|
||||||
}
|
|
||||||
.fuel-breakpoint {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
.fuel-breakpoint.fuel-20 {
|
|
||||||
left: 20%;
|
|
||||||
}
|
|
||||||
.fuel-breakpoint.fuel-40 {
|
|
||||||
left: 40%;
|
|
||||||
}
|
|
||||||
.fuel-breakpoint.fuel-60 {
|
|
||||||
left: 60%;
|
|
||||||
}
|
|
||||||
.fuel-breakpoint.fuel-80 {
|
|
||||||
left: 80%;
|
|
||||||
}
|
|
||||||
.arrow-up {
|
|
||||||
bottom: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-left: 4px solid transparent;
|
|
||||||
border-right: 4px solid transparent;
|
|
||||||
border-bottom: 4px solid #000;
|
|
||||||
}
|
|
||||||
.arrow-down {
|
|
||||||
top: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-left: 4px solid transparent;
|
|
||||||
border-right: 4px solid transparent;
|
|
||||||
border-top: 4px solid #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.fuel.fueled {
|
|
||||||
.arrow-up {
|
|
||||||
border-bottom: 4px solid #fff;
|
|
||||||
border-bottom: 4px solid #000;
|
|
||||||
}
|
|
||||||
.arrow-down {
|
|
||||||
border-top: 4px solid #fff;
|
|
||||||
border-top: 4px solid #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#actor-flags {
|
#actor-flags {
|
||||||
|
|
|
@ -251,44 +251,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel.resources {
|
|
||||||
.traits {
|
|
||||||
.fuel-wrapper {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 300px 100px;
|
|
||||||
width: 400px;
|
|
||||||
justify-self: end;
|
|
||||||
.fuel-label {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 14px;
|
|
||||||
width: 100%;
|
|
||||||
text-shadow: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
height: auto;
|
|
||||||
text-align: center;
|
|
||||||
margin-left: -2px;
|
|
||||||
border-radius: 0 4px 4px 0;
|
|
||||||
}
|
|
||||||
.fuel {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 4px;
|
|
||||||
height: 16px;
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
.fuel-bar {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.sheet-navigation {
|
nav.sheet-navigation {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(7, 1fr);
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
@ -1161,44 +1123,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input[type=range][orient=vertical] {
|
|
||||||
-webkit-appearance: slider-vertical;
|
|
||||||
width: 10px;
|
|
||||||
height: 60px !important;
|
|
||||||
padding: 0 0 !important;
|
|
||||||
background-color: #c40f0f !important;
|
|
||||||
box-sizing: border-box;
|
|
||||||
&::-webkit-slider-runnable-track {
|
|
||||||
-webkit-appearance: slider-vertical !important;
|
|
||||||
height: 60px !important;
|
|
||||||
width: 10px !important;
|
|
||||||
line-height: 60px !important;
|
|
||||||
padding-top: 0 !important;
|
|
||||||
padding-bottom: 0 !important;
|
|
||||||
margin-top: 0 0 !important;
|
|
||||||
border-radius: 3px !important;
|
|
||||||
background: linear-gradient( to top, #c40f0f 50%, #0dce0d 50% );
|
|
||||||
}
|
|
||||||
&::-webkit-slider-thumb {
|
|
||||||
-webkit-appearance: none !important;
|
|
||||||
background-color: #c40f0f !important;
|
|
||||||
margin-right: -4px !important;
|
|
||||||
margin-top: 0px !important;
|
|
||||||
cursor: grab !important;
|
|
||||||
border-radius: 0 0 0 0 !important;
|
|
||||||
width: 10px !important;
|
|
||||||
height: 5px !important;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output {
|
|
||||||
display: block;
|
|
||||||
margin: 5px auto;
|
|
||||||
font-size: 1.75em;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
.vertslider {
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,28 +35,6 @@ body.dark-theme {
|
||||||
box-shadow: @blockquoteShadow;
|
box-shadow: @blockquoteShadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sw5e.sheet.actor {
|
|
||||||
.swalt-sheet {
|
|
||||||
.panel.resources {
|
|
||||||
.traits {
|
|
||||||
.fuel-wrapper {
|
|
||||||
.fuel-label {
|
|
||||||
background: #D6D6D6;
|
|
||||||
color: #1C1C1C;
|
|
||||||
border: 1px solid #1C1C1C;
|
|
||||||
}
|
|
||||||
.fuel {
|
|
||||||
background: #c40f0f;
|
|
||||||
.fuel-bar {
|
|
||||||
background: #0dce0d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border-width: 0 0 1px 0;
|
border-width: 0 0 1px 0;
|
||||||
border-bottom: 1px solid @hrColor;
|
border-bottom: 1px solid @hrColor;
|
||||||
|
|
|
@ -35,28 +35,6 @@ body.light-theme {
|
||||||
box-shadow: @blockquoteShadow;
|
box-shadow: @blockquoteShadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sw5e.sheet.actor {
|
|
||||||
.swalt-sheet {
|
|
||||||
.panel.resources {
|
|
||||||
.traits {
|
|
||||||
.fuel-wrapper {
|
|
||||||
.fuel-label {
|
|
||||||
background: #D6D6D6;
|
|
||||||
color: #1C1C1C;
|
|
||||||
border: 1px solid #1C1C1C;
|
|
||||||
}
|
|
||||||
.fuel {
|
|
||||||
background: #c40f0f;
|
|
||||||
.fuel-bar {
|
|
||||||
background: #0dce0d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border-width: 0 0 1px 0;
|
border-width: 0 0 1px 0;
|
||||||
border-bottom: 1px solid @hrColor;
|
border-bottom: 1px solid @hrColor;
|
||||||
|
|
|
@ -120,9 +120,6 @@ export default class Actor5e extends Actor {
|
||||||
// Inventory encumbrance
|
// Inventory encumbrance
|
||||||
data.attributes.encumbrance = this._computeEncumbrance(actorData);
|
data.attributes.encumbrance = this._computeEncumbrance(actorData);
|
||||||
|
|
||||||
// Prepare Starship Data
|
|
||||||
if (actorData.type === "starship") this._computeStarshipData(actorData, data);
|
|
||||||
|
|
||||||
// Prepare skills
|
// Prepare skills
|
||||||
this._prepareSkills(actorData, bonuses, checkBonus, originalSkills);
|
this._prepareSkills(actorData, bonuses, checkBonus, originalSkills);
|
||||||
|
|
||||||
|
@ -228,11 +225,7 @@ export default class Actor5e extends Actor {
|
||||||
*/
|
*/
|
||||||
async getClassFeatures({className, archetypeName, level} = {}) {
|
async getClassFeatures({className, archetypeName, level} = {}) {
|
||||||
const existing = new Set(this.items.map((i) => i.name));
|
const existing = new Set(this.items.map((i) => i.name));
|
||||||
const features = await Actor5e.loadClassFeatures({
|
const features = await Actor5e.loadClassFeatures({className, archetypeName, level});
|
||||||
className,
|
|
||||||
archetypeName,
|
|
||||||
level
|
|
||||||
});
|
|
||||||
return features.filter((f) => !existing.has(f.name)) || [];
|
return features.filter((f) => !existing.has(f.name)) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,87 +358,15 @@ export default class Actor5e extends Actor {
|
||||||
*/
|
*/
|
||||||
_prepareStarshipData(actorData) {
|
_prepareStarshipData(actorData) {
|
||||||
const data = actorData.data;
|
const data = actorData.data;
|
||||||
data.attributes.prof = 0;
|
|
||||||
// Determine Starship size-based properties based on owned Starship item
|
|
||||||
const size = actorData.items.filter((i) => i.type === "starship");
|
|
||||||
if (size.length !== 0) {
|
|
||||||
const sizeData = size[0].data.data;
|
|
||||||
const tiers = parseInt(sizeData.tier) || 0;
|
|
||||||
data.traits.size = sizeData.size; // needs to be the short code
|
|
||||||
data.details.tier = tiers;
|
|
||||||
data.attributes.ac.value = 10 + Math.max(tiers - 1, 0);
|
|
||||||
data.attributes.hull.die = sizeData.hullDice;
|
|
||||||
data.attributes.hull.dicemax = sizeData.hullDiceStart + tiers;
|
|
||||||
data.attributes.hull.dice = sizeData.hullDiceStart + tiers - (parseInt(sizeData.hullDiceUsed) || 0);
|
|
||||||
data.attributes.shld.die = sizeData.shldDice;
|
|
||||||
data.attributes.shld.dicemax = sizeData.shldDiceStart + tiers;
|
|
||||||
data.attributes.shld.dice = sizeData.shldDiceStart + tiers - (parseInt(sizeData.shldDiceUsed) || 0);
|
|
||||||
sizeData.pwrDice = SW5E.powerDieTypes[tiers];
|
|
||||||
data.attributes.power.die = sizeData.pwrDice;
|
|
||||||
data.attributes.cost.baseBuild = sizeData.buildBaseCost;
|
|
||||||
data.attributes.workforce.minBuild = sizeData.buildMinWorkforce;
|
|
||||||
data.attributes.workforce.max = data.attributes.workforce.minBuild * 5;
|
|
||||||
data.attributes.cost.baseUpgrade = SW5E.baseUpgradeCost[tiers];
|
|
||||||
data.attributes.cost.multUpgrade = sizeData.upgrdCostMult;
|
|
||||||
data.attributes.workforce.minUpgrade = sizeData.upgrdMinWorkforce;
|
|
||||||
data.attributes.equip.size.crewMinWorkforce = parseInt(sizeData.crewMinWorkforce) || 1;
|
|
||||||
data.attributes.mods.capLimit = sizeData.modBaseCap;
|
|
||||||
data.attributes.mods.suites.cap = sizeData.modMaxSuiteCap;
|
|
||||||
data.attributes.cost.multModification = sizeData.modCostMult;
|
|
||||||
data.attributes.workforce.minModification = sizeData.modMinWorkforce;
|
|
||||||
data.attributes.cost.multEquip = sizeData.equipCostMult;
|
|
||||||
data.attributes.workforce.minEquip = sizeData.equipMinWorkforce;
|
|
||||||
data.attributes.equip.size.cargoCap = sizeData.cargoCap;
|
|
||||||
data.attributes.fuel.cost = sizeData.fuelCost;
|
|
||||||
data.attributes.fuel.cap = sizeData.fuelCap;
|
|
||||||
data.attributes.equip.size.foodCap = sizeData.foodCap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine Starship armor-based properties based on owned Starship item
|
// Proficiency
|
||||||
const armor = actorData.items.filter((i) => i.type === "equipment" && i.data.armor.type === "ssarmor"); // && (i.data.equipped === true)));
|
data.attributes.prof = Math.floor((Math.max(data.details.tier, 1) + 7) / 4);
|
||||||
if (armor.length !== 0) {
|
|
||||||
const armorData = armor[0].data;
|
|
||||||
data.attributes.equip.armor.dr = parseInt(armorData.dmgred.value) || 0;
|
|
||||||
data.attributes.equip.armor.maxDex = armorData.armor.dex;
|
|
||||||
data.attributes.equip.armor.stealthDisadv = armorData.stealth;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine Starship hyperdrive-based properties based on owned Starship item
|
// Link hull to hp and shields to temp hp
|
||||||
const hyperdrive = actorData.items.filter((i) => i.type === "equipment" && i.data.armor.type === "hyper"); // && (i.data.equipped === true)));
|
data.attributes.hull.value = data.attributes.hp.value;
|
||||||
if (hyperdrive.length !== 0) {
|
data.attributes.hull.max = data.attributes.hp.max;
|
||||||
const hdData = hyperdrive[0].data;
|
data.attributes.shld.value = data.attributes.hp.temp;
|
||||||
data.attributes.equip.hyperdrive.class = parseFloat(hdData.hdclass.value) || null;
|
data.attributes.shld.max = data.attributes.hp.tempmax;
|
||||||
}
|
|
||||||
|
|
||||||
// Determine Starship power coupling-based properties based on owned Starship item
|
|
||||||
const pwrcpl = actorData.items.filter((i) => i.type === "equipment" && i.data.armor.type === "powerc"); // && (i.data.equipped === true)));
|
|
||||||
if (pwrcpl.length !== 0) {
|
|
||||||
const pwrcplData = pwrcpl[0].data;
|
|
||||||
data.attributes.equip.powerCoupling.centralCap = parseInt(pwrcplData.cscap.value) || 0;
|
|
||||||
data.attributes.equip.powerCoupling.systemCap = parseInt(pwrcplData.sscap.value) || 0;
|
|
||||||
data.attributes.power.central.max = 0;
|
|
||||||
data.attributes.power.comms.max = 0;
|
|
||||||
data.attributes.power.engines.max = 0;
|
|
||||||
data.attributes.power.shields.max = 0;
|
|
||||||
data.attributes.power.sensors.max = 0;
|
|
||||||
data.attributes.power.weapons.max = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine Starship reactor-based properties based on owned Starship item
|
|
||||||
const reactor = actorData.items.filter((i) => i.type === "equipment" && i.data.armor.type === "reactor"); // && (i.data.equipped === true)));
|
|
||||||
if (reactor.length !== 0) {
|
|
||||||
const reactorData = reactor[0].data;
|
|
||||||
data.attributes.equip.reactor.fuelMult = parseFloat(reactorData.fuelcostsmod.value) || 0;
|
|
||||||
data.attributes.equip.reactor.powerRecDie = reactorData.powdicerec.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine Starship shield-based properties based on owned Starship item
|
|
||||||
const shields = actorData.items.filter((i) => i.type === "equipment" && i.data.armor.type === "ssshield"); // && (i.data.equipped === true)));
|
|
||||||
if (shields.length !== 0) {
|
|
||||||
const shieldsData = shields[0].data;
|
|
||||||
data.attributes.equip.shields.capMult = parseFloat(shieldsData.capx.value) || 1;
|
|
||||||
data.attributes.equip.shields.regenRateMult = parseFloat(shieldsData.regrateco.value) || 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -813,65 +734,10 @@ export default class Actor5e extends Actor {
|
||||||
return {value: weight.toNearest(0.1), max, pct, encumbered: pct > 2 / 3};
|
return {value: weight.toNearest(0.1), max, pct, encumbered: pct > 2 / 3};
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStarshipData(actorData, data) {
|
|
||||||
// Calculate AC
|
|
||||||
data.attributes.ac.value += Math.min(data.abilities.dex.mod, data.attributes.equip.armor.maxDex);
|
|
||||||
|
|
||||||
// Set Power Die Storage
|
|
||||||
data.attributes.power.central.max += data.attributes.equip.powerCoupling.centralCap;
|
|
||||||
data.attributes.power.comms.max += data.attributes.equip.powerCoupling.systemCap;
|
|
||||||
data.attributes.power.engines.max += data.attributes.equip.powerCoupling.systemCap;
|
|
||||||
data.attributes.power.shields.max += data.attributes.equip.powerCoupling.systemCap;
|
|
||||||
data.attributes.power.sensors.max += data.attributes.equip.powerCoupling.systemCap;
|
|
||||||
data.attributes.power.weapons.max += data.attributes.equip.powerCoupling.systemCap;
|
|
||||||
|
|
||||||
// Find Size info of Starship
|
|
||||||
const size = actorData.items.filter((i) => i.type === "starship");
|
|
||||||
if (size.length === 0) return;
|
|
||||||
const sizeData = size[0].data.data;
|
|
||||||
|
|
||||||
// Prepare Hull Points
|
|
||||||
data.attributes.hp.max =
|
|
||||||
sizeData.hullDiceRolled.reduce((a, b) => a + b, 0) + data.abilities.con.mod * data.attributes.hull.dicemax;
|
|
||||||
if (data.attributes.hp.value === null) data.attributes.hp.value = data.attributes.hp.max;
|
|
||||||
|
|
||||||
// Prepare Shield Points
|
|
||||||
data.attributes.hp.tempmax =
|
|
||||||
(sizeData.shldDiceRolled.reduce((a, b) => a + b, 0) +
|
|
||||||
data.abilities.str.mod * data.attributes.shld.dicemax) *
|
|
||||||
data.attributes.equip.shields.capMult;
|
|
||||||
if (data.attributes.hp.temp === null) data.attributes.hp.temp = data.attributes.hp.tempmax;
|
|
||||||
|
|
||||||
// Prepare Speeds
|
|
||||||
data.attributes.movement.space =
|
|
||||||
sizeData.baseSpaceSpeed + 50 * (data.abilities.str.mod - data.abilities.con.mod);
|
|
||||||
data.attributes.movement.turn = Math.min(
|
|
||||||
data.attributes.movement.space,
|
|
||||||
Math.max(50, sizeData.baseTurnSpeed - 50 * (data.abilities.dex.mod - data.abilities.con.mod))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Prepare Max Suites
|
|
||||||
data.attributes.mods.suites.max =
|
|
||||||
sizeData.modMaxSuitesBase + sizeData.modMaxSuitesMult * data.abilities.con.mod;
|
|
||||||
|
|
||||||
// Prepare Hardpoints
|
|
||||||
data.attributes.mods.hardpoints.max = sizeData.hardpointMult * Math.max(1, data.abilities.str.mod);
|
|
||||||
|
|
||||||
//Prepare Fuel
|
|
||||||
data.attributes.fuel = this._computeFuel(actorData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Event Handlers */
|
/* Event Handlers */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
_computeFuel(actorData) {
|
|
||||||
const fuel = actorData.data.attributes.fuel;
|
|
||||||
// Compute Fuel percentage
|
|
||||||
const pct = Math.clamped((fuel.value.toNearest(0.1) * 100) / fuel.cap, 0, 100);
|
|
||||||
return {...fuel, pct, fueled: pct > 0};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
await super._preCreate(data, options, user);
|
await super._preCreate(data, options, user);
|
||||||
|
@ -1044,9 +910,7 @@ export default class Actor5e extends Actor {
|
||||||
const label = CONFIG.SW5E.abilities[abilityId];
|
const label = CONFIG.SW5E.abilities[abilityId];
|
||||||
new Dialog({
|
new Dialog({
|
||||||
title: game.i18n.format("SW5E.AbilityPromptTitle", {ability: label}),
|
title: game.i18n.format("SW5E.AbilityPromptTitle", {ability: label}),
|
||||||
content: `<p>${game.i18n.format("SW5E.AbilityPromptText", {
|
content: `<p>${game.i18n.format("SW5E.AbilityPromptText", {ability: label})}</p>`,
|
||||||
ability: label
|
|
||||||
})}</p>`,
|
|
||||||
buttons: {
|
buttons: {
|
||||||
test: {
|
test: {
|
||||||
label: game.i18n.localize("SW5E.ActionAbil"),
|
label: game.i18n.localize("SW5E.ActionAbil"),
|
||||||
|
@ -1233,18 +1097,13 @@ export default class Actor5e extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment successes
|
// Increment successes
|
||||||
else
|
else await this.update({"data.attributes.death.success": Math.clamped(successes, 0, 3)});
|
||||||
await this.update({
|
|
||||||
"data.attributes.death.success": Math.clamped(successes, 0, 3)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save failure
|
// Save failure
|
||||||
else {
|
else {
|
||||||
let failures = (death.failure || 0) + (d20 === 1 ? 2 : 1);
|
let failures = (death.failure || 0) + (d20 === 1 ? 2 : 1);
|
||||||
await this.update({
|
await this.update({"data.attributes.death.failure": Math.clamped(failures, 0, 3)});
|
||||||
"data.attributes.death.failure": Math.clamped(failures, 0, 3)
|
|
||||||
});
|
|
||||||
if (failures >= 3) {
|
if (failures >= 3) {
|
||||||
// 3 Failures = death
|
// 3 Failures = death
|
||||||
chatString = "SW5E.DeathSaveFailure";
|
chatString = "SW5E.DeathSaveFailure";
|
||||||
|
@ -1253,10 +1112,7 @@ export default class Actor5e extends Actor {
|
||||||
|
|
||||||
// Display success/failure chat message
|
// Display success/failure chat message
|
||||||
if (chatString) {
|
if (chatString) {
|
||||||
let chatData = {
|
let chatData = {content: game.i18n.format(chatString, {name: this.name}), speaker};
|
||||||
content: game.i18n.format(chatString, {name: this.name}),
|
|
||||||
speaker
|
|
||||||
};
|
|
||||||
ChatMessage.applyRollMode(chatData, roll.options.rollMode);
|
ChatMessage.applyRollMode(chatData, roll.options.rollMode);
|
||||||
await ChatMessage.create(chatData);
|
await ChatMessage.create(chatData);
|
||||||
}
|
}
|
||||||
|
@ -1293,12 +1149,7 @@ export default class Actor5e extends Actor {
|
||||||
|
|
||||||
// If no class is available, display an error notification
|
// If no class is available, display an error notification
|
||||||
if (!cls) {
|
if (!cls) {
|
||||||
ui.notifications.error(
|
ui.notifications.error(game.i18n.format("SW5E.HitDiceWarn", {name: this.name, formula: denomination}));
|
||||||
game.i18n.format("SW5E.HitDiceWarn", {
|
|
||||||
name: this.name,
|
|
||||||
formula: denomination
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1333,218 +1184,6 @@ export default class Actor5e extends Actor {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**
|
|
||||||
* Roll a hull die of the appropriate type, gaining hull points equal to the die roll plus your CON modifier
|
|
||||||
* @param {string} [denomination] The hit denomination of hull die to roll. Example "d8".
|
|
||||||
* If no denomination is provided, the first available HD will be used
|
|
||||||
* @param {string} [numDice] How many damage dice to roll?
|
|
||||||
* @param {string} [keep] Which dice to keep? Example "kh1".
|
|
||||||
* @param {boolean} [dialog] Show a dialog prompt for configuring the hull die roll?
|
|
||||||
* @return {Promise<Roll|null>} The created Roll instance, or null if no hull die was rolled
|
|
||||||
*/
|
|
||||||
async rollHullDie(denomination, numDice = "1", keep = "", {dialog = true} = {}) {
|
|
||||||
// If no denomination was provided, choose the first available
|
|
||||||
let sship = null;
|
|
||||||
if (!denomination) {
|
|
||||||
sship = this.itemTypes.class.find(
|
|
||||||
(s) => s.data.data.hullDiceUsed < s.data.data.tier + s.data.data.hullDiceStart
|
|
||||||
);
|
|
||||||
if (!sship) return null;
|
|
||||||
denomination = sship.data.data.hullDice;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise locate a starship (if any) which has an available hit die of the requested denomination
|
|
||||||
else {
|
|
||||||
sship = this.items.find((i) => {
|
|
||||||
const d = i.data.data;
|
|
||||||
return d.hullDice === denomination && (d.hitDiceUsed || 0) < (d.tier || 0) + d.hullDiceStart;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no class is available, display an error notification
|
|
||||||
if (!sship) {
|
|
||||||
ui.notifications.error(
|
|
||||||
game.i18n.format("SW5E.HullDiceWarn", {
|
|
||||||
name: this.name,
|
|
||||||
formula: denomination
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare roll data
|
|
||||||
const parts = [`${numDice}${denomination}${keep}`, "@abilities.con.mod"];
|
|
||||||
const title = game.i18n.localize("SW5E.HullDiceRoll");
|
|
||||||
const rollData = duplicate(this.data.data);
|
|
||||||
|
|
||||||
// Call the roll helper utility
|
|
||||||
const roll = await damageRoll({
|
|
||||||
event: new Event("hitDie"),
|
|
||||||
parts: parts,
|
|
||||||
data: rollData,
|
|
||||||
title: title,
|
|
||||||
speaker: ChatMessage.getSpeaker({actor: this}),
|
|
||||||
allowcritical: false,
|
|
||||||
fastForward: !dialog,
|
|
||||||
dialogOptions: {width: 350},
|
|
||||||
messageData: {"flags.sw5e.roll": {type: "hullDie"}}
|
|
||||||
});
|
|
||||||
if (!roll) return null;
|
|
||||||
|
|
||||||
// Adjust actor data
|
|
||||||
await sship.update({
|
|
||||||
"data.hullDiceUsed": sship.data.data.hullDiceUsed + 1
|
|
||||||
});
|
|
||||||
const hp = this.data.data.attributes.hp;
|
|
||||||
const dhp = Math.min(hp.max - hp.value, roll.total);
|
|
||||||
await this.update({"data.attributes.hp.value": hp.value + dhp});
|
|
||||||
return roll;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Roll a hull die of the appropriate type, gaining hull points equal to the die roll plus your CON modifier
|
|
||||||
* @return {Promise<Roll|null>} The created Roll instance, or null if no hull die was rolled
|
|
||||||
*/
|
|
||||||
async rollHullDieCheck() {
|
|
||||||
// If no denomination was provided, choose the first available
|
|
||||||
let sship = null;
|
|
||||||
if (!denomination) {
|
|
||||||
sship = this.itemTypes.class.find(
|
|
||||||
(s) => s.data.data.hullDiceUsed < s.data.data.tier + s.data.data.hullDiceStart
|
|
||||||
);
|
|
||||||
if (!sship) return null;
|
|
||||||
denomination = sship.data.data.hullDice;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise locate a starship (if any) which has an available hit die of the requested denomination
|
|
||||||
else {
|
|
||||||
sship = this.items.find((i) => {
|
|
||||||
const d = i.data.data;
|
|
||||||
return d.hullDice === denomination && (d.hitDiceUsed || 0) < (d.tier || 0) + d.hullDiceStart;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no class is available, display an error notification
|
|
||||||
if (!sship) {
|
|
||||||
ui.notifications.error(
|
|
||||||
game.i18n.format("SW5E.HullDiceWarn", {
|
|
||||||
name: this.name,
|
|
||||||
formula: denomination
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare roll data
|
|
||||||
const parts = [`${numDice}${denomination}${keep}`, "@abilities.con.mod"];
|
|
||||||
const title = game.i18n.localize("SW5E.HullDiceRoll");
|
|
||||||
const rollData = duplicate(this.data.data);
|
|
||||||
|
|
||||||
// Call the roll helper utility
|
|
||||||
const roll = await damageRoll({
|
|
||||||
event: new Event("hitDie"),
|
|
||||||
parts: parts,
|
|
||||||
data: rollData,
|
|
||||||
title: title,
|
|
||||||
speaker: ChatMessage.getSpeaker({actor: this}),
|
|
||||||
allowcritical: false,
|
|
||||||
fastForward: !dialog,
|
|
||||||
dialogOptions: {width: 350},
|
|
||||||
messageData: {"flags.sw5e.roll": {type: "hullDie"}}
|
|
||||||
});
|
|
||||||
if (!roll) return null;
|
|
||||||
|
|
||||||
// Adjust actor data
|
|
||||||
await sship.update({
|
|
||||||
"data.hullDiceUsed": sship.data.data.hullDiceUsed + 1
|
|
||||||
});
|
|
||||||
const hp = this.data.data.attributes.hp;
|
|
||||||
const dhp = Math.min(hp.max - hp.value, roll.total);
|
|
||||||
await this.update({"data.attributes.hp.value": hp.value + dhp});
|
|
||||||
return roll;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Roll a shield die of the appropriate type, gaining shield points equal to the die roll
|
|
||||||
* multiplied by the shield regeneration coefficient
|
|
||||||
* @param {string} [denomination] The denomination of shield die to roll. Example "d8".
|
|
||||||
* If no denomination is provided, the first available SD will be used
|
|
||||||
* @param {boolean} [natural] Natural ship shield regeneration (true) or user action (false)?
|
|
||||||
* @param {string} [numDice] How many damage dice to roll?
|
|
||||||
* @param {string} [keep] Which dice to keep? Example "kh1".
|
|
||||||
* @param {boolean} [dialog] Show a dialog prompt for configuring the shield die roll?
|
|
||||||
* @return {Promise<Roll|null>} The created Roll instance, or null if no shield die was rolled
|
|
||||||
*/
|
|
||||||
async rollShieldDie(denomination, natural = false, numDice = "1", keep = "", {dialog = true} = {}) {
|
|
||||||
// If no denomination was provided, choose the first available
|
|
||||||
let sship = null;
|
|
||||||
if (!denomination) {
|
|
||||||
sship = this.itemTypes.class.find(
|
|
||||||
(s) => s.data.data.shldDiceUsed < s.data.data.tier + s.data.data.shldDiceStart
|
|
||||||
);
|
|
||||||
if (!sship) return null;
|
|
||||||
denomination = sship.data.data.shldDice;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise locate a starship (if any) which has an available hit die of the requested denomination
|
|
||||||
else {
|
|
||||||
sship = this.items.find((i) => {
|
|
||||||
const d = i.data.data;
|
|
||||||
return d.shldDice === denomination && (d.shldDiceUsed || 0) < (d.tier || 0) + d.shldDiceStart;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no starship is available, display an error notification
|
|
||||||
if (!sship) {
|
|
||||||
ui.notifications.error(
|
|
||||||
game.i18n.format("SW5E.ShldDiceWarn", {
|
|
||||||
name: this.name,
|
|
||||||
formula: denomination
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if natural regeneration roll max
|
|
||||||
if (natural) {
|
|
||||||
numdice = denomination.substring(1);
|
|
||||||
denomination = "";
|
|
||||||
keep = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare roll data
|
|
||||||
const parts = [`${numDice}${denomination}${keep} * @attributes.regenRate`];
|
|
||||||
const title = game.i18n.localize("SW5E.ShieldDiceRoll");
|
|
||||||
const rollData = duplicate(this.data.data);
|
|
||||||
|
|
||||||
// Call the roll helper utility
|
|
||||||
roll = await damageRoll({
|
|
||||||
event: new Event("shldDie"),
|
|
||||||
parts: parts,
|
|
||||||
data: rollData,
|
|
||||||
title: title,
|
|
||||||
speaker: ChatMessage.getSpeaker({actor: this}),
|
|
||||||
allowcritical: false,
|
|
||||||
fastForward: !dialog,
|
|
||||||
dialogOptions: {width: 350},
|
|
||||||
messageData: {"flags.sw5e.roll": {type: "shldDie"}}
|
|
||||||
});
|
|
||||||
if (!roll) return null;
|
|
||||||
|
|
||||||
// Adjust actor data
|
|
||||||
await sship.update({
|
|
||||||
"data.shldDiceUsed": sship.data.data.shldDiceUsed + 1
|
|
||||||
});
|
|
||||||
const hp = this.data.data.attributes.hp;
|
|
||||||
const dhp = Math.min(hp.tempmax - hp.temp, roll.total);
|
|
||||||
await this.update({"data.attributes.hp.temp": hp.temp + dhp});
|
|
||||||
return roll;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Results from a rest operation.
|
* Results from a rest operation.
|
||||||
*
|
*
|
||||||
|
@ -1578,10 +1217,7 @@ export default class Actor5e extends Actor {
|
||||||
// Display a Dialog for rolling hit dice
|
// Display a Dialog for rolling hit dice
|
||||||
if (dialog) {
|
if (dialog) {
|
||||||
try {
|
try {
|
||||||
newDay = await ShortRestDialog.shortRestDialog({
|
newDay = await ShortRestDialog.shortRestDialog({actor: this, canRoll: hd0 > 0});
|
||||||
actor: this,
|
|
||||||
canRoll: hd0 > 0
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1678,10 +1314,7 @@ export default class Actor5e extends Actor {
|
||||||
},
|
},
|
||||||
updateItems: [
|
updateItems: [
|
||||||
...hitDiceUpdates,
|
...hitDiceUpdates,
|
||||||
...this._getRestItemUsesRecovery({
|
...this._getRestItemUsesRecovery({recoverLongRestUses: longRest, recoverDailyUses: newDay})
|
||||||
recoverLongRestUses: longRest,
|
|
||||||
recoverDailyUses: newDay
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
newDay: newDay
|
newDay: newDay
|
||||||
};
|
};
|
||||||
|
@ -1904,10 +1537,7 @@ export default class Actor5e extends Actor {
|
||||||
if (hitDiceRecovered < maxHitDice && d.hitDiceUsed > 0) {
|
if (hitDiceRecovered < maxHitDice && d.hitDiceUsed > 0) {
|
||||||
let delta = Math.min(d.hitDiceUsed || 0, maxHitDice - hitDiceRecovered);
|
let delta = Math.min(d.hitDiceUsed || 0, maxHitDice - hitDiceRecovered);
|
||||||
hitDiceRecovered += delta;
|
hitDiceRecovered += delta;
|
||||||
updates.push({
|
updates.push({"_id": item.id, "data.hitDiceUsed": d.hitDiceUsed - delta});
|
||||||
"_id": item.id,
|
|
||||||
"data.hitDiceUsed": d.hitDiceUsed - delta
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1948,96 +1578,6 @@ export default class Actor5e extends Actor {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**
|
|
||||||
* Deploy an Actor into this one.
|
|
||||||
*
|
|
||||||
* @param {Actor} target The Actor to be deployed.
|
|
||||||
* @param {boolean} [coord] Deploy as Coordinator
|
|
||||||
* @param {boolean} [gunner] Deploy as Gunner
|
|
||||||
* @param {boolean} [mech] Deploy as Mechanic
|
|
||||||
* @param {boolean} [oper] Deploy as Operator
|
|
||||||
* @param {boolean} [pilot] Deploy as Pilot
|
|
||||||
* @param {boolean} [tech] Deploy as Technician
|
|
||||||
* @param {boolean} [crew] Deploy as Crew
|
|
||||||
* @param {boolean} [pass] Deploy as Passenger
|
|
||||||
*/
|
|
||||||
async deployInto(
|
|
||||||
target,
|
|
||||||
{
|
|
||||||
coord = false,
|
|
||||||
gunner = false,
|
|
||||||
mech = false,
|
|
||||||
oper = false,
|
|
||||||
pilot = false,
|
|
||||||
tech = false,
|
|
||||||
crew = false,
|
|
||||||
pass = false
|
|
||||||
} = {}
|
|
||||||
) {
|
|
||||||
// Get the starship Actor data and the new char data
|
|
||||||
const sship = duplicate(this.toJSON());
|
|
||||||
const ssDeploy = sship.data.attributes.deployment;
|
|
||||||
const char = target;
|
|
||||||
const charUUID = char.uuid;
|
|
||||||
const charName = char.data.name;
|
|
||||||
const charRank = char.data.data.attributes.rank;
|
|
||||||
let charProf = 0;
|
|
||||||
if (charRank === undefined || charRank.total > 0) {
|
|
||||||
charProf = char.data.data.attributes.prof;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (coord) {
|
|
||||||
ssDeploy.coord.uuid = charUUID;
|
|
||||||
ssDeploy.coord.name = charName;
|
|
||||||
ssDeploy.coord.rank = charRank ? charRank.coord : 0;
|
|
||||||
ssDeploy.coord.prof = charProf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gunner) {
|
|
||||||
ssDeploy.gunner.uuid = charUUID;
|
|
||||||
ssDeploy.gunner.name = charName;
|
|
||||||
ssDeploy.gunner.rank = charRank ? charRank.gunner : 0;
|
|
||||||
ssDeploy.gunner.prof = charProf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mech) {
|
|
||||||
ssDeploy.mechanic.uuid = charUUID;
|
|
||||||
ssDeploy.mechanic.name = charName;
|
|
||||||
ssDeploy.mechanic.rank = charRank ? charRank.mechanic : 0;
|
|
||||||
ssDeploy.mechanic.prof = charProf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oper) {
|
|
||||||
ssDeploy.operator.uuid = charUUID;
|
|
||||||
ssDeploy.operator.name = charName;
|
|
||||||
ssDeploy.operator.rank = charRank ? charRank.operator : 0;
|
|
||||||
ssDeploy.operator.prof = charProf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pilot) {
|
|
||||||
ssDeploy.pilot.uuid = charUUID;
|
|
||||||
ssDeploy.pilot.name = charName;
|
|
||||||
ssDeploy.pilot.rank = charRank ? charRank.pilot : 0;
|
|
||||||
ssDeploy.pilot.prof = charProf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tech) {
|
|
||||||
ssDeploy.technician.uuid = charUUID;
|
|
||||||
ssDeploy.technician.name = charName;
|
|
||||||
ssDeploy.technician.rank = charRank ? charRank.technician : 0;
|
|
||||||
ssDeploy.technician.prof = charProf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crew) {
|
|
||||||
ssDeploy.crew.push({uuid: charUUID, name: charName, rank: charRank, prof: charProf});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pass) {
|
|
||||||
ssDeploy.passenger.push({uuid: charUUID, name: charName, rank: charRank, prof: charProf});
|
|
||||||
}
|
|
||||||
this.update({"data.attributes.deployment": ssDeploy});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform this Actor into another one.
|
* Transform this Actor into another one.
|
||||||
*
|
*
|
||||||
|
|
2126
module/actor/old_entity.js
Normal file
2126
module/actor/old_entity.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -5,7 +5,7 @@ import ActorHitDiceConfig from "../../../apps/hit-dice-config.js";
|
||||||
import ActorMovementConfig from "../../../apps/movement-config.js";
|
import ActorMovementConfig from "../../../apps/movement-config.js";
|
||||||
import ActorSensesConfig from "../../../apps/senses-config.js";
|
import ActorSensesConfig from "../../../apps/senses-config.js";
|
||||||
import ActorTypeConfig from "../../../apps/actor-type.js";
|
import ActorTypeConfig from "../../../apps/actor-type.js";
|
||||||
import {SW5E} from '../../../config.js';
|
import {SW5E} from "../../../config.js";
|
||||||
import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js";
|
import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,7 +58,8 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get template() {
|
get template() {
|
||||||
if ( !game.user.isGM && this.actor.limited ) return "systems/sw5e/templates/actors/newActor/expanded-limited-sheet.html";
|
if (!game.user.isGM && this.actor.limited)
|
||||||
|
return "systems/sw5e/templates/actors/newActor/expanded-limited-sheet.html";
|
||||||
return `systems/sw5e/templates/actors/newActor/${this.actor.data.type}-sheet.html`;
|
return `systems/sw5e/templates/actors/newActor/${this.actor.data.type}-sheet.html`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +67,6 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
getData(options) {
|
getData(options) {
|
||||||
|
|
||||||
// Basic data
|
// Basic data
|
||||||
let isOwner = this.actor.isOwner;
|
let isOwner = this.actor.isOwner;
|
||||||
const data = {
|
const data = {
|
||||||
|
@ -78,7 +78,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
isCharacter: this.actor.type === "character",
|
isCharacter: this.actor.type === "character",
|
||||||
isNPC: this.actor.type === "npc",
|
isNPC: this.actor.type === "npc",
|
||||||
isStarship: this.actor.type === "starship",
|
isStarship: this.actor.type === "starship",
|
||||||
isVehicle: this.actor.type === 'vehicle',
|
isVehicle: this.actor.type === "vehicle",
|
||||||
config: CONFIG.SW5E,
|
config: CONFIG.SW5E,
|
||||||
rollData: this.actor.getRollData.bind(this.actor)
|
rollData: this.actor.getRollData.bind(this.actor)
|
||||||
};
|
};
|
||||||
|
@ -137,7 +137,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
data.effects = prepareActiveEffectCategories(this.actor.effects);
|
data.effects = prepareActiveEffectCategories(this.actor.effects);
|
||||||
|
|
||||||
// Return data to the sheet
|
// Return data to the sheet
|
||||||
return data
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -156,31 +156,35 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
let speeds = [
|
let speeds = [
|
||||||
[movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`],
|
[movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`],
|
||||||
[movement.climb, `${game.i18n.localize("SW5E.MovementClimb")} ${movement.climb}`],
|
[movement.climb, `${game.i18n.localize("SW5E.MovementClimb")} ${movement.climb}`],
|
||||||
[movement.fly, `${game.i18n.localize("SW5E.MovementFly")} ${movement.fly}` + (movement.hover ? ` (${game.i18n.localize("SW5E.MovementHover")})` : "")],
|
[
|
||||||
|
movement.fly,
|
||||||
|
`${game.i18n.localize("SW5E.MovementFly")} ${movement.fly}` +
|
||||||
|
(movement.hover ? ` (${game.i18n.localize("SW5E.MovementHover")})` : "")
|
||||||
|
],
|
||||||
[movement.swim, `${game.i18n.localize("SW5E.MovementSwim")} ${movement.swim}`]
|
[movement.swim, `${game.i18n.localize("SW5E.MovementSwim")} ${movement.swim}`]
|
||||||
]
|
];
|
||||||
if (largestPrimary) {
|
if (largestPrimary) {
|
||||||
speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]);
|
speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter and sort speeds on their values
|
// Filter and sort speeds on their values
|
||||||
speeds = speeds.filter(s => !!s[0]).sort((a, b) => b[0] - a[0]);
|
speeds = speeds.filter((s) => !!s[0]).sort((a, b) => b[0] - a[0]);
|
||||||
|
|
||||||
// Case 1: Largest as primary
|
// Case 1: Largest as primary
|
||||||
if (largestPrimary) {
|
if (largestPrimary) {
|
||||||
let primary = speeds.shift();
|
let primary = speeds.shift();
|
||||||
return {
|
return {
|
||||||
primary: `${primary ? primary[1] : "0"} ${movement.units}`,
|
primary: `${primary ? primary[1] : "0"} ${movement.units}`,
|
||||||
special: speeds.map(s => s[1]).join(", ")
|
special: speeds.map((s) => s[1]).join(", ")
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: Walk as primary
|
// Case 2: Walk as primary
|
||||||
else {
|
else {
|
||||||
return {
|
return {
|
||||||
primary: `${movement.walk || 0} ${movement.units}`,
|
primary: `${movement.walk || 0} ${movement.units}`,
|
||||||
special: speeds.length ? speeds.map(s => s[1]).join(", ") : ""
|
special: speeds.length ? speeds.map((s) => s[1]).join(", ") : ""
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +194,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const senses = actorData.data.attributes.senses || {};
|
const senses = actorData.data.attributes.senses || {};
|
||||||
const tags = {};
|
const tags = {};
|
||||||
for (let [k, label] of Object.entries(CONFIG.SW5E.senses)) {
|
for (let [k, label] of Object.entries(CONFIG.SW5E.senses)) {
|
||||||
const v = senses[k] ?? 0
|
const v = senses[k] ?? 0;
|
||||||
if (v === 0) continue;
|
if (v === 0) continue;
|
||||||
tags[k] = `${game.i18n.localize(label)} ${v} ${senses.units}`;
|
tags[k] = `${game.i18n.localize(label)} ${v} ${senses.units}`;
|
||||||
}
|
}
|
||||||
|
@ -207,14 +211,14 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
*/
|
*/
|
||||||
_prepareTraits(traits) {
|
_prepareTraits(traits) {
|
||||||
const map = {
|
const map = {
|
||||||
"dr": CONFIG.SW5E.damageResistanceTypes,
|
dr: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"di": CONFIG.SW5E.damageResistanceTypes,
|
di: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"dv": CONFIG.SW5E.damageResistanceTypes,
|
dv: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"ci": CONFIG.SW5E.conditionTypes,
|
ci: CONFIG.SW5E.conditionTypes,
|
||||||
"languages": CONFIG.SW5E.languages,
|
languages: CONFIG.SW5E.languages,
|
||||||
"armorProf": CONFIG.SW5E.armorProficiencies,
|
armorProf: CONFIG.SW5E.armorProficiencies,
|
||||||
"weaponProf": CONFIG.SW5E.weaponProficiencies,
|
weaponProf: CONFIG.SW5E.weaponProficiencies,
|
||||||
"toolProf": CONFIG.SW5E.toolProficiencies
|
toolProf: CONFIG.SW5E.toolProficiencies
|
||||||
};
|
};
|
||||||
for (let [t, choices] of Object.entries(map)) {
|
for (let [t, choices] of Object.entries(map)) {
|
||||||
const trait = traits[t];
|
const trait = traits[t];
|
||||||
|
@ -230,7 +234,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Add custom entry
|
// Add custom entry
|
||||||
if (trait.custom) {
|
if (trait.custom) {
|
||||||
trait.custom.split(";").forEach((c, i) => trait.selected[`custom${i+1}`] = c.trim());
|
trait.custom.split(";").forEach((c, i) => (trait.selected[`custom${i + 1}`] = c.trim()));
|
||||||
}
|
}
|
||||||
trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive";
|
trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive";
|
||||||
}
|
}
|
||||||
|
@ -252,8 +256,8 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Define some mappings
|
// Define some mappings
|
||||||
const sections = {
|
const sections = {
|
||||||
"atwill": -20,
|
atwill: -20,
|
||||||
"innate": -10,
|
innate: -10
|
||||||
};
|
};
|
||||||
|
|
||||||
// Label power slot uses headers
|
// Label power slot uses headers
|
||||||
|
@ -270,12 +274,17 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
label: label,
|
label: label,
|
||||||
usesSlots: i > 0,
|
usesSlots: i > 0,
|
||||||
canCreate: owner,
|
canCreate: owner,
|
||||||
canPrepare: (data.actor.type === "character") && (i >= 1),
|
canPrepare: data.actor.type === "character" && i >= 1,
|
||||||
powers: [],
|
powers: [],
|
||||||
uses: useLabels[i] || value || 0,
|
uses: useLabels[i] || value || 0,
|
||||||
slots: useLabels[i] || max || 0,
|
slots: useLabels[i] || max || 0,
|
||||||
override: override || 0,
|
override: override || 0,
|
||||||
dataset: {"type": "power", "level": prepMode in sections ? 1 : i, "preparation.mode": prepMode, "school": school},
|
dataset: {
|
||||||
|
"type": "power",
|
||||||
|
"level": prepMode in sections ? 1 : i,
|
||||||
|
"preparation.mode": prepMode,
|
||||||
|
"school": school
|
||||||
|
},
|
||||||
prop: sl
|
prop: sl
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -284,7 +293,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const maxLevel = Array.fromRange(10).reduce((max, i) => {
|
const maxLevel = Array.fromRange(10).reduce((max, i) => {
|
||||||
if (i === 0) return max;
|
if (i === 0) return max;
|
||||||
const level = levels[`power${i}`];
|
const level = levels[`power${i}`];
|
||||||
if ( (level.max || level.override ) && ( i > max ) ) max = i;
|
if ((level.max || level.override) && i > max) max = i;
|
||||||
return max;
|
return max;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
@ -298,7 +307,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over every power item, adding powers to the powerbook by section
|
// Iterate over every power item, adding powers to the powerbook by section
|
||||||
powers.forEach(power => {
|
powers.forEach((power) => {
|
||||||
const mode = power.data.preparation.mode || "prepared";
|
const mode = power.data.preparation.mode || "prepared";
|
||||||
let s = power.data.level || 0;
|
let s = power.data.level || 0;
|
||||||
const sl = `power${s}`;
|
const sl = `power${s}`;
|
||||||
|
@ -341,13 +350,13 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_filterItems(items, filters) {
|
_filterItems(items, filters) {
|
||||||
return items.filter(item => {
|
return items.filter((item) => {
|
||||||
const data = item.data;
|
const data = item.data;
|
||||||
|
|
||||||
// Action usage
|
// Action usage
|
||||||
for (let f of ["action", "bonus", "reaction"]) {
|
for (let f of ["action", "bonus", "reaction"]) {
|
||||||
if (filters.has(f)) {
|
if (filters.has(f)) {
|
||||||
if ((data.activation && (data.activation.type !== f))) return false;
|
if (data.activation && data.activation.type !== f) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,64 +404,62 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
activateListeners(html) {
|
activateListeners(html) {
|
||||||
|
|
||||||
// Activate Item Filters
|
// Activate Item Filters
|
||||||
const filterLists = html.find(".filter-list");
|
const filterLists = html.find(".filter-list");
|
||||||
filterLists.each(this._initializeFilterItemList.bind(this));
|
filterLists.each(this._initializeFilterItemList.bind(this));
|
||||||
filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this));
|
filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this));
|
||||||
|
|
||||||
// Item summaries
|
// Item summaries
|
||||||
html.find('.item .item-name.rollable h4').click(event => this._onItemSummary(event));
|
html.find(".item .item-name.rollable h4").click((event) => this._onItemSummary(event));
|
||||||
|
|
||||||
// View Item Sheets
|
// View Item Sheets
|
||||||
html.find('.item-edit').click(this._onItemEdit.bind(this));
|
html.find(".item-edit").click(this._onItemEdit.bind(this));
|
||||||
|
|
||||||
// Editable Only Listeners
|
// Editable Only Listeners
|
||||||
if (this.isEditable) {
|
if (this.isEditable) {
|
||||||
|
|
||||||
// Input focus and update
|
// Input focus and update
|
||||||
const inputs = html.find("input");
|
const inputs = html.find("input");
|
||||||
inputs.focus(ev => ev.currentTarget.select());
|
inputs.focus((ev) => ev.currentTarget.select());
|
||||||
inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this));
|
inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this));
|
||||||
|
|
||||||
// Ability Proficiency
|
// Ability Proficiency
|
||||||
html.find('.ability-proficiency').click(this._onToggleAbilityProficiency.bind(this));
|
html.find(".ability-proficiency").click(this._onToggleAbilityProficiency.bind(this));
|
||||||
|
|
||||||
// Toggle Skill Proficiency
|
// Toggle Skill Proficiency
|
||||||
html.find('.skill-proficiency').on("click contextmenu", this._onCycleSkillProficiency.bind(this));
|
html.find(".skill-proficiency").on("click contextmenu", this._onCycleSkillProficiency.bind(this));
|
||||||
|
|
||||||
// Trait Selector
|
// Trait Selector
|
||||||
html.find('.trait-selector').click(this._onTraitSelector.bind(this));
|
html.find(".trait-selector").click(this._onTraitSelector.bind(this));
|
||||||
|
|
||||||
// Configure Special Flags
|
// Configure Special Flags
|
||||||
html.find('.config-button').click(this._onConfigMenu.bind(this));
|
html.find(".config-button").click(this._onConfigMenu.bind(this));
|
||||||
|
|
||||||
// Owned Item management
|
// Owned Item management
|
||||||
html.find('.item-create').click(this._onItemCreate.bind(this));
|
html.find(".item-create").click(this._onItemCreate.bind(this));
|
||||||
html.find('.item-delete').click(this._onItemDelete.bind(this));
|
html.find(".item-delete").click(this._onItemDelete.bind(this));
|
||||||
html.find('.item-collapse').click(this._onItemCollapse.bind(this));
|
html.find(".item-collapse").click(this._onItemCollapse.bind(this));
|
||||||
html.find('.item-uses input').click(ev => ev.target.select()).change(this._onUsesChange.bind(this));
|
html.find(".item-uses input")
|
||||||
html.find('.slot-max-override').click(this._onPowerSlotOverride.bind(this));
|
.click((ev) => ev.target.select())
|
||||||
html.find('.increment-class-level').click(this._onIncrementClassLevel.bind(this));
|
.change(this._onUsesChange.bind(this));
|
||||||
html.find('.decrement-class-level').click(this._onDecrementClassLevel.bind(this));
|
html.find(".slot-max-override").click(this._onPowerSlotOverride.bind(this));
|
||||||
|
html.find(".increment-class-level").click(this._onIncrementClassLevel.bind(this));
|
||||||
|
html.find(".decrement-class-level").click(this._onDecrementClassLevel.bind(this));
|
||||||
|
|
||||||
// Active Effect management
|
// Active Effect management
|
||||||
html.find(".effect-control").click(ev => onManageActiveEffect(ev, this.actor));
|
html.find(".effect-control").click((ev) => onManageActiveEffect(ev, this.actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Owner Only Listeners
|
// Owner Only Listeners
|
||||||
if (this.actor.isOwner) {
|
if (this.actor.isOwner) {
|
||||||
|
|
||||||
// Ability Checks
|
// Ability Checks
|
||||||
html.find('.ability-name').click(this._onRollAbilityTest.bind(this));
|
html.find(".ability-name").click(this._onRollAbilityTest.bind(this));
|
||||||
|
|
||||||
|
|
||||||
// Roll Skill Checks
|
// Roll Skill Checks
|
||||||
html.find('.skill-name').click(this._onRollSkillCheck.bind(this));
|
html.find(".skill-name").click(this._onRollSkillCheck.bind(this));
|
||||||
|
|
||||||
// Item Rolling
|
// Item Rolling
|
||||||
html.find('.item .item-image').click(event => this._onItemRoll(event));
|
html.find(".item .item-image").click((event) => this._onItemRoll(event));
|
||||||
html.find('.item .item-recharge').click(event => this._onItemRecharge(event));
|
html.find(".item .item-recharge").click((event) => this._onItemRecharge(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise remove rollable classes
|
// Otherwise remove rollable classes
|
||||||
|
@ -547,9 +554,9 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Toggle next level - forward on click, backwards on right
|
// Toggle next level - forward on click, backwards on right
|
||||||
if (event.type === "click") {
|
if (event.type === "click") {
|
||||||
field.val(levels[(idx === levels.length - 1) ? 0 : idx + 1]);
|
field.val(levels[idx === levels.length - 1 ? 0 : idx + 1]);
|
||||||
} else if (event.type === "contextmenu") {
|
} else if (event.type === "contextmenu") {
|
||||||
field.val(levels[(idx === 0) ? levels.length - 1 : idx - 1]);
|
field.val(levels[idx === 0 ? levels.length - 1 : idx - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the field value and save the form
|
// Update the field value and save the form
|
||||||
|
@ -560,13 +567,13 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropActor(event, data) {
|
async _onDropActor(event, data) {
|
||||||
const canPolymorph = game.user.isGM || (this.actor.data.type === "starship") || (this.actor.owner && game.settings.get('sw5e', 'allowPolymorphing'));
|
const canPolymorph = game.user.isGM || (this.actor.isOwner && game.settings.get("sw5e", "allowPolymorphing"));
|
||||||
if (!canPolymorph) return false;
|
if (!canPolymorph) return false;
|
||||||
|
|
||||||
// Get the target actor
|
// Get the target actor
|
||||||
let sourceActor = null;
|
let sourceActor = null;
|
||||||
if (data.pack) {
|
if (data.pack) {
|
||||||
const pack = game.packs.find(p => p.collection === data.pack);
|
const pack = game.packs.find((p) => p.collection === data.pack);
|
||||||
sourceActor = await pack.getEntity(data.id);
|
sourceActor = await pack.getEntity(data.id);
|
||||||
} else {
|
} else {
|
||||||
sourceActor = game.actors.get(data.id);
|
sourceActor = game.actors.get(data.id);
|
||||||
|
@ -574,61 +581,37 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
if (!sourceActor) return;
|
if (!sourceActor) return;
|
||||||
|
|
||||||
// Define a function to record polymorph settings for future use
|
// Define a function to record polymorph settings for future use
|
||||||
const rememberOptions = html => {
|
const rememberOptions = (html) => {
|
||||||
const options = {};
|
const options = {};
|
||||||
html.find('input').each((i, el) => {
|
html.find("input").each((i, el) => {
|
||||||
options[el.name] = el.checked;
|
options[el.name] = el.checked;
|
||||||
});
|
});
|
||||||
const settings = mergeObject(game.settings.get('sw5e', 'polymorphSettings') || {}, options);
|
const settings = mergeObject(game.settings.get("sw5e", "polymorphSettings") || {}, options);
|
||||||
game.settings.set('sw5e', 'polymorphSettings', settings);
|
game.settings.set("sw5e", "polymorphSettings", settings);
|
||||||
return settings;
|
return settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create and render the Dialog
|
// Create and render the Dialog
|
||||||
|
return new Dialog(
|
||||||
if (this.actor.data.type === "starship") {
|
{
|
||||||
return new Dialog({
|
title: game.i18n.localize("SW5E.PolymorphPromptTitle"),
|
||||||
title: game.i18n.localize('SW5E.DeploymentPromptTitle') + " " + sourceActor.data.name + " into " + this.actor.data.name,
|
|
||||||
content: {
|
content: {
|
||||||
i18n: SW5E.deploymentTypes,
|
options: game.settings.get("sw5e", "polymorphSettings"),
|
||||||
isToken: this.actor.isToken
|
|
||||||
},
|
|
||||||
default: 'accept',
|
|
||||||
buttons: {
|
|
||||||
deploy: {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: game.i18n.localize('SW5E.DeploymentAcceptSettings'),
|
|
||||||
callback: html => this.actor.deployInto(sourceActor, rememberOptions(html))
|
|
||||||
},
|
|
||||||
cancel: {
|
|
||||||
icon: '<i class="fas fa-times"></i>',
|
|
||||||
label: game.i18n.localize('Cancel')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
classes: ['dialog', 'sw5e'],
|
|
||||||
width: 600,
|
|
||||||
template: 'systems/sw5e/templates/apps/deployment-prompt.html'
|
|
||||||
}).render(true);
|
|
||||||
} else {
|
|
||||||
return new Dialog({
|
|
||||||
title: game.i18n.localize('SW5E.PolymorphPromptTitle'),
|
|
||||||
content: {
|
|
||||||
options: game.settings.get('sw5e', 'polymorphSettings'),
|
|
||||||
i18n: SW5E.polymorphSettings,
|
i18n: SW5E.polymorphSettings,
|
||||||
isToken: this.actor.isToken
|
isToken: this.actor.isToken
|
||||||
},
|
},
|
||||||
default: 'accept',
|
default: "accept",
|
||||||
buttons: {
|
buttons: {
|
||||||
accept: {
|
accept: {
|
||||||
icon: '<i class="fas fa-check"></i>',
|
icon: '<i class="fas fa-check"></i>',
|
||||||
label: game.i18n.localize('SW5E.PolymorphAcceptSettings'),
|
label: game.i18n.localize("SW5E.PolymorphAcceptSettings"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, rememberOptions(html))
|
callback: (html) => this.actor.transformInto(sourceActor, rememberOptions(html))
|
||||||
},
|
},
|
||||||
wildshape: {
|
wildshape: {
|
||||||
icon: '<i class="fas fa-paw"></i>',
|
icon: '<i class="fas fa-paw"></i>',
|
||||||
label: game.i18n.localize('SW5E.PolymorphWildShape'),
|
label: game.i18n.localize("SW5E.PolymorphWildShape"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, {
|
callback: (html) =>
|
||||||
|
this.actor.transformInto(sourceActor, {
|
||||||
keepBio: true,
|
keepBio: true,
|
||||||
keepClass: true,
|
keepClass: true,
|
||||||
keepMental: true,
|
keepMental: true,
|
||||||
|
@ -639,46 +622,49 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
},
|
},
|
||||||
polymorph: {
|
polymorph: {
|
||||||
icon: '<i class="fas fa-pastafarianism"></i>',
|
icon: '<i class="fas fa-pastafarianism"></i>',
|
||||||
label: game.i18n.localize('SW5E.Polymorph'),
|
label: game.i18n.localize("SW5E.Polymorph"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, {
|
callback: (html) =>
|
||||||
|
this.actor.transformInto(sourceActor, {
|
||||||
transformTokens: rememberOptions(html).transformTokens
|
transformTokens: rememberOptions(html).transformTokens
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
icon: '<i class="fas fa-times"></i>',
|
icon: '<i class="fas fa-times"></i>',
|
||||||
label: game.i18n.localize('Cancel')
|
label: game.i18n.localize("Cancel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
classes: ['dialog', 'sw5e'],
|
{
|
||||||
|
classes: ["dialog", "sw5e"],
|
||||||
width: 600,
|
width: 600,
|
||||||
template: 'systems/sw5e/templates/apps/polymorph-prompt.html'
|
template: "systems/sw5e/templates/apps/polymorph-prompt.html"
|
||||||
}).render(true);
|
|
||||||
}
|
}
|
||||||
|
).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
|
|
||||||
// Check to make sure items of this type are allowed on this actor
|
// Check to make sure items of this type are allowed on this actor
|
||||||
if (this.constructor.unsupportedItemTypes.has(itemData.type)) {
|
if (this.constructor.unsupportedItemTypes.has(itemData.type)) {
|
||||||
return ui.notifications.warn(game.i18n.format("SW5E.ActorWarningInvalidItem", {
|
return ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.ActorWarningInvalidItem", {
|
||||||
itemType: game.i18n.localize(CONFIG.Item.typeLabels[itemData.type]),
|
itemType: game.i18n.localize(CONFIG.Item.typeLabels[itemData.type]),
|
||||||
actorType: game.i18n.localize(CONFIG.Actor.typeLabels[this.actor.type])
|
actorType: game.i18n.localize(CONFIG.Actor.typeLabels[this.actor.type])
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a Consumable power scroll on the Inventory tab
|
// Create a Consumable power scroll on the Inventory tab
|
||||||
if ( (itemData.type === "power") && (this._tabs[0].active === "inventory") ) {
|
if (itemData.type === "power" && this._tabs[0].active === "inventory") {
|
||||||
const scroll = await Item5e.createScrollFromPower(itemData);
|
const scroll = await Item5e.createScrollFromPower(itemData);
|
||||||
itemData = scroll.data;
|
itemData = scroll.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemData.data) {
|
if (itemData.data) {
|
||||||
// Ignore certain statuses
|
// Ignore certain statuses
|
||||||
["equipped", "proficient", "prepared"].forEach(k => delete itemData.data[k]);
|
["equipped", "proficient", "prepared"].forEach((k) => delete itemData.data[k]);
|
||||||
|
|
||||||
// Downgrade ATTUNED to REQUIRED
|
// Downgrade ATTUNED to REQUIRED
|
||||||
itemData.data.attunement = Math.min(itemData.data.attunement, CONFIG.SW5E.attunementTypes.REQUIRED);
|
itemData.data.attunement = Math.min(itemData.data.attunement, CONFIG.SW5E.attunementTypes.REQUIRED);
|
||||||
|
@ -686,14 +672,14 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Stack identical consumables
|
// Stack identical consumables
|
||||||
if (itemData.type === "consumable" && itemData.flags.core?.sourceId) {
|
if (itemData.type === "consumable" && itemData.flags.core?.sourceId) {
|
||||||
const similarItem = this.actor.items.find(i => {
|
const similarItem = this.actor.items.find((i) => {
|
||||||
const sourceId = i.getFlag("core", "sourceId");
|
const sourceId = i.getFlag("core", "sourceId");
|
||||||
return sourceId && (sourceId === itemData.flags.core?.sourceId) &&
|
return sourceId && sourceId === itemData.flags.core?.sourceId && i.type === "consumable";
|
||||||
(i.type === "consumable");
|
|
||||||
});
|
});
|
||||||
if ( similarItem && itemData.name !== "Power Cell" ) { // Always create a new powercell instead of increasing quantity
|
if (similarItem && itemData.name !== "Power Cell") {
|
||||||
|
// Always create a new powercell instead of increasing quantity
|
||||||
return similarItem.update({
|
return similarItem.update({
|
||||||
'data.quantity': similarItem.data.data.quantity + Math.max(itemData.data.quantity, 1)
|
"data.quantity": similarItem.data.data.quantity + Math.max(itemData.data.quantity, 1)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -739,7 +725,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const item = this.actor.items.get(itemId);
|
const item = this.actor.items.get(itemId);
|
||||||
const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max);
|
const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max);
|
||||||
event.target.value = uses;
|
event.target.value = uses;
|
||||||
return item.update({ 'data.uses.value': uses });
|
return item.update({"data.uses.value": uses});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -767,7 +753,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemId);
|
const item = this.actor.items.get(itemId);
|
||||||
return item.rollRecharge();
|
return item.rollRecharge();
|
||||||
};
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
@ -788,7 +774,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
} else {
|
} else {
|
||||||
let div = $(`<div class="item-summary">${chatData.description.value}</div>`);
|
let div = $(`<div class="item-summary">${chatData.description.value}</div>`);
|
||||||
let props = $(`<div class="item-properties"></div>`);
|
let props = $(`<div class="item-properties"></div>`);
|
||||||
chatData.properties.forEach(p => props.append(`<span class="tag">${p}</span>`));
|
chatData.properties.forEach((p) => props.append(`<span class="tag">${p}</span>`));
|
||||||
div.append(props);
|
div.append(props);
|
||||||
li.append(div.hide());
|
li.append(div.hide());
|
||||||
div.slideDown(200);
|
div.slideDown(200);
|
||||||
|
@ -874,7 +860,7 @@ _onItemCollapse(event) {
|
||||||
_onIncrementClassLevel(event) {
|
_onIncrementClassLevel(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const div = event.currentTarget.closest(".character")
|
const div = event.currentTarget.closest(".character");
|
||||||
const li = event.currentTarget.closest("li");
|
const li = event.currentTarget.closest("li");
|
||||||
|
|
||||||
const actorId = div.id.split("-")[1];
|
const actorId = div.id.split("-")[1];
|
||||||
|
@ -884,7 +870,7 @@ _onItemCollapse(event) {
|
||||||
const item = actor.items.get(itemId);
|
const item = actor.items.get(itemId);
|
||||||
|
|
||||||
let levels = item.data.data.levels;
|
let levels = item.data.data.levels;
|
||||||
const update = {_id: item.data._id, data: {levels: (levels + 1) }};
|
const update = {_id: item.data._id, data: {levels: levels + 1}};
|
||||||
|
|
||||||
actor.updateEmbeddedDocuments("Item", [update]);
|
actor.updateEmbeddedDocuments("Item", [update]);
|
||||||
}
|
}
|
||||||
|
@ -898,7 +884,7 @@ _onItemCollapse(event) {
|
||||||
_onDecrementClassLevel(event) {
|
_onDecrementClassLevel(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const div = event.currentTarget.closest(".character")
|
const div = event.currentTarget.closest(".character");
|
||||||
const li = event.currentTarget.closest("li");
|
const li = event.currentTarget.closest("li");
|
||||||
|
|
||||||
const actorId = div.id.split("-")[1];
|
const actorId = div.id.split("-")[1];
|
||||||
|
@ -908,7 +894,7 @@ _onItemCollapse(event) {
|
||||||
const item = actor.items.get(itemId);
|
const item = actor.items.get(itemId);
|
||||||
|
|
||||||
let levels = item.data.data.levels;
|
let levels = item.data.data.levels;
|
||||||
const update = {_id: item.data._id, data: {levels: (levels - 1) }};
|
const update = {_id: item.data._id, data: {levels: levels - 1}};
|
||||||
|
|
||||||
actor.updateEmbeddedDocuments("Item", [update]);
|
actor.updateEmbeddedDocuments("Item", [update]);
|
||||||
}
|
}
|
||||||
|
@ -982,7 +968,7 @@ _onItemCollapse(event) {
|
||||||
const label = a.parentElement.querySelector("label");
|
const label = a.parentElement.querySelector("label");
|
||||||
const choices = CONFIG.SW5E[a.dataset.options];
|
const choices = CONFIG.SW5E[a.dataset.options];
|
||||||
const options = {name: a.dataset.target, title: label.innerText, choices};
|
const options = {name: a.dataset.target, title: label.innerText, choices};
|
||||||
return new TraitSelector(this.actor, options).render(true)
|
return new TraitSelector(this.actor, options).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -992,7 +978,7 @@ _onItemCollapse(event) {
|
||||||
let buttons = super._getHeaderButtons();
|
let buttons = super._getHeaderButtons();
|
||||||
if (this.actor.isPolymorphed) {
|
if (this.actor.isPolymorphed) {
|
||||||
buttons.unshift({
|
buttons.unshift({
|
||||||
label: 'SW5E.PolymorphRestoreTransformation',
|
label: "SW5E.PolymorphRestoreTransformation",
|
||||||
class: "restore-transformation",
|
class: "restore-transformation",
|
||||||
icon: "fas fa-backward",
|
icon: "fas fa-backward",
|
||||||
onclick: () => this.actor.revertOriginalForm()
|
onclick: () => this.actor.revertOriginalForm()
|
||||||
|
|
|
@ -7,7 +7,6 @@ import Actor5e from "../../entity.js";
|
||||||
* @type {ActorSheet5e}
|
* @type {ActorSheet5e}
|
||||||
*/
|
*/
|
||||||
export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
|
|
||||||
get template() {
|
get template() {
|
||||||
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
||||||
return "systems/sw5e/templates/actors/newActor/character-sheet.html";
|
return "systems/sw5e/templates/actors/newActor/character-sheet.html";
|
||||||
|
@ -17,17 +16,18 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
|
|
||||||
return mergeObject(super.defaultOptions, {
|
return mergeObject(super.defaultOptions, {
|
||||||
classes: ["swalt", "sw5e", "sheet", "actor", "character"],
|
classes: ["swalt", "sw5e", "sheet", "actor", "character"],
|
||||||
blockFavTab: true,
|
blockFavTab: true,
|
||||||
subTabs: null,
|
subTabs: null,
|
||||||
width: 800,
|
width: 800,
|
||||||
tabs: [{
|
tabs: [
|
||||||
|
{
|
||||||
navSelector: ".root-tabs",
|
navSelector: ".root-tabs",
|
||||||
contentSelector: ".sheet-body",
|
contentSelector: ".sheet-body",
|
||||||
initial: "attributes"
|
initial: "attributes"
|
||||||
}],
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,10 +56,12 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
|
|
||||||
// Experience Tracking
|
// Experience Tracking
|
||||||
sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking");
|
sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking");
|
||||||
sheetData["classLabels"] = this.actor.itemTypes.class.map(c => c.name).join(", ");
|
sheetData["classLabels"] = this.actor.itemTypes.class.map((c) => c.name).join(", ");
|
||||||
sheetData["multiclassLabels"] = this.actor.itemTypes.class.map(c => {
|
sheetData["multiclassLabels"] = this.actor.itemTypes.class
|
||||||
return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(' ')
|
.map((c) => {
|
||||||
}).join(', ');
|
return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(" ");
|
||||||
|
})
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
// Return data for rendering
|
// Return data for rendering
|
||||||
return sheetData;
|
return sheetData;
|
||||||
|
@ -72,7 +74,6 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
|
|
||||||
// Categorize items as inventory, powerbook, features, and classes
|
// Categorize items as inventory, powerbook, features, and classes
|
||||||
const inventory = {
|
const inventory = {
|
||||||
weapon: {label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"}},
|
weapon: {label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"}},
|
||||||
|
@ -84,11 +85,27 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Partition items by category
|
// Partition items by category
|
||||||
let [items, forcepowers, techpowers, feats, classes, deployments, deploymentfeatures, ventures, species, archetypes, classfeatures, backgrounds, fightingstyles, fightingmasteries, lightsaberforms] = data.items.reduce((arr, item) => {
|
let [
|
||||||
|
items,
|
||||||
|
forcepowers,
|
||||||
|
techpowers,
|
||||||
|
feats,
|
||||||
|
classes,
|
||||||
|
deployments,
|
||||||
|
deploymentfeatures,
|
||||||
|
ventures,
|
||||||
|
species,
|
||||||
|
archetypes,
|
||||||
|
classfeatures,
|
||||||
|
backgrounds,
|
||||||
|
fightingstyles,
|
||||||
|
fightingmasteries,
|
||||||
|
lightsaberforms
|
||||||
|
] = data.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
// Item details
|
// Item details
|
||||||
item.img = item.img || CONST.DEFAULT_TOKEN;
|
item.img = item.img || CONST.DEFAULT_TOKEN;
|
||||||
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1;
|
||||||
item.attunement = {
|
item.attunement = {
|
||||||
[CONFIG.SW5E.attunementTypes.REQUIRED]: {
|
[CONFIG.SW5E.attunementTypes.REQUIRED]: {
|
||||||
icon: "fa-sun",
|
icon: "fa-sun",
|
||||||
|
@ -103,16 +120,18 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
}[item.data.attunement];
|
}[item.data.attunement];
|
||||||
|
|
||||||
// Item usage
|
// Item usage
|
||||||
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
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.isOnCooldown =
|
||||||
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
item.data.recharge && !!item.data.recharge.value && item.data.recharge.charged === false;
|
||||||
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
item.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0;
|
||||||
|
item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type);
|
||||||
|
|
||||||
// Item toggle state
|
// Item toggle state
|
||||||
this._prepareItemToggleState(item);
|
this._prepareItemToggleState(item);
|
||||||
|
|
||||||
// Primary Class
|
// Primary Class
|
||||||
if ( item.type === "class" ) item.isOriginalClass = ( item._id === this.actor.data.data.details.originalClass );
|
if (item.type === "class")
|
||||||
|
item.isOriginalClass = item._id === this.actor.data.data.details.originalClass;
|
||||||
|
|
||||||
// Classify items into types
|
// Classify items into types
|
||||||
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[1].push(item);
|
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[1].push(item);
|
||||||
|
@ -131,7 +150,9 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
else if (item.type === "lightsaberform") arr[14].push(item);
|
else if (item.type === "lightsaberform") arr[14].push(item);
|
||||||
else if (Object.keys(inventory).includes(item.type)) arr[0].push(item);
|
else if (Object.keys(inventory).includes(item.type)) arr[0].push(item);
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], [], [], [], [], [], [], [], [], [], [], [], [], [], []]);
|
},
|
||||||
|
[[], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
|
||||||
|
);
|
||||||
|
|
||||||
// Apply active item filters
|
// Apply active item filters
|
||||||
items = this._filterItems(items, this._filters.inventory);
|
items = this._filterItems(items, this._filters.inventory);
|
||||||
|
@ -153,18 +174,89 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
|
|
||||||
// Organize Features
|
// Organize Features
|
||||||
const features = {
|
const features = {
|
||||||
classes: { label: "SW5E.ItemTypeClassPl", items: [], hasActions: false, dataset: {type: "class"}, isClass: true },
|
classes: {
|
||||||
classfeatures: { label: "SW5E.ItemTypeClassFeats", items: [], hasActions: true, dataset: {type: "classfeature"}, isClassfeature: true },
|
label: "SW5E.ItemTypeClassPl",
|
||||||
archetype: { label: "SW5E.ItemTypeArchetype", items: [], hasActions: false, dataset: {type: "archetype"}, isArchetype: true },
|
items: [],
|
||||||
deployments: { label: "SW5E.ItemTypeDeploymentPl", items: [], hasActions: false, dataset: {type: "deployment"}, isDeployment: true },
|
hasActions: false,
|
||||||
deploymentfeatures: { label: "SW5E.ItemTypeDeploymentFeaturePl", items: [], hasActions: true, dataset: {type: "deploymentfeature"}, isDeploymentfeature: true },
|
dataset: {type: "class"},
|
||||||
ventures: { label: "SW5E.ItemTypeVenturePl", items: [], hasActions: false, dataset: {type: "venture"}, isVenture: true },
|
isClass: true
|
||||||
species: { label: "SW5E.ItemTypeSpecies", items: [], hasActions: false, dataset: {type: "species"}, isSpecies: true },
|
},
|
||||||
background: { label: "SW5E.ItemTypeBackground", items: [], hasActions: false, dataset: {type: "background"}, isBackground: true },
|
classfeatures: {
|
||||||
fightingstyles: { label: "SW5E.ItemTypeFightingStylePl", items: [], hasActions: false, dataset: {type: "fightingstyle"}, isFightingstyle: true },
|
label: "SW5E.ItemTypeClassFeats",
|
||||||
fightingmasteries: { label: "SW5E.ItemTypeFightingMasteryPl", items: [], hasActions: false, dataset: {type: "fightingmastery"}, isFightingmastery: true },
|
items: [],
|
||||||
lightsaberforms: { label: "SW5E.ItemTypeLightsaberFormPl", items: [], hasActions: false, dataset: {type: "lightsaberform"}, isLightsaberform: true },
|
hasActions: true,
|
||||||
active: { label: "SW5E.FeatureActive", items: [], hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
|
dataset: {type: "classfeature"},
|
||||||
|
isClassfeature: true
|
||||||
|
},
|
||||||
|
archetype: {
|
||||||
|
label: "SW5E.ItemTypeArchetype",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "archetype"},
|
||||||
|
isArchetype: true
|
||||||
|
},
|
||||||
|
deployments: {
|
||||||
|
label: "SW5E.ItemTypeDeploymentPl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "deployment"},
|
||||||
|
isDeployment: true
|
||||||
|
},
|
||||||
|
deploymentfeatures: {
|
||||||
|
label: "SW5E.ItemTypeDeploymentFeaturePl",
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {type: "deploymentfeature"},
|
||||||
|
isDeploymentfeature: true
|
||||||
|
},
|
||||||
|
ventures: {
|
||||||
|
label: "SW5E.ItemTypeVenturePl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "venture"},
|
||||||
|
isVenture: true
|
||||||
|
},
|
||||||
|
species: {
|
||||||
|
label: "SW5E.ItemTypeSpecies",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "species"},
|
||||||
|
isSpecies: true
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
label: "SW5E.ItemTypeBackground",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "background"},
|
||||||
|
isBackground: true
|
||||||
|
},
|
||||||
|
fightingstyles: {
|
||||||
|
label: "SW5E.ItemTypeFightingStylePl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "fightingstyle"},
|
||||||
|
isFightingstyle: true
|
||||||
|
},
|
||||||
|
fightingmasteries: {
|
||||||
|
label: "SW5E.ItemTypeFightingMasteryPl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "fightingmastery"},
|
||||||
|
isFightingmastery: true
|
||||||
|
},
|
||||||
|
lightsaberforms: {
|
||||||
|
label: "SW5E.ItemTypeLightsaberFormPl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "lightsaberform"},
|
||||||
|
isLightsaberform: true
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
label: "SW5E.FeatureActive",
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {"type": "feat", "activation.type": "action"}
|
||||||
|
},
|
||||||
passive: {label: "SW5E.FeaturePassive", items: [], hasActions: false, dataset: {type: "feat"}}
|
passive: {label: "SW5E.FeaturePassive", items: [], hasActions: false, dataset: {type: "feat"}}
|
||||||
};
|
};
|
||||||
for (let f of feats) {
|
for (let f of feats) {
|
||||||
|
@ -207,8 +299,7 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
if (isAlways) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always;
|
if (isAlways) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always;
|
||||||
else if (isPrepared) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared;
|
else if (isPrepared) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared;
|
||||||
else item.toggleTitle = game.i18n.localize("SW5E.PowerUnprepared");
|
else item.toggleTitle = game.i18n.localize("SW5E.PowerUnprepared");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const isActive = getProperty(item.data, "equipped");
|
const isActive = getProperty(item.data, "equipped");
|
||||||
item.toggleClass = isActive ? "active" : "";
|
item.toggleClass = isActive ? "active" : "";
|
||||||
item.toggleTitle = game.i18n.localize(isActive ? "SW5E.Equipped" : "SW5E.Unequipped");
|
item.toggleTitle = game.i18n.localize(isActive ? "SW5E.Equipped" : "SW5E.Unequipped");
|
||||||
|
@ -231,19 +322,21 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
// html.find(".currency-convert").click(this._onConvertCurrency.bind(this));
|
// html.find(".currency-convert").click(this._onConvertCurrency.bind(this));
|
||||||
|
|
||||||
// Item State Toggling
|
// Item State Toggling
|
||||||
html.find('.item-toggle').click(this._onToggleItem.bind(this));
|
html.find(".item-toggle").click(this._onToggleItem.bind(this));
|
||||||
|
|
||||||
// Short and Long Rest
|
// Short and Long Rest
|
||||||
html.find('.short-rest').click(this._onShortRest.bind(this));
|
html.find(".short-rest").click(this._onShortRest.bind(this));
|
||||||
html.find('.long-rest').click(this._onLongRest.bind(this));
|
html.find(".long-rest").click(this._onLongRest.bind(this));
|
||||||
|
|
||||||
// Rollable sheet actions
|
// Rollable sheet actions
|
||||||
html.find(".rollable[data-action]").click(this._onSheetAction.bind(this));
|
html.find(".rollable[data-action]").click(this._onSheetAction.bind(this));
|
||||||
|
|
||||||
// Send Languages to Chat onClick
|
// Send Languages to Chat onClick
|
||||||
html.find('[data-options="share-languages"]').click(event => {
|
html.find('[data-options="share-languages"]').click((event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let langs = this.actor.data.data.traits.languages.value.map(l => CONFIG.SW5E.languages[l] || l).join(", ");
|
let langs = this.actor.data.data.traits.languages.value
|
||||||
|
.map((l) => CONFIG.SW5E.languages[l] || l)
|
||||||
|
.join(", ");
|
||||||
let custom = this.actor.data.data.traits.languages.custom;
|
let custom = this.actor.data.data.traits.languages.custom;
|
||||||
if (custom) langs += ", " + custom.replace(/;/g, ",");
|
if (custom) langs += ", " + custom.replace(/;/g, ",");
|
||||||
let content = `
|
let content = `
|
||||||
|
@ -279,9 +372,9 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Item Delete Confirmation
|
// Item Delete Confirmation
|
||||||
html.find('.item-delete').off("click");
|
html.find(".item-delete").off("click");
|
||||||
html.find('.item-delete').click(event => {
|
html.find(".item-delete").click((event) => {
|
||||||
let li = $(event.currentTarget).parents('.item');
|
let li = $(event.currentTarget).parents(".item");
|
||||||
let itemId = li.attr("data-item-id");
|
let itemId = li.attr("data-item-id");
|
||||||
let item = this.actor.items.get(itemId);
|
let item = this.actor.items.get(itemId);
|
||||||
new Dialog({
|
new Dialog({
|
||||||
|
@ -290,17 +383,17 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
buttons: {
|
buttons: {
|
||||||
Yes: {
|
Yes: {
|
||||||
icon: '<i class="fa fa-check"></i>',
|
icon: '<i class="fa fa-check"></i>',
|
||||||
label: 'Yes',
|
label: "Yes",
|
||||||
callback: dlg => {
|
callback: (dlg) => {
|
||||||
this.actor.deleteOwnedItem(itemId);
|
this.actor.deleteOwnedItem(itemId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
icon: '<i class="fas fa-times"></i>',
|
icon: '<i class="fas fa-times"></i>',
|
||||||
label: 'No'
|
label: "No"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
default: "cancel"
|
||||||
default: 'cancel'
|
|
||||||
}).render(true);
|
}).render(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -325,7 +418,6 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle toggling the state of an Owned Item within the Actor
|
* Handle toggling the state of an Owned Item within the Actor
|
||||||
* @param {Event} event The triggering click event
|
* @param {Event} event The triggering click event
|
||||||
|
@ -369,10 +461,9 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
|
|
||||||
// Increment the number of class levels of a character instead of creating a new item
|
// Increment the number of class levels of a character instead of creating a new item
|
||||||
if (itemData.type === "class") {
|
if (itemData.type === "class") {
|
||||||
const cls = this.actor.itemTypes.class.find(c => c.name === itemData.name);
|
const cls = this.actor.itemTypes.class.find((c) => c.name === itemData.name);
|
||||||
let priorLevel = cls?.data.data.levels ?? 0;
|
let priorLevel = cls?.data.data.levels ?? 0;
|
||||||
if (!!cls) {
|
if (!!cls) {
|
||||||
const next = Math.min(priorLevel + 1, 20 + priorLevel - this.actor.data.data.details.level);
|
const next = Math.min(priorLevel + 1, 20 + priorLevel - this.actor.data.data.details.level);
|
||||||
|
@ -457,9 +548,9 @@ async function addFavorites(app, html, data) {
|
||||||
value: data.actor.data.powers.power9.value,
|
value: data.actor.data.powers.power9.value,
|
||||||
max: data.actor.data.powers.power9.max
|
max: data.actor.data.powers.power9.max
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
let powerCount = 0
|
let powerCount = 0;
|
||||||
let items = data.actor.items;
|
let items = data.actor.items;
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
if (item.type == "class") continue;
|
if (item.type == "class") continue;
|
||||||
|
@ -470,24 +561,28 @@ async function addFavorites(app, html, data) {
|
||||||
}
|
}
|
||||||
let isFav = item.flags.favtab.isFavourite;
|
let isFav = item.flags.favtab.isFavourite;
|
||||||
if (app.options.editable) {
|
if (app.options.editable) {
|
||||||
let favBtn = $(`<a class="item-control item-toggle item-fav ${isFav ? "active" : ""}" data-fav="${isFav}" title="${isFav ? "Remove from Favourites" : "Add to Favourites"}"><i class="fas fa-star"></i></a>`);
|
let favBtn = $(
|
||||||
favBtn.click(ev => {
|
`<a class="item-control item-toggle item-fav ${isFav ? "active" : ""}" data-fav="${isFav}" title="${
|
||||||
|
isFav ? "Remove from Favourites" : "Add to Favourites"
|
||||||
|
}"><i class="fas fa-star"></i></a>`
|
||||||
|
);
|
||||||
|
favBtn.click((ev) => {
|
||||||
app.actor.items.get(item.data._id).update({
|
app.actor.items.get(item.data._id).update({
|
||||||
"flags.favtab.isFavourite": !item.flags.favtab.isFavourite
|
"flags.favtab.isFavourite": !item.flags.favtab.isFavourite
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
html.find(`.item[data-item-id="${item.data._id}"]`).find('.item-controls').prepend(favBtn);
|
html.find(`.item[data-item-id="${item.data._id}"]`).find(".item-controls").prepend(favBtn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFav) {
|
if (isFav) {
|
||||||
item.powerComps = "";
|
item.powerComps = "";
|
||||||
if (item.data.components) {
|
if (item.data.components) {
|
||||||
let comps = item.data.components;
|
let comps = item.data.components;
|
||||||
let v = (comps.vocal) ? "V" : "";
|
let v = comps.vocal ? "V" : "";
|
||||||
let s = (comps.somatic) ? "S" : "";
|
let s = comps.somatic ? "S" : "";
|
||||||
let m = (comps.material) ? "M" : "";
|
let m = comps.material ? "M" : "";
|
||||||
let c = !!(comps.concentration);
|
let c = !!comps.concentration;
|
||||||
let r = !!(comps.ritual);
|
let r = !!comps.ritual;
|
||||||
item.powerComps = `${v}${s}${m}`;
|
item.powerComps = `${v}${s}${m}`;
|
||||||
item.powerCon = c;
|
item.powerCon = c;
|
||||||
item.powerRit = r;
|
item.powerRit = r;
|
||||||
|
@ -495,15 +590,15 @@ async function addFavorites(app, html, data) {
|
||||||
|
|
||||||
item.editable = app.options.editable;
|
item.editable = app.options.editable;
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case 'feat':
|
case "feat":
|
||||||
if (item.flags.favtab.sort === undefined) {
|
if (item.flags.favtab.sort === undefined) {
|
||||||
item.flags.favtab.sort = (favFeats.count + 1) * 100000; // initial sort key if not present
|
item.flags.favtab.sort = (favFeats.count + 1) * 100000; // initial sort key if not present
|
||||||
}
|
}
|
||||||
favFeats.push(item);
|
favFeats.push(item);
|
||||||
break;
|
break;
|
||||||
case 'power':
|
case "power":
|
||||||
if (item.data.preparation.mode) {
|
if (item.data.preparation.mode) {
|
||||||
item.powerPrepMode = ` (${CONFIG.SW5E.powerPreparationModes[item.data.preparation.mode]})`
|
item.powerPrepMode = ` (${CONFIG.SW5E.powerPreparationModes[item.data.preparation.mode]})`;
|
||||||
}
|
}
|
||||||
if (item.data.level) {
|
if (item.data.level) {
|
||||||
favPowers[item.data.level].powers.push(item);
|
favPowers[item.data.level].powers.push(item);
|
||||||
|
@ -529,60 +624,60 @@ async function addFavorites(app, html, data) {
|
||||||
// html.find('.favourite .item-controls').css('flex', '0 0 22px');
|
// html.find('.favourite .item-controls').css('flex', '0 0 22px');
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let tabContainer = html.find('.favtabtarget');
|
let tabContainer = html.find(".favtabtarget");
|
||||||
data.favItems = favItems.length > 0 ? favItems.sort((a, b) => (a.flags.favtab.sort) - (b.flags.favtab.sort)) : false;
|
data.favItems = favItems.length > 0 ? favItems.sort((a, b) => a.flags.favtab.sort - b.flags.favtab.sort) : false;
|
||||||
data.favFeats = favFeats.length > 0 ? favFeats.sort((a, b) => (a.flags.favtab.sort) - (b.flags.favtab.sort)) : false;
|
data.favFeats = favFeats.length > 0 ? favFeats.sort((a, b) => a.flags.favtab.sort - b.flags.favtab.sort) : false;
|
||||||
data.favPowers = powerCount > 0 ? favPowers : false;
|
data.favPowers = powerCount > 0 ? favPowers : false;
|
||||||
data.editable = app.options.editable;
|
data.editable = app.options.editable;
|
||||||
|
|
||||||
await loadTemplates(['systems/sw5e/templates/actors/newActor/item.hbs']);
|
await loadTemplates(["systems/sw5e/templates/actors/newActor/item.hbs"]);
|
||||||
let favtabHtml = $(await renderTemplate('systems/sw5e/templates/actors/newActor/template.hbs', data));
|
let favtabHtml = $(await renderTemplate("systems/sw5e/templates/actors/newActor/template.hbs", data));
|
||||||
favtabHtml.find('.item-name h4').click(event => app._onItemSummary(event));
|
favtabHtml.find(".item-name h4").click((event) => app._onItemSummary(event));
|
||||||
|
|
||||||
if (app.options.editable) {
|
if (app.options.editable) {
|
||||||
favtabHtml.find('.item-image').click(ev => app._onItemRoll(ev));
|
favtabHtml.find(".item-image").click((ev) => app._onItemRoll(ev));
|
||||||
let handler = ev => app._onDragStart(ev);
|
let handler = (ev) => app._onDragStart(ev);
|
||||||
favtabHtml.find('.item').each((i, li) => {
|
favtabHtml.find(".item").each((i, li) => {
|
||||||
if (li.classList.contains("inventory-header")) return;
|
if (li.classList.contains("inventory-header")) return;
|
||||||
li.setAttribute("draggable", true);
|
li.setAttribute("draggable", true);
|
||||||
li.addEventListener("dragstart", handler, false);
|
li.addEventListener("dragstart", handler, false);
|
||||||
});
|
});
|
||||||
//favtabHtml.find('.item-toggle').click(event => app._onToggleItem(event));
|
//favtabHtml.find('.item-toggle').click(event => app._onToggleItem(event));
|
||||||
favtabHtml.find('.item-edit').click(ev => {
|
favtabHtml.find(".item-edit").click((ev) => {
|
||||||
let itemId = $(ev.target).parents('.item')[0].dataset.itemId;
|
let itemId = $(ev.target).parents(".item")[0].dataset.itemId;
|
||||||
app.actor.items.get(itemId).sheet.render(true);
|
app.actor.items.get(itemId).sheet.render(true);
|
||||||
});
|
});
|
||||||
favtabHtml.find('.item-fav').click(ev => {
|
favtabHtml.find(".item-fav").click((ev) => {
|
||||||
let itemId = $(ev.target).parents('.item')[0].dataset.itemId;
|
let itemId = $(ev.target).parents(".item")[0].dataset.itemId;
|
||||||
let val = !app.actor.items.get(itemId).data.flags.favtab.isFavourite
|
let val = !app.actor.items.get(itemId).data.flags.favtab.isFavourite;
|
||||||
app.actor.items.get(itemId).update({
|
app.actor.items.get(itemId).update({
|
||||||
"flags.favtab.isFavourite": val
|
"flags.favtab.isFavourite": val
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sorting
|
// Sorting
|
||||||
favtabHtml.find('.item').on('drop', ev => {
|
favtabHtml.find(".item").on("drop", (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
let dropData = JSON.parse(ev.originalEvent.dataTransfer.getData('text/plain'));
|
let dropData = JSON.parse(ev.originalEvent.dataTransfer.getData("text/plain"));
|
||||||
// if (dropData.actorId !== app.actor.id || dropData.data.type === 'power') return;
|
// if (dropData.actorId !== app.actor.id || dropData.data.type === 'power') return;
|
||||||
if (dropData.actorId !== app.actor.id) return;
|
if (dropData.actorId !== app.actor.id) return;
|
||||||
let list = null;
|
let list = null;
|
||||||
if (dropData.data.type === 'feat') list = favFeats;
|
if (dropData.data.type === "feat") list = favFeats;
|
||||||
else list = favItems;
|
else list = favItems;
|
||||||
let dragSource = list.find(i => i.data._id === dropData.data._id);
|
let dragSource = list.find((i) => i.data._id === dropData.data._id);
|
||||||
let siblings = list.filter(i => i.data._id !== dropData.data._id);
|
let siblings = list.filter((i) => i.data._id !== dropData.data._id);
|
||||||
let targetId = ev.target.closest('.item').dataset.itemId;
|
let targetId = ev.target.closest(".item").dataset.itemId;
|
||||||
let dragTarget = siblings.find(s => s.data._id === targetId);
|
let dragTarget = siblings.find((s) => s.data._id === targetId);
|
||||||
|
|
||||||
if (dragTarget === undefined) return;
|
if (dragTarget === undefined) return;
|
||||||
const sortUpdates = SortingHelpers.performIntegerSort(dragSource, {
|
const sortUpdates = SortingHelpers.performIntegerSort(dragSource, {
|
||||||
target: dragTarget,
|
target: dragTarget,
|
||||||
siblings: siblings,
|
siblings: siblings,
|
||||||
sortKey: 'flags.favtab.sort'
|
sortKey: "flags.favtab.sort"
|
||||||
});
|
});
|
||||||
const updateData = sortUpdates.map(u => {
|
const updateData = sortUpdates.map((u) => {
|
||||||
const update = u.update;
|
const update = u.update;
|
||||||
update._id = u.target.data._id;
|
update._id = u.target.data._id;
|
||||||
return update;
|
return update;
|
||||||
|
@ -611,46 +706,44 @@ async function addSubTabs(app, html, data) {
|
||||||
if (data.options.subTabs == null) {
|
if (data.options.subTabs == null) {
|
||||||
//let subTabs = []; //{subgroup: '', target: '', active: false}
|
//let subTabs = []; //{subgroup: '', target: '', active: false}
|
||||||
data.options.subTabs = {};
|
data.options.subTabs = {};
|
||||||
html.find('[data-subgroup-selection] [data-subgroup]').each((idx, el) => {
|
html.find("[data-subgroup-selection] [data-subgroup]").each((idx, el) => {
|
||||||
let subgroup = el.getAttribute('data-subgroup');
|
let subgroup = el.getAttribute("data-subgroup");
|
||||||
let target = el.getAttribute('data-target');
|
let target = el.getAttribute("data-target");
|
||||||
let targetObj = {target: target, active: el.classList.contains("active")}
|
let targetObj = {target: target, active: el.classList.contains("active")};
|
||||||
if (data.options.subTabs.hasOwnProperty(subgroup)) {
|
if (data.options.subTabs.hasOwnProperty(subgroup)) {
|
||||||
data.options.subTabs[subgroup].push(targetObj);
|
data.options.subTabs[subgroup].push(targetObj);
|
||||||
} else {
|
} else {
|
||||||
data.options.subTabs[subgroup] = [];
|
data.options.subTabs[subgroup] = [];
|
||||||
data.options.subTabs[subgroup].push(targetObj);
|
data.options.subTabs[subgroup].push(targetObj);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const group in data.options.subTabs) {
|
for (const group in data.options.subTabs) {
|
||||||
data.options.subTabs[group].forEach(tab => {
|
data.options.subTabs[group].forEach((tab) => {
|
||||||
if (tab.active) {
|
if (tab.active) {
|
||||||
html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).addClass('active');
|
html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).addClass("active");
|
||||||
} else {
|
} else {
|
||||||
html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).removeClass('active');
|
html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).removeClass("active");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
html.find('[data-subgroup-selection]').children().on('click', event => {
|
html.find("[data-subgroup-selection]")
|
||||||
let subgroup = event.target.closest('[data-subgroup]').getAttribute('data-subgroup');
|
.children()
|
||||||
let target = event.target.closest('[data-target]').getAttribute('data-target');
|
.on("click", (event) => {
|
||||||
html.find(`[data-subgroup=${subgroup}]`).removeClass('active');
|
let subgroup = event.target.closest("[data-subgroup]").getAttribute("data-subgroup");
|
||||||
html.find(`[data-subgroup=${subgroup}][data-target=${target}]`).addClass('active');
|
let target = event.target.closest("[data-target]").getAttribute("data-target");
|
||||||
let tabId = data.options.subTabs[subgroup].find(tab => {
|
html.find(`[data-subgroup=${subgroup}]`).removeClass("active");
|
||||||
return tab.target == target
|
html.find(`[data-subgroup=${subgroup}][data-target=${target}]`).addClass("active");
|
||||||
|
let tabId = data.options.subTabs[subgroup].find((tab) => {
|
||||||
|
return tab.target == target;
|
||||||
});
|
});
|
||||||
data.options.subTabs[subgroup].map(el => {
|
data.options.subTabs[subgroup].map((el) => {
|
||||||
el.active = el.target == target;
|
el.active = el.target == target;
|
||||||
return el;
|
return el;
|
||||||
})
|
});
|
||||||
|
});
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Hooks.on("renderActorSheet5eCharacterNew", (app, html, data) => {
|
Hooks.on("renderActorSheet5eCharacterNew", (app, html, data) => {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ActorSheet5e from "./base.js";
|
||||||
* @extends {ActorSheet5e}
|
* @extends {ActorSheet5e}
|
||||||
*/
|
*/
|
||||||
export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get template() {
|
get template() {
|
||||||
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
||||||
|
@ -17,11 +16,13 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
return mergeObject(super.defaultOptions, {
|
return mergeObject(super.defaultOptions, {
|
||||||
classes: ["sw5e", "sheet", "actor", "npc"],
|
classes: ["sw5e", "sheet", "actor", "npc"],
|
||||||
width: 800,
|
width: 800,
|
||||||
tabs: [{
|
tabs: [
|
||||||
|
{
|
||||||
navSelector: ".root-tabs",
|
navSelector: ".root-tabs",
|
||||||
contentSelector: ".sheet-body",
|
contentSelector: ".sheet-body",
|
||||||
initial: "attributes"
|
initial: "attributes"
|
||||||
}],
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,28 +38,41 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
|
|
||||||
// Categorize Items as Features and Powers
|
// Categorize Items as Features and Powers
|
||||||
const features = {
|
const features = {
|
||||||
weapons: { label: game.i18n.localize("SW5E.AttackPl"), items: [] , hasActions: true, dataset: {type: "weapon", "weapon-type": "natural"} },
|
weapons: {
|
||||||
actions: { label: game.i18n.localize("SW5E.ActionPl"), items: [] , hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
|
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"}},
|
passive: {label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"}},
|
||||||
equipment: {label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}}
|
equipment: {label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start by classifying items into groups for rendering
|
// Start by classifying items into groups for rendering
|
||||||
let [forcepowers, techpowers, other] = data.items.reduce((arr, item) => {
|
let [forcepowers, techpowers, other] = data.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
item.img = item.img || CONST.DEFAULT_TOKEN;
|
item.img = item.img || CONST.DEFAULT_TOKEN;
|
||||||
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1;
|
||||||
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
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.isOnCooldown =
|
||||||
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
item.data.recharge && !!item.data.recharge.value && item.data.recharge.charged === false;
|
||||||
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
item.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0;
|
||||||
|
item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type);
|
||||||
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[0].push(item);
|
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[0].push(item);
|
||||||
else if (item.type === "power" && ["tec"].includes(item.data.school)) arr[1].push(item);
|
else if (item.type === "power" && ["tec"].includes(item.data.school)) arr[1].push(item);
|
||||||
else arr[2].push(item);
|
else arr[2].push(item);
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], [], []]);
|
},
|
||||||
|
[[], [], []]
|
||||||
|
);
|
||||||
|
|
||||||
// Apply item filters
|
// Apply item filters
|
||||||
forcepowers = this._filterItems(forcepowers, this._filters.forcePowerbook);
|
forcepowers = this._filterItems(forcepowers, this._filters.forcePowerbook);
|
||||||
|
@ -75,8 +89,7 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
else if (item.type === "feat") {
|
else if (item.type === "feat") {
|
||||||
if (item.data.activation.type) features.actions.items.push(item);
|
if (item.data.activation.type) features.actions.items.push(item);
|
||||||
else features.passive.items.push(item);
|
else features.passive.items.push(item);
|
||||||
}
|
} else features.equipment.items.push(item);
|
||||||
else features.equipment.items.push(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign and return
|
// Assign and return
|
||||||
|
@ -107,7 +120,6 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
|
|
||||||
// Format NPC Challenge Rating
|
// Format NPC Challenge Rating
|
||||||
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
||||||
let crv = "data.details.cr";
|
let crv = "data.details.cr";
|
||||||
|
@ -145,4 +157,3 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
this.actor.update({"data.attributes.hp.value": hp, "data.attributes.hp.max": hp});
|
this.actor.update({"data.attributes.hp.value": hp, "data.attributes.hp.max": hp});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ActorSheet5e from "./base.js";
|
||||||
* @extends {ActorSheet5e}
|
* @extends {ActorSheet5e}
|
||||||
*/
|
*/
|
||||||
export default class ActorSheet5eStarship extends ActorSheet5e {
|
export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get template() {
|
get template() {
|
||||||
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
||||||
|
@ -17,12 +16,13 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
return mergeObject(super.defaultOptions, {
|
return mergeObject(super.defaultOptions, {
|
||||||
classes: ["sw5e", "sheet", "actor", "starship"],
|
classes: ["sw5e", "sheet", "actor", "starship"],
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 775,
|
tabs: [
|
||||||
tabs: [{
|
{
|
||||||
navSelector: ".root-tabs",
|
navSelector: ".root-tabs",
|
||||||
contentSelector: ".sheet-body",
|
contentSelector: ".sheet-body",
|
||||||
initial: "attributes"
|
initial: "attributes"
|
||||||
}],
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,29 +33,47 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
|
|
||||||
// Categorize Items as Features and Powers
|
// Categorize Items as Features and Powers
|
||||||
const features = {
|
const features = {
|
||||||
weapons: { label: game.i18n.localize("SW5E.ItemTypeWeaponPl"), items: [], hasActions: true, dataset: {type: "weapon", "weapon-type": "natural"} },
|
weapons: {
|
||||||
|
label: game.i18n.localize("SW5E.ItemTypeWeaponPl"),
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {"type": "weapon", "weapon-type": "natural"}
|
||||||
|
},
|
||||||
passive: {label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"}},
|
passive: {label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"}},
|
||||||
equipment: {label: game.i18n.localize("SW5E.StarshipEquipment"), items: [], dataset: {type: "equipment"}},
|
equipment: {label: game.i18n.localize("SW5E.StarshipEquipment"), items: [], dataset: {type: "equipment"}},
|
||||||
starshipfeatures: { label: game.i18n.localize("SW5E.StarshipfeaturePl"), items: [], hasActions: true, dataset: {type: "starshipfeature"} },
|
starshipfeatures: {
|
||||||
starshipmods: { label: game.i18n.localize("SW5E.StarshipmodPl"), items: [], hasActions: false, dataset: {type: "starshipmod"} }
|
label: game.i18n.localize("SW5E.StarshipfeaturePl"),
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {type: "starshipfeature"}
|
||||||
|
},
|
||||||
|
starshipmods: {
|
||||||
|
label: game.i18n.localize("SW5E.StarshipmodPl"),
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "starshipmod"}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start by classifying items into groups for rendering
|
// Start by classifying items into groups for rendering
|
||||||
let [forcepowers, techpowers, other] = data.items.reduce((arr, item) => {
|
let [forcepowers, techpowers, other] = data.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
item.img = item.img || CONST.DEFAULT_TOKEN;
|
item.img = item.img || CONST.DEFAULT_TOKEN;
|
||||||
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1;
|
||||||
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
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.isOnCooldown =
|
||||||
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
item.data.recharge && !!item.data.recharge.value && item.data.recharge.charged === false;
|
||||||
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
item.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0;
|
||||||
|
item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type);
|
||||||
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[0].push(item);
|
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[0].push(item);
|
||||||
else if (item.type === "power" && ["tec"].includes(item.data.school)) arr[1].push(item);
|
else if (item.type === "power" && ["tec"].includes(item.data.school)) arr[1].push(item);
|
||||||
else arr[2].push(item);
|
else arr[2].push(item);
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], [], []]);
|
},
|
||||||
|
[[], [], []]
|
||||||
|
);
|
||||||
|
|
||||||
// Apply item filters
|
// Apply item filters
|
||||||
forcepowers = this._filterItems(forcepowers, this._filters.forcePowerbook);
|
forcepowers = this._filterItems(forcepowers, this._filters.forcePowerbook);
|
||||||
|
@ -72,14 +90,11 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
else if (item.type === "feat") {
|
else if (item.type === "feat") {
|
||||||
if (item.data.activation.type) features.actions.items.push(item);
|
if (item.data.activation.type) features.actions.items.push(item);
|
||||||
else features.passive.items.push(item);
|
else features.passive.items.push(item);
|
||||||
}
|
} else if (item.type === "starshipfeature") {
|
||||||
else if ( item.type === "starshipfeature" ) {
|
|
||||||
features.starshipfeatures.items.push(item);
|
features.starshipfeatures.items.push(item);
|
||||||
}
|
} else if (item.type === "starshipmod") {
|
||||||
else if ( item.type === "starshipmod" ) {
|
|
||||||
features.starshipmods.items.push(item);
|
features.starshipmods.items.push(item);
|
||||||
}
|
} else features.equipment.items.push(item);
|
||||||
else features.equipment.items.push(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign and return
|
// Assign and return
|
||||||
|
@ -88,7 +103,6 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
// data.techPowerbook = techPowerbook;
|
// data.techPowerbook = techPowerbook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
|
@ -116,7 +130,6 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
|
|
||||||
// Format NPC Challenge Rating
|
// Format NPC Challenge Rating
|
||||||
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
||||||
let crv = "data.details.cr";
|
let crv = "data.details.cr";
|
||||||
|
@ -136,11 +149,6 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
activateListeners(html) {
|
activateListeners(html) {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
html.find(".health .rollable").click(this._onRollHPFormula.bind(this));
|
html.find(".health .rollable").click(this._onRollHPFormula.bind(this));
|
||||||
html.find('.refuel').click(this._onIncrementFuelLevel.bind(this));
|
|
||||||
html.find('.burnfuel').click(this._onDecrementFuelLevel.bind(this));
|
|
||||||
html.find('#engineslidervalue')[0].addEventListener('input', this._engineSliderUpdate.bind(this));
|
|
||||||
html.find('#shieldslidervalue')[0].addEventListener('input', this._shieldSliderUpdate.bind(this));
|
|
||||||
html.find('#weaponslidervalue')[0].addEventListener('input', this._weaponSliderUpdate.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -158,96 +166,4 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
AudioHelper.play({src: CONFIG.sounds.dice});
|
AudioHelper.play({src: CONFIG.sounds.dice});
|
||||||
this.actor.update({"data.attributes.hp.value": hp, "data.attributes.hp.max": hp});
|
this.actor.update({"data.attributes.hp.value": hp, "data.attributes.hp.max": hp});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle refueling a starship
|
|
||||||
* @param {Event} event The original click event
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_onIncrementFuelLevel(event) {
|
|
||||||
// event.preventDefault();
|
|
||||||
// const fuelcaparray = this.actor.data.effects.changes;
|
|
||||||
// var fuelcappos = fuelcaparray.indexOf('fuel.cap');
|
|
||||||
// const refuel = this.actor.data.effect.changes[fuelcappos].value;
|
|
||||||
this.actor.update({"data.attributes.fuel.value": this.actor.data.data.attributes.fuel.cap});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a starship burning fuel
|
|
||||||
* @param {Event} event The original click event
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_onDecrementFuelLevel(event) {
|
|
||||||
// event.preventDefault();
|
|
||||||
// const fuelcaparray = this.actor.data.effects.changes;
|
|
||||||
// var fuelcappos = fuelcaparray.indexOf('fuel.cap');
|
|
||||||
// const refuel = this.actor.data.effect.changes[fuelcappos].value;
|
|
||||||
this.actor.update({"data.attributes.fuel.value": this.actor.data.data.attributes.fuel.value - 1});
|
|
||||||
}
|
|
||||||
_engineSliderUpdate(input) {
|
|
||||||
var symbol;
|
|
||||||
var coefficient;
|
|
||||||
switch(input.target.value) {
|
|
||||||
case "0":
|
|
||||||
symbol = "↓";
|
|
||||||
coefficient = 0.5;
|
|
||||||
break;
|
|
||||||
case "1":
|
|
||||||
symbol = "=";
|
|
||||||
coefficient = 1;
|
|
||||||
break;
|
|
||||||
case "2":
|
|
||||||
symbol = "↑";
|
|
||||||
coefficient = 2;
|
|
||||||
};
|
|
||||||
let slideroutput = symbol;
|
|
||||||
document.querySelector('#engineslideroutput').value = slideroutput;
|
|
||||||
this.actor.update({"data.attributes.power.routing.engines": coefficient});
|
|
||||||
}
|
|
||||||
|
|
||||||
_shieldSliderUpdate(input) {
|
|
||||||
var symbol;
|
|
||||||
var coefficient;
|
|
||||||
switch(input.target.value) {
|
|
||||||
case "0":
|
|
||||||
symbol = "↓";
|
|
||||||
coefficient = 0.5;
|
|
||||||
break;
|
|
||||||
case "1":
|
|
||||||
symbol = "=";
|
|
||||||
coefficient = 1;
|
|
||||||
break;
|
|
||||||
case "2":
|
|
||||||
symbol = "↑";
|
|
||||||
coefficient = 2;
|
|
||||||
};
|
|
||||||
let slideroutput = symbol;
|
|
||||||
document.querySelector('#shieldslideroutput').value = slideroutput;
|
|
||||||
this.actor.update({"data.attributes.power.routing.shields": coefficient});
|
|
||||||
}
|
|
||||||
|
|
||||||
_weaponSliderUpdate(input) {
|
|
||||||
var symbol;
|
|
||||||
var coefficient;
|
|
||||||
switch(input.target.value) {
|
|
||||||
case "0":
|
|
||||||
symbol = "↓";
|
|
||||||
coefficient = 0.5;
|
|
||||||
break;
|
|
||||||
case "1":
|
|
||||||
symbol = "=";
|
|
||||||
coefficient = 1;
|
|
||||||
break;
|
|
||||||
case "2":
|
|
||||||
symbol = "↑";
|
|
||||||
coefficient = 2;
|
|
||||||
};
|
|
||||||
let slideroutput = symbol;
|
|
||||||
document.querySelector('#weaponslideroutput').value = slideroutput;
|
|
||||||
this.actor.update({"data.attributes.power.routing.weapons": coefficient});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,12 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new cargo entry for a vehicle Actor.
|
* Creates a new cargo entry for a vehicle Actor.
|
||||||
*/
|
*/
|
||||||
static get newCargo() {
|
static get newCargo() {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: "",
|
||||||
quantity: 1
|
quantity: 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -46,7 +45,6 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_computeEncumbrance(totalWeight, actorData) {
|
_computeEncumbrance(totalWeight, actorData) {
|
||||||
|
|
||||||
// Compute currency weight
|
// Compute currency weight
|
||||||
const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0);
|
const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0);
|
||||||
totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
||||||
|
@ -75,25 +73,24 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareCrewedItem(item) {
|
_prepareCrewedItem(item) {
|
||||||
|
|
||||||
// Determine crewed status
|
// Determine crewed status
|
||||||
const isCrewed = item.data.crewed;
|
const isCrewed = item.data.crewed;
|
||||||
item.toggleClass = isCrewed ? 'active' : '';
|
item.toggleClass = isCrewed ? "active" : "";
|
||||||
item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? 'Crewed' : 'Uncrewed'}`);
|
item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? "Crewed" : "Uncrewed"}`);
|
||||||
|
|
||||||
// Handle crew actions
|
// Handle crew actions
|
||||||
if (item.type === 'feat' && item.data.activation.type === 'crew') {
|
if (item.type === "feat" && item.data.activation.type === "crew") {
|
||||||
item.crew = item.data.activation.cost;
|
item.crew = item.data.activation.cost;
|
||||||
item.cover = game.i18n.localize(`SW5E.${item.data.cover ? 'CoverTotal' : 'None'}`);
|
item.cover = game.i18n.localize(`SW5E.${item.data.cover ? "CoverTotal" : "None"}`);
|
||||||
if (item.data.cover === .5) item.cover = '½';
|
if (item.data.cover === 0.5) item.cover = "½";
|
||||||
else if (item.data.cover === .75) item.cover = '¾';
|
else if (item.data.cover === 0.75) item.cover = "¾";
|
||||||
else if (item.data.cover === null) item.cover = '—';
|
else if (item.data.cover === null) item.cover = "—";
|
||||||
if (item.crew < 1 || item.crew === null) item.crew = '—';
|
if (item.crew < 1 || item.crew === null) item.crew = "—";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare vehicle weapons
|
// Prepare vehicle weapons
|
||||||
if (item.type === 'equipment' || item.type === 'weapon') {
|
if (item.type === "equipment" || item.type === "weapon") {
|
||||||
item.threshold = item.data.hp.dt ? item.data.hp.dt : '—';
|
item.threshold = item.data.hp.dt ? item.data.hp.dt : "—";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,111 +101,125 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
const cargoColumns = [{
|
const cargoColumns = [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'quantity',
|
css: "item-qty",
|
||||||
editable: 'Number'
|
property: "quantity",
|
||||||
}];
|
editable: "Number"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const equipmentColumns = [{
|
const equipmentColumns = [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'data.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.AC"),
|
||||||
}, {
|
css: "item-ac",
|
||||||
label: game.i18n.localize('SW5E.HP'),
|
property: "data.armor.value"
|
||||||
css: 'item-hp',
|
},
|
||||||
property: 'data.hp.value',
|
{
|
||||||
editable: 'Number'
|
label: game.i18n.localize("SW5E.HP"),
|
||||||
}, {
|
css: "item-hp",
|
||||||
label: game.i18n.localize('SW5E.Threshold'),
|
property: "data.hp.value",
|
||||||
css: 'item-threshold',
|
editable: "Number"
|
||||||
property: 'threshold'
|
},
|
||||||
}];
|
{
|
||||||
|
label: game.i18n.localize("SW5E.Threshold"),
|
||||||
|
css: "item-threshold",
|
||||||
|
property: "threshold"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const features = {
|
const features = {
|
||||||
actions: {
|
actions: {
|
||||||
label: game.i18n.localize('SW5E.ActionPl'),
|
label: game.i18n.localize("SW5E.ActionPl"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'feat', 'activation.type': 'crew'},
|
dataset: {"type": "feat", "activation.type": "crew"},
|
||||||
columns: [{
|
columns: [
|
||||||
label: game.i18n.localize('SW5E.VehicleCrew'),
|
{
|
||||||
css: 'item-crew',
|
label: game.i18n.localize("SW5E.VehicleCrew"),
|
||||||
property: 'crew'
|
css: "item-crew",
|
||||||
}, {
|
property: "crew"
|
||||||
label: game.i18n.localize('SW5E.Cover'),
|
},
|
||||||
css: 'item-cover',
|
{
|
||||||
property: 'cover'
|
label: game.i18n.localize("SW5E.Cover"),
|
||||||
}]
|
css: "item-cover",
|
||||||
|
property: "cover"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
equipment: {
|
equipment: {
|
||||||
label: game.i18n.localize('SW5E.ItemTypeEquipment'),
|
label: game.i18n.localize("SW5E.ItemTypeEquipment"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'equipment', 'armor.type': 'vehicle'},
|
dataset: {"type": "equipment", "armor.type": "vehicle"},
|
||||||
columns: equipmentColumns
|
columns: equipmentColumns
|
||||||
},
|
},
|
||||||
passive: {
|
passive: {
|
||||||
label: game.i18n.localize('SW5E.Features'),
|
label: game.i18n.localize("SW5E.Features"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'feat'}
|
dataset: {type: "feat"}
|
||||||
},
|
},
|
||||||
reactions: {
|
reactions: {
|
||||||
label: game.i18n.localize('SW5E.ReactionPl'),
|
label: game.i18n.localize("SW5E.ReactionPl"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'feat', 'activation.type': 'reaction'}
|
dataset: {"type": "feat", "activation.type": "reaction"}
|
||||||
},
|
},
|
||||||
weapons: {
|
weapons: {
|
||||||
label: game.i18n.localize('SW5E.ItemTypeWeaponPl'),
|
label: game.i18n.localize("SW5E.ItemTypeWeaponPl"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'weapon', 'weapon-type': 'siege'},
|
dataset: {"type": "weapon", "weapon-type": "siege"},
|
||||||
columns: equipmentColumns
|
columns: equipmentColumns
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cargo = {
|
const cargo = {
|
||||||
crew: {
|
crew: {
|
||||||
label: game.i18n.localize('SW5E.VehicleCrew'),
|
label: game.i18n.localize("SW5E.VehicleCrew"),
|
||||||
items: data.data.cargo.crew,
|
items: data.data.cargo.crew,
|
||||||
css: 'cargo-row crew',
|
css: "cargo-row crew",
|
||||||
editableName: true,
|
editableName: true,
|
||||||
dataset: {type: 'crew'},
|
dataset: {type: "crew"},
|
||||||
columns: cargoColumns
|
columns: cargoColumns
|
||||||
},
|
},
|
||||||
passengers: {
|
passengers: {
|
||||||
label: game.i18n.localize('SW5E.VehiclePassengers'),
|
label: game.i18n.localize("SW5E.VehiclePassengers"),
|
||||||
items: data.data.cargo.passengers,
|
items: data.data.cargo.passengers,
|
||||||
css: 'cargo-row passengers',
|
css: "cargo-row passengers",
|
||||||
editableName: true,
|
editableName: true,
|
||||||
dataset: {type: 'passengers'},
|
dataset: {type: "passengers"},
|
||||||
columns: cargoColumns
|
columns: cargoColumns
|
||||||
},
|
},
|
||||||
cargo: {
|
cargo: {
|
||||||
label: game.i18n.localize('SW5E.VehicleCargo'),
|
label: game.i18n.localize("SW5E.VehicleCargo"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'loot'},
|
dataset: {type: "loot"},
|
||||||
columns: [{
|
columns: [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'data.quantity',
|
css: "item-qty",
|
||||||
editable: 'Number'
|
property: "data.quantity",
|
||||||
}, {
|
editable: "Number"
|
||||||
label: game.i18n.localize('SW5E.Price'),
|
},
|
||||||
css: 'item-price',
|
{
|
||||||
property: 'data.price',
|
label: game.i18n.localize("SW5E.Price"),
|
||||||
editable: 'Number'
|
css: "item-price",
|
||||||
}, {
|
property: "data.price",
|
||||||
label: game.i18n.localize('SW5E.Weight'),
|
editable: "Number"
|
||||||
css: 'item-weight',
|
},
|
||||||
property: 'data.weight',
|
{
|
||||||
editable: 'Number'
|
label: game.i18n.localize("SW5E.Weight"),
|
||||||
}]
|
css: "item-weight",
|
||||||
|
property: "data.weight",
|
||||||
|
editable: "Number"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -234,8 +245,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
features.equipment.items.push(item);
|
features.equipment.items.push(item);
|
||||||
break;
|
break;
|
||||||
case "feat":
|
case "feat":
|
||||||
if ( !item.data.activation.type || (item.data.activation.type === "none") ) features.passive.items.push(item);
|
if (!item.data.activation.type || item.data.activation.type === "none")
|
||||||
else if (item.data.activation.type === 'reaction') features.reactions.items.push(item);
|
features.passive.items.push(item);
|
||||||
|
else if (item.data.activation.type === "reaction") features.reactions.items.push(item);
|
||||||
else features.actions.items.push(item);
|
else features.actions.items.push(item);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -259,21 +271,21 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
if (!this.isEditable) return;
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
html.find('.item-toggle').click(this._onToggleItem.bind(this));
|
html.find(".item-toggle").click(this._onToggleItem.bind(this));
|
||||||
html.find('.item-hp input')
|
html.find(".item-hp input")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onHPChange.bind(this));
|
.change(this._onHPChange.bind(this));
|
||||||
|
|
||||||
html.find('.item:not(.cargo-row) input[data-property]')
|
html.find(".item:not(.cargo-row) input[data-property]")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onEditInSheet.bind(this));
|
.change(this._onEditInSheet.bind(this));
|
||||||
|
|
||||||
html.find('.cargo-row input')
|
html.find(".cargo-row input")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onCargoRowChange.bind(this));
|
.change(this._onCargoRowChange.bind(this));
|
||||||
|
|
||||||
if (this.actor.data.data.attributes.actions.stations) {
|
if (this.actor.data.data.attributes.actions.stations) {
|
||||||
html.find('.counter.actions, .counter.action-thresholds').hide();
|
html.find(".counter.actions, .counter.action-thresholds").hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,9 +300,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
_onCargoRowChange(event) {
|
_onCargoRowChange(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const target = event.currentTarget;
|
const target = event.currentTarget;
|
||||||
const row = target.closest('.item');
|
const row = target.closest(".item");
|
||||||
const idx = Number(row.dataset.itemId);
|
const idx = Number(row.dataset.itemId);
|
||||||
const property = row.classList.contains('crew') ? 'crew' : 'passengers';
|
const property = row.classList.contains("crew") ? "crew" : "passengers";
|
||||||
|
|
||||||
// Get the cargo entry
|
// Get the cargo entry
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[property]);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[property]);
|
||||||
|
@ -298,10 +310,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
if (!entry) return null;
|
if (!entry) return null;
|
||||||
|
|
||||||
// Update the cargo value
|
// Update the cargo value
|
||||||
const key = target.dataset.property || 'name';
|
const key = target.dataset.property || "name";
|
||||||
const type = target.dataset.dtype;
|
const type = target.dataset.dtype;
|
||||||
let value = target.value;
|
let value = target.value;
|
||||||
if (type === 'Number') value = Number(value);
|
if (type === "Number") value = Number(value);
|
||||||
entry[key] = value;
|
entry[key] = value;
|
||||||
|
|
||||||
// Perform the Actor update
|
// Perform the Actor update
|
||||||
|
@ -318,14 +330,18 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onEditInSheet(event) {
|
_onEditInSheet(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const property = event.currentTarget.dataset.property;
|
const property = event.currentTarget.dataset.property;
|
||||||
const type = event.currentTarget.dataset.dtype;
|
const type = event.currentTarget.dataset.dtype;
|
||||||
let value = event.currentTarget.value;
|
let value = event.currentTarget.value;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'Number': value = parseInt(value); break;
|
case "Number":
|
||||||
case 'Boolean': value = value === 'true'; break;
|
value = parseInt(value);
|
||||||
|
break;
|
||||||
|
case "Boolean":
|
||||||
|
value = value === "true";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return item.update({[`${property}`]: value});
|
return item.update({[`${property}`]: value});
|
||||||
}
|
}
|
||||||
|
@ -342,7 +358,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const target = event.currentTarget;
|
const target = event.currentTarget;
|
||||||
const type = target.dataset.type;
|
const type = target.dataset.type;
|
||||||
if (type === 'crew' || type === 'passengers') {
|
if (type === "crew" || type === "passengers") {
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]);
|
||||||
cargo.push(this.constructor.newCargo);
|
cargo.push(this.constructor.newCargo);
|
||||||
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
||||||
|
@ -360,10 +376,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onItemDelete(event) {
|
_onItemDelete(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const row = event.currentTarget.closest('.item');
|
const row = event.currentTarget.closest(".item");
|
||||||
if (row.classList.contains('cargo-row')) {
|
if (row.classList.contains("cargo-row")) {
|
||||||
const idx = Number(row.dataset.itemId);
|
const idx = Number(row.dataset.itemId);
|
||||||
const type = row.classList.contains('crew') ? 'crew' : 'passengers';
|
const type = row.classList.contains("crew") ? "crew" : "passengers";
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx);
|
||||||
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
||||||
}
|
}
|
||||||
|
@ -376,7 +392,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
const cargoTypes = ["weapon", "equipment", "consumable", "tool", "loot", "backpack"];
|
const cargoTypes = ["weapon", "equipment", "consumable", "tool", "loot", "backpack"];
|
||||||
const isCargo = cargoTypes.includes(itemData.type) && (this._tabs[0].active === "cargo");
|
const isCargo = cargoTypes.includes(itemData.type) && this._tabs[0].active === "cargo";
|
||||||
foundry.utils.setProperty(itemData, "flags.sw5e.vehicleCargo", isCargo);
|
foundry.utils.setProperty(itemData, "flags.sw5e.vehicleCargo", isCargo);
|
||||||
return super._onDropItemCreate(itemData);
|
return super._onDropItemCreate(itemData);
|
||||||
}
|
}
|
||||||
|
@ -391,11 +407,11 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onHPChange(event) {
|
_onHPChange(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max);
|
const hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max);
|
||||||
event.currentTarget.value = hp;
|
event.currentTarget.value = hp;
|
||||||
return item.update({'data.hp.value': hp});
|
return item.update({"data.hp.value": hp});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -408,9 +424,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onToggleItem(event) {
|
_onToggleItem(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const crewed = !!item.data.data.crewed;
|
const crewed = !!item.data.data.crewed;
|
||||||
return item.update({'data.crewed': !crewed});
|
return item.update({"data.crewed": !crewed});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import ActorHitDiceConfig from "../../../apps/hit-dice-config.js";
|
||||||
import ActorMovementConfig from "../../../apps/movement-config.js";
|
import ActorMovementConfig from "../../../apps/movement-config.js";
|
||||||
import ActorSensesConfig from "../../../apps/senses-config.js";
|
import ActorSensesConfig from "../../../apps/senses-config.js";
|
||||||
import ActorTypeConfig from "../../../apps/actor-type.js";
|
import ActorTypeConfig from "../../../apps/actor-type.js";
|
||||||
import {SW5E} from '../../../config.js';
|
import {SW5E} from "../../../config.js";
|
||||||
import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js";
|
import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +54,6 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get template() {
|
get template() {
|
||||||
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/oldActor/limited-sheet.html";
|
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/oldActor/limited-sheet.html";
|
||||||
|
@ -65,7 +64,6 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
getData(options) {
|
getData(options) {
|
||||||
|
|
||||||
// Basic data
|
// Basic data
|
||||||
let isOwner = this.actor.isOwner;
|
let isOwner = this.actor.isOwner;
|
||||||
const data = {
|
const data = {
|
||||||
|
@ -77,7 +75,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
isCharacter: this.actor.type === "character",
|
isCharacter: this.actor.type === "character",
|
||||||
isNPC: this.actor.type === "npc",
|
isNPC: this.actor.type === "npc",
|
||||||
isStarship: this.actor.type === "starship",
|
isStarship: this.actor.type === "starship",
|
||||||
isVehicle: this.actor.type === 'vehicle',
|
isVehicle: this.actor.type === "vehicle",
|
||||||
config: CONFIG.SW5E,
|
config: CONFIG.SW5E,
|
||||||
rollData: this.actor.getRollData.bind(this.actor)
|
rollData: this.actor.getRollData.bind(this.actor)
|
||||||
};
|
};
|
||||||
|
@ -132,7 +130,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
data.effects = prepareActiveEffectCategories(this.actor.effects);
|
data.effects = prepareActiveEffectCategories(this.actor.effects);
|
||||||
|
|
||||||
// Return data to the sheet
|
// Return data to the sheet
|
||||||
return data
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -151,31 +149,35 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
let speeds = [
|
let speeds = [
|
||||||
[movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`],
|
[movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`],
|
||||||
[movement.climb, `${game.i18n.localize("SW5E.MovementClimb")} ${movement.climb}`],
|
[movement.climb, `${game.i18n.localize("SW5E.MovementClimb")} ${movement.climb}`],
|
||||||
[movement.fly, `${game.i18n.localize("SW5E.MovementFly")} ${movement.fly}` + (movement.hover ? ` (${game.i18n.localize("SW5E.MovementHover")})` : "")],
|
[
|
||||||
|
movement.fly,
|
||||||
|
`${game.i18n.localize("SW5E.MovementFly")} ${movement.fly}` +
|
||||||
|
(movement.hover ? ` (${game.i18n.localize("SW5E.MovementHover")})` : "")
|
||||||
|
],
|
||||||
[movement.swim, `${game.i18n.localize("SW5E.MovementSwim")} ${movement.swim}`]
|
[movement.swim, `${game.i18n.localize("SW5E.MovementSwim")} ${movement.swim}`]
|
||||||
]
|
];
|
||||||
if (largestPrimary) {
|
if (largestPrimary) {
|
||||||
speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]);
|
speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter and sort speeds on their values
|
// Filter and sort speeds on their values
|
||||||
speeds = speeds.filter(s => !!s[0]).sort((a, b) => b[0] - a[0]);
|
speeds = speeds.filter((s) => !!s[0]).sort((a, b) => b[0] - a[0]);
|
||||||
|
|
||||||
// Case 1: Largest as primary
|
// Case 1: Largest as primary
|
||||||
if (largestPrimary) {
|
if (largestPrimary) {
|
||||||
let primary = speeds.shift();
|
let primary = speeds.shift();
|
||||||
return {
|
return {
|
||||||
primary: `${primary ? primary[1] : "0"} ${movement.units}`,
|
primary: `${primary ? primary[1] : "0"} ${movement.units}`,
|
||||||
special: speeds.map(s => s[1]).join(", ")
|
special: speeds.map((s) => s[1]).join(", ")
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: Walk as primary
|
// Case 2: Walk as primary
|
||||||
else {
|
else {
|
||||||
return {
|
return {
|
||||||
primary: `${movement.walk || 0} ${movement.units}`,
|
primary: `${movement.walk || 0} ${movement.units}`,
|
||||||
special: speeds.length ? speeds.map(s => s[1]).join(", ") : ""
|
special: speeds.length ? speeds.map((s) => s[1]).join(", ") : ""
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +187,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const senses = actorData.data.attributes.senses || {};
|
const senses = actorData.data.attributes.senses || {};
|
||||||
const tags = {};
|
const tags = {};
|
||||||
for (let [k, label] of Object.entries(CONFIG.SW5E.senses)) {
|
for (let [k, label] of Object.entries(CONFIG.SW5E.senses)) {
|
||||||
const v = senses[k] ?? 0
|
const v = senses[k] ?? 0;
|
||||||
if (v === 0) continue;
|
if (v === 0) continue;
|
||||||
tags[k] = `${game.i18n.localize(label)} ${v} ${senses.units}`;
|
tags[k] = `${game.i18n.localize(label)} ${v} ${senses.units}`;
|
||||||
}
|
}
|
||||||
|
@ -202,14 +204,14 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
*/
|
*/
|
||||||
_prepareTraits(traits) {
|
_prepareTraits(traits) {
|
||||||
const map = {
|
const map = {
|
||||||
"dr": CONFIG.SW5E.damageResistanceTypes,
|
dr: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"di": CONFIG.SW5E.damageResistanceTypes,
|
di: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"dv": CONFIG.SW5E.damageResistanceTypes,
|
dv: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"ci": CONFIG.SW5E.conditionTypes,
|
ci: CONFIG.SW5E.conditionTypes,
|
||||||
"languages": CONFIG.SW5E.languages,
|
languages: CONFIG.SW5E.languages,
|
||||||
"armorProf": CONFIG.SW5E.armorProficiencies,
|
armorProf: CONFIG.SW5E.armorProficiencies,
|
||||||
"weaponProf": CONFIG.SW5E.weaponProficiencies,
|
weaponProf: CONFIG.SW5E.weaponProficiencies,
|
||||||
"toolProf": CONFIG.SW5E.toolProficiencies
|
toolProf: CONFIG.SW5E.toolProficiencies
|
||||||
};
|
};
|
||||||
for (let [t, choices] of Object.entries(map)) {
|
for (let [t, choices] of Object.entries(map)) {
|
||||||
const trait = traits[t];
|
const trait = traits[t];
|
||||||
|
@ -225,7 +227,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Add custom entry
|
// Add custom entry
|
||||||
if (trait.custom) {
|
if (trait.custom) {
|
||||||
trait.custom.split(";").forEach((c, i) => trait.selected[`custom${i+1}`] = c.trim());
|
trait.custom.split(";").forEach((c, i) => (trait.selected[`custom${i + 1}`] = c.trim()));
|
||||||
}
|
}
|
||||||
trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive";
|
trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive";
|
||||||
}
|
}
|
||||||
|
@ -246,9 +248,9 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Define some mappings
|
// Define some mappings
|
||||||
const sections = {
|
const sections = {
|
||||||
"atwill": -20,
|
atwill: -20,
|
||||||
"innate": -10,
|
innate: -10,
|
||||||
"pact": 0.5
|
pact: 0.5
|
||||||
};
|
};
|
||||||
|
|
||||||
// Label power slot uses headers
|
// Label power slot uses headers
|
||||||
|
@ -265,7 +267,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
label: label,
|
label: label,
|
||||||
usesSlots: i > 0,
|
usesSlots: i > 0,
|
||||||
canCreate: owner,
|
canCreate: owner,
|
||||||
canPrepare: (data.actor.type === "character") && (i >= 1),
|
canPrepare: data.actor.type === "character" && i >= 1,
|
||||||
powers: [],
|
powers: [],
|
||||||
uses: useLabels[i] || value || 0,
|
uses: useLabels[i] || value || 0,
|
||||||
slots: useLabels[i] || max || 0,
|
slots: useLabels[i] || max || 0,
|
||||||
|
@ -279,7 +281,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const maxLevel = Array.fromRange(10).reduce((max, i) => {
|
const maxLevel = Array.fromRange(10).reduce((max, i) => {
|
||||||
if (i === 0) return max;
|
if (i === 0) return max;
|
||||||
const level = levels[`power${i}`];
|
const level = levels[`power${i}`];
|
||||||
if ( (level.max || level.override ) && ( i > max ) ) max = i;
|
if ((level.max || level.override) && i > max) max = i;
|
||||||
return max;
|
return max;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
@ -309,7 +311,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over every power item, adding powers to the powerbook by section
|
// Iterate over every power item, adding powers to the powerbook by section
|
||||||
powers.forEach(power => {
|
powers.forEach((power) => {
|
||||||
const mode = power.data.preparation.mode || "prepared";
|
const mode = power.data.preparation.mode || "prepared";
|
||||||
let s = power.data.level || 0;
|
let s = power.data.level || 0;
|
||||||
const sl = `power${s}`;
|
const sl = `power${s}`;
|
||||||
|
@ -352,13 +354,13 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_filterItems(items, filters) {
|
_filterItems(items, filters) {
|
||||||
return items.filter(item => {
|
return items.filter((item) => {
|
||||||
const data = item.data;
|
const data = item.data;
|
||||||
|
|
||||||
// Action usage
|
// Action usage
|
||||||
for (let f of ["action", "bonus", "reaction"]) {
|
for (let f of ["action", "bonus", "reaction"]) {
|
||||||
if (filters.has(f)) {
|
if (filters.has(f)) {
|
||||||
if ((data.activation && (data.activation.type !== f))) return false;
|
if (data.activation && data.activation.type !== f) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,61 +407,59 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
activateListeners(html) {
|
activateListeners(html) {
|
||||||
|
|
||||||
// Activate Item Filters
|
// Activate Item Filters
|
||||||
const filterLists = html.find(".filter-list");
|
const filterLists = html.find(".filter-list");
|
||||||
filterLists.each(this._initializeFilterItemList.bind(this));
|
filterLists.each(this._initializeFilterItemList.bind(this));
|
||||||
filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this));
|
filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this));
|
||||||
|
|
||||||
// Item summaries
|
// Item summaries
|
||||||
html.find('.item .item-name.rollable h4').click(event => this._onItemSummary(event));
|
html.find(".item .item-name.rollable h4").click((event) => this._onItemSummary(event));
|
||||||
|
|
||||||
// View Item Sheets
|
// View Item Sheets
|
||||||
html.find('.item-edit').click(this._onItemEdit.bind(this));
|
html.find(".item-edit").click(this._onItemEdit.bind(this));
|
||||||
|
|
||||||
// Editable Only Listeners
|
// Editable Only Listeners
|
||||||
if (this.isEditable) {
|
if (this.isEditable) {
|
||||||
|
|
||||||
// Input focus and update
|
// Input focus and update
|
||||||
const inputs = html.find("input");
|
const inputs = html.find("input");
|
||||||
inputs.focus(ev => ev.currentTarget.select());
|
inputs.focus((ev) => ev.currentTarget.select());
|
||||||
inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this));
|
inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this));
|
||||||
|
|
||||||
// Ability Proficiency
|
// Ability Proficiency
|
||||||
html.find('.ability-proficiency').click(this._onToggleAbilityProficiency.bind(this));
|
html.find(".ability-proficiency").click(this._onToggleAbilityProficiency.bind(this));
|
||||||
|
|
||||||
// Toggle Skill Proficiency
|
// Toggle Skill Proficiency
|
||||||
html.find('.skill-proficiency').on("click contextmenu", this._onCycleSkillProficiency.bind(this));
|
html.find(".skill-proficiency").on("click contextmenu", this._onCycleSkillProficiency.bind(this));
|
||||||
|
|
||||||
// Trait Selector
|
// Trait Selector
|
||||||
html.find('.trait-selector').click(this._onTraitSelector.bind(this));
|
html.find(".trait-selector").click(this._onTraitSelector.bind(this));
|
||||||
|
|
||||||
// Configure Special Flags
|
// Configure Special Flags
|
||||||
html.find('.config-button').click(this._onConfigMenu.bind(this));
|
html.find(".config-button").click(this._onConfigMenu.bind(this));
|
||||||
|
|
||||||
// Owned Item management
|
// Owned Item management
|
||||||
html.find('.item-create').click(this._onItemCreate.bind(this));
|
html.find(".item-create").click(this._onItemCreate.bind(this));
|
||||||
html.find('.item-delete').click(this._onItemDelete.bind(this));
|
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(".item-uses input")
|
||||||
html.find('.slot-max-override').click(this._onPowerSlotOverride.bind(this));
|
.click((ev) => ev.target.select())
|
||||||
|
.change(this._onUsesChange.bind(this));
|
||||||
|
html.find(".slot-max-override").click(this._onPowerSlotOverride.bind(this));
|
||||||
|
|
||||||
// Active Effect management
|
// Active Effect management
|
||||||
html.find(".effect-control").click(ev => onManageActiveEffect(ev, this.actor));
|
html.find(".effect-control").click((ev) => onManageActiveEffect(ev, this.actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Owner Only Listeners
|
// Owner Only Listeners
|
||||||
if (this.actor.isOwner) {
|
if (this.actor.isOwner) {
|
||||||
|
|
||||||
// Ability Checks
|
// Ability Checks
|
||||||
html.find('.ability-name').click(this._onRollAbilityTest.bind(this));
|
html.find(".ability-name").click(this._onRollAbilityTest.bind(this));
|
||||||
|
|
||||||
|
|
||||||
// Roll Skill Checks
|
// Roll Skill Checks
|
||||||
html.find('.skill-name').click(this._onRollSkillCheck.bind(this));
|
html.find(".skill-name").click(this._onRollSkillCheck.bind(this));
|
||||||
|
|
||||||
// Item Rolling
|
// Item Rolling
|
||||||
html.find('.item .item-image').click(event => this._onItemRoll(event));
|
html.find(".item .item-image").click((event) => this._onItemRoll(event));
|
||||||
html.find('.item .item-recharge').click(event => this._onItemRecharge(event));
|
html.find(".item .item-recharge").click((event) => this._onItemRecharge(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise remove rollable classes
|
// Otherwise remove rollable classes
|
||||||
|
@ -554,9 +554,9 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Toggle next level - forward on click, backwards on right
|
// Toggle next level - forward on click, backwards on right
|
||||||
if (event.type === "click") {
|
if (event.type === "click") {
|
||||||
field.val(levels[(idx === levels.length - 1) ? 0 : idx + 1]);
|
field.val(levels[idx === levels.length - 1 ? 0 : idx + 1]);
|
||||||
} else if (event.type === "contextmenu") {
|
} else if (event.type === "contextmenu") {
|
||||||
field.val(levels[(idx === 0) ? levels.length - 1 : idx - 1]);
|
field.val(levels[idx === 0 ? levels.length - 1 : idx - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the field value and save the form
|
// Update the field value and save the form
|
||||||
|
@ -567,13 +567,13 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropActor(event, data) {
|
async _onDropActor(event, data) {
|
||||||
const canPolymorph = game.user.isGM || (this.actor.isOwner && game.settings.get('sw5e', 'allowPolymorphing'));
|
const canPolymorph = game.user.isGM || (this.actor.isOwner && game.settings.get("sw5e", "allowPolymorphing"));
|
||||||
if (!canPolymorph) return false;
|
if (!canPolymorph) return false;
|
||||||
|
|
||||||
// Get the target actor
|
// Get the target actor
|
||||||
let sourceActor = null;
|
let sourceActor = null;
|
||||||
if (data.pack) {
|
if (data.pack) {
|
||||||
const pack = game.packs.find(p => p.collection === data.pack);
|
const pack = game.packs.find((p) => p.collection === data.pack);
|
||||||
sourceActor = await pack.getEntity(data.id);
|
sourceActor = await pack.getEntity(data.id);
|
||||||
} else {
|
} else {
|
||||||
sourceActor = game.actors.get(data.id);
|
sourceActor = game.actors.get(data.id);
|
||||||
|
@ -581,35 +581,37 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
if (!sourceActor) return;
|
if (!sourceActor) return;
|
||||||
|
|
||||||
// Define a function to record polymorph settings for future use
|
// Define a function to record polymorph settings for future use
|
||||||
const rememberOptions = html => {
|
const rememberOptions = (html) => {
|
||||||
const options = {};
|
const options = {};
|
||||||
html.find('input').each((i, el) => {
|
html.find("input").each((i, el) => {
|
||||||
options[el.name] = el.checked;
|
options[el.name] = el.checked;
|
||||||
});
|
});
|
||||||
const settings = mergeObject(game.settings.get('sw5e', 'polymorphSettings') || {}, options);
|
const settings = mergeObject(game.settings.get("sw5e", "polymorphSettings") || {}, options);
|
||||||
game.settings.set('sw5e', 'polymorphSettings', settings);
|
game.settings.set("sw5e", "polymorphSettings", settings);
|
||||||
return settings;
|
return settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create and render the Dialog
|
// Create and render the Dialog
|
||||||
return new Dialog({
|
return new Dialog(
|
||||||
title: game.i18n.localize('SW5E.PolymorphPromptTitle'),
|
{
|
||||||
|
title: game.i18n.localize("SW5E.PolymorphPromptTitle"),
|
||||||
content: {
|
content: {
|
||||||
options: game.settings.get('sw5e', 'polymorphSettings'),
|
options: game.settings.get("sw5e", "polymorphSettings"),
|
||||||
i18n: SW5E.polymorphSettings,
|
i18n: SW5E.polymorphSettings,
|
||||||
isToken: this.actor.isToken
|
isToken: this.actor.isToken
|
||||||
},
|
},
|
||||||
default: 'accept',
|
default: "accept",
|
||||||
buttons: {
|
buttons: {
|
||||||
accept: {
|
accept: {
|
||||||
icon: '<i class="fas fa-check"></i>',
|
icon: '<i class="fas fa-check"></i>',
|
||||||
label: game.i18n.localize('SW5E.PolymorphAcceptSettings'),
|
label: game.i18n.localize("SW5E.PolymorphAcceptSettings"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, rememberOptions(html))
|
callback: (html) => this.actor.transformInto(sourceActor, rememberOptions(html))
|
||||||
},
|
},
|
||||||
wildshape: {
|
wildshape: {
|
||||||
icon: '<i class="fas fa-paw"></i>',
|
icon: '<i class="fas fa-paw"></i>',
|
||||||
label: game.i18n.localize('SW5E.PolymorphWildShape'),
|
label: game.i18n.localize("SW5E.PolymorphWildShape"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, {
|
callback: (html) =>
|
||||||
|
this.actor.transformInto(sourceActor, {
|
||||||
keepBio: true,
|
keepBio: true,
|
||||||
keepClass: true,
|
keepClass: true,
|
||||||
keepMental: true,
|
keepMental: true,
|
||||||
|
@ -620,46 +622,50 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
},
|
},
|
||||||
polymorph: {
|
polymorph: {
|
||||||
icon: '<i class="fas fa-pastafarianism"></i>',
|
icon: '<i class="fas fa-pastafarianism"></i>',
|
||||||
label: game.i18n.localize('SW5E.Polymorph'),
|
label: game.i18n.localize("SW5E.Polymorph"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, {
|
callback: (html) =>
|
||||||
|
this.actor.transformInto(sourceActor, {
|
||||||
transformTokens: rememberOptions(html).transformTokens
|
transformTokens: rememberOptions(html).transformTokens
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
icon: '<i class="fas fa-times"></i>',
|
icon: '<i class="fas fa-times"></i>',
|
||||||
label: game.i18n.localize('Cancel')
|
label: game.i18n.localize("Cancel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
classes: ['dialog', 'sw5e'],
|
{
|
||||||
|
classes: ["dialog", "sw5e"],
|
||||||
width: 600,
|
width: 600,
|
||||||
template: 'systems/sw5e/templates/apps/polymorph-prompt.html'
|
template: "systems/sw5e/templates/apps/polymorph-prompt.html"
|
||||||
}).render(true);
|
}
|
||||||
|
).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
|
|
||||||
// Check to make sure items of this type are allowed on this actor
|
// Check to make sure items of this type are allowed on this actor
|
||||||
if (this.constructor.unsupportedItemTypes.has(itemData.type)) {
|
if (this.constructor.unsupportedItemTypes.has(itemData.type)) {
|
||||||
return ui.notifications.warn(game.i18n.format("SW5E.ActorWarningInvalidItem", {
|
return ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.ActorWarningInvalidItem", {
|
||||||
itemType: game.i18n.localize(CONFIG.Item.typeLabels[itemData.type]),
|
itemType: game.i18n.localize(CONFIG.Item.typeLabels[itemData.type]),
|
||||||
actorType: game.i18n.localize(CONFIG.Actor.typeLabels[this.actor.type])
|
actorType: game.i18n.localize(CONFIG.Actor.typeLabels[this.actor.type])
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a Consumable power scroll on the Inventory tab
|
// Create a Consumable power scroll on the Inventory tab
|
||||||
// TODO: This is pretty non functional as the base items for the scrolls, and the powers, are not defined, maybe consider using holocrons
|
// TODO: This is pretty non functional as the base items for the scrolls, and the powers, are not defined, maybe consider using holocrons
|
||||||
if ( (itemData.type === "power") && (this._tabs[0].active === "inventory") ) {
|
if (itemData.type === "power" && this._tabs[0].active === "inventory") {
|
||||||
const scroll = await Item5e.createScrollFromPower(itemData);
|
const scroll = await Item5e.createScrollFromPower(itemData);
|
||||||
itemData = scroll.data;
|
itemData = scroll.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemData.data) {
|
if (itemData.data) {
|
||||||
// Ignore certain statuses
|
// Ignore certain statuses
|
||||||
["equipped", "proficient", "prepared"].forEach(k => delete itemData.data[k]);
|
["equipped", "proficient", "prepared"].forEach((k) => delete itemData.data[k]);
|
||||||
|
|
||||||
// Downgrade ATTUNED to REQUIRED
|
// Downgrade ATTUNED to REQUIRED
|
||||||
itemData.data.attunement = Math.min(itemData.data.attunement, CONFIG.SW5E.attunementTypes.REQUIRED);
|
itemData.data.attunement = Math.min(itemData.data.attunement, CONFIG.SW5E.attunementTypes.REQUIRED);
|
||||||
|
@ -667,14 +673,13 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Stack identical consumables
|
// Stack identical consumables
|
||||||
if (itemData.type === "consumable" && itemData.flags.core?.sourceId) {
|
if (itemData.type === "consumable" && itemData.flags.core?.sourceId) {
|
||||||
const similarItem = this.actor.items.find(i => {
|
const similarItem = this.actor.items.find((i) => {
|
||||||
const sourceId = i.getFlag("core", "sourceId");
|
const sourceId = i.getFlag("core", "sourceId");
|
||||||
return sourceId && (sourceId === itemData.flags.core?.sourceId) &&
|
return sourceId && sourceId === itemData.flags.core?.sourceId && i.type === "consumable";
|
||||||
(i.type === "consumable");
|
|
||||||
});
|
});
|
||||||
if (similarItem) {
|
if (similarItem) {
|
||||||
return similarItem.update({
|
return similarItem.update({
|
||||||
'data.quantity': similarItem.data.data.quantity + Math.max(itemData.data.quantity, 1)
|
"data.quantity": similarItem.data.data.quantity + Math.max(itemData.data.quantity, 1)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -720,7 +725,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const item = this.actor.items.get(itemId);
|
const item = this.actor.items.get(itemId);
|
||||||
const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max);
|
const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max);
|
||||||
event.target.value = uses;
|
event.target.value = uses;
|
||||||
return item.update({ 'data.uses.value': uses });
|
return item.update({"data.uses.value": uses});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -748,7 +753,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemId);
|
const item = this.actor.items.get(itemId);
|
||||||
return item.rollRecharge();
|
return item.rollRecharge();
|
||||||
};
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
@ -769,7 +774,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
} else {
|
} else {
|
||||||
let div = $(`<div class="item-summary">${chatData.description.value}</div>`);
|
let div = $(`<div class="item-summary">${chatData.description.value}</div>`);
|
||||||
let props = $(`<div class="item-properties"></div>`);
|
let props = $(`<div class="item-properties"></div>`);
|
||||||
chatData.properties.forEach(p => props.append(`<span class="tag">${p}</span>`));
|
chatData.properties.forEach((p) => props.append(`<span class="tag">${p}</span>`));
|
||||||
div.append(props);
|
div.append(props);
|
||||||
li.append(div.hide());
|
li.append(div.hide());
|
||||||
div.slideDown(200);
|
div.slideDown(200);
|
||||||
|
@ -894,7 +899,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const label = a.parentElement.querySelector("label");
|
const label = a.parentElement.querySelector("label");
|
||||||
const choices = CONFIG.SW5E[a.dataset.options];
|
const choices = CONFIG.SW5E[a.dataset.options];
|
||||||
const options = {name: a.dataset.target, title: label.innerText, choices};
|
const options = {name: a.dataset.target, title: label.innerText, choices};
|
||||||
return new TraitSelector(this.actor, options).render(true)
|
return new TraitSelector(this.actor, options).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -904,7 +909,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
let buttons = super._getHeaderButtons();
|
let buttons = super._getHeaderButtons();
|
||||||
if (this.actor.isPolymorphed) {
|
if (this.actor.isPolymorphed) {
|
||||||
buttons.unshift({
|
buttons.unshift({
|
||||||
label: 'SW5E.PolymorphRestoreTransformation',
|
label: "SW5E.PolymorphRestoreTransformation",
|
||||||
class: "restore-transformation",
|
class: "restore-transformation",
|
||||||
icon: "fas fa-backward",
|
icon: "fas fa-backward",
|
||||||
onclick: () => this.actor.revertOriginalForm()
|
onclick: () => this.actor.revertOriginalForm()
|
||||||
|
|
|
@ -7,7 +7,6 @@ import Actor5e from "../../entity.js";
|
||||||
* @type {ActorSheet5e}
|
* @type {ActorSheet5e}
|
||||||
*/
|
*/
|
||||||
export default class ActorSheet5eCharacter extends ActorSheet5e {
|
export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define default rendering options for the NPC sheet
|
* Define default rendering options for the NPC sheet
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
|
@ -45,10 +44,12 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
|
|
||||||
// Experience Tracking
|
// Experience Tracking
|
||||||
sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking");
|
sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking");
|
||||||
sheetData["classLabels"] = this.actor.itemTypes.class.map(c => c.name).join(", ");
|
sheetData["classLabels"] = this.actor.itemTypes.class.map((c) => c.name).join(", ");
|
||||||
sheetData["multiclassLabels"] = this.actor.itemTypes.class.map(c => {
|
sheetData["multiclassLabels"] = this.actor.itemTypes.class
|
||||||
return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(' ')
|
.map((c) => {
|
||||||
}).join(', ');
|
return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(" ");
|
||||||
|
})
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
// Return data for rendering
|
// Return data for rendering
|
||||||
return sheetData;
|
return sheetData;
|
||||||
|
@ -61,7 +62,6 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
|
|
||||||
// Categorize items as inventory, powerbook, features, and classes
|
// Categorize items as inventory, powerbook, features, and classes
|
||||||
const inventory = {
|
const inventory = {
|
||||||
weapon: {label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"}},
|
weapon: {label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"}},
|
||||||
|
@ -73,11 +73,23 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Partition items by category
|
// Partition items by category
|
||||||
let [items, powers, feats, classes, species, archetypes, classfeatures, backgrounds, fightingstyles, fightingmasteries, lightsaberforms] = data.items.reduce((arr, item) => {
|
let [
|
||||||
|
items,
|
||||||
|
powers,
|
||||||
|
feats,
|
||||||
|
classes,
|
||||||
|
species,
|
||||||
|
archetypes,
|
||||||
|
classfeatures,
|
||||||
|
backgrounds,
|
||||||
|
fightingstyles,
|
||||||
|
fightingmasteries,
|
||||||
|
lightsaberforms
|
||||||
|
] = data.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
// Item details
|
// Item details
|
||||||
item.img = item.img || CONST.DEFAULT_TOKEN;
|
item.img = item.img || CONST.DEFAULT_TOKEN;
|
||||||
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1;
|
||||||
item.attunement = {
|
item.attunement = {
|
||||||
[CONFIG.SW5E.attunementTypes.REQUIRED]: {
|
[CONFIG.SW5E.attunementTypes.REQUIRED]: {
|
||||||
icon: "fa-sun",
|
icon: "fa-sun",
|
||||||
|
@ -92,16 +104,18 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
}[item.data.attunement];
|
}[item.data.attunement];
|
||||||
|
|
||||||
// Item usage
|
// Item usage
|
||||||
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
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.isOnCooldown =
|
||||||
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
item.data.recharge && !!item.data.recharge.value && item.data.recharge.charged === false;
|
||||||
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
item.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0;
|
||||||
|
item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type);
|
||||||
|
|
||||||
// Item toggle state
|
// Item toggle state
|
||||||
this._prepareItemToggleState(item);
|
this._prepareItemToggleState(item);
|
||||||
|
|
||||||
// Primary Class
|
// Primary Class
|
||||||
if ( item.type === "class" ) item.isOriginalClass = ( item._id === this.actor.data.data.details.originalClass );
|
if (item.type === "class")
|
||||||
|
item.isOriginalClass = item._id === this.actor.data.data.details.originalClass;
|
||||||
|
|
||||||
// Classify items into types
|
// Classify items into types
|
||||||
if (item.type === "power") arr[1].push(item);
|
if (item.type === "power") arr[1].push(item);
|
||||||
|
@ -116,7 +130,9 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
else if (item.type === "lightsaberform") arr[10].push(item);
|
else if (item.type === "lightsaberform") arr[10].push(item);
|
||||||
else if (Object.keys(inventory).includes(item.type)) arr[0].push(item);
|
else if (Object.keys(inventory).includes(item.type)) arr[0].push(item);
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], [], [], [], [], [], [], [], [], [], []]);
|
},
|
||||||
|
[[], [], [], [], [], [], [], [], [], [], []]
|
||||||
|
);
|
||||||
|
|
||||||
// Apply active item filters
|
// Apply active item filters
|
||||||
items = this._filterItems(items, this._filters.inventory);
|
items = this._filterItems(items, this._filters.inventory);
|
||||||
|
@ -133,21 +149,74 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
|
|
||||||
// Organize Powerbook and count the number of prepared powers (excluding always, at will, etc...)
|
// Organize Powerbook and count the number of prepared powers (excluding always, at will, etc...)
|
||||||
const powerbook = this._preparePowerbook(data, powers);
|
const powerbook = this._preparePowerbook(data, powers);
|
||||||
const nPrepared = powers.filter(s => {
|
const nPrepared = powers.filter((s) => {
|
||||||
return (s.data.level > 0) && (s.data.preparation.mode === "prepared") && s.data.preparation.prepared;
|
return s.data.level > 0 && s.data.preparation.mode === "prepared" && s.data.preparation.prepared;
|
||||||
}).length;
|
}).length;
|
||||||
|
|
||||||
// Organize Features
|
// Organize Features
|
||||||
const features = {
|
const features = {
|
||||||
classes: { label: "SW5E.ItemTypeClassPl", items: [], hasActions: false, dataset: {type: "class"}, isClass: true },
|
classes: {
|
||||||
classfeatures: { label: "SW5E.ItemTypeClassFeats", items: [], hasActions: true, dataset: {type: "classfeature"}, isClassfeature: true },
|
label: "SW5E.ItemTypeClassPl",
|
||||||
archetype: { label: "SW5E.ItemTypeArchetype", items: [], hasActions: false, dataset: {type: "archetype"}, isArchetype: true },
|
items: [],
|
||||||
species: { label: "SW5E.ItemTypeSpecies", items: [], hasActions: false, dataset: {type: "species"}, isSpecies: true },
|
hasActions: false,
|
||||||
background: { label: "SW5E.ItemTypeBackground", items: [], hasActions: false, dataset: {type: "background"}, isBackground: true },
|
dataset: {type: "class"},
|
||||||
fightingstyles: { label: "SW5E.ItemTypeFightingStylePl", items: [], hasActions: false, dataset: {type: "fightingstyle"}, isFightingstyle: true },
|
isClass: true
|
||||||
fightingmasteries: { label: "SW5E.ItemTypeFightingMasteryPl", items: [], hasActions: false, dataset: {type: "fightingmastery"}, isFightingmastery: true },
|
},
|
||||||
lightsaberforms: { label: "SW5E.ItemTypeLightsaberFormPl", items: [], hasActions: false, dataset: {type: "lightsaberform"}, isLightsaberform: true },
|
classfeatures: {
|
||||||
active: { label: "SW5E.FeatureActive", items: [], hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
|
label: "SW5E.ItemTypeClassFeats",
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {type: "classfeature"},
|
||||||
|
isClassfeature: true
|
||||||
|
},
|
||||||
|
archetype: {
|
||||||
|
label: "SW5E.ItemTypeArchetype",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "archetype"},
|
||||||
|
isArchetype: true
|
||||||
|
},
|
||||||
|
species: {
|
||||||
|
label: "SW5E.ItemTypeSpecies",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "species"},
|
||||||
|
isSpecies: true
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
label: "SW5E.ItemTypeBackground",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "background"},
|
||||||
|
isBackground: true
|
||||||
|
},
|
||||||
|
fightingstyles: {
|
||||||
|
label: "SW5E.ItemTypeFightingStylePl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "fightingstyle"},
|
||||||
|
isFightingstyle: true
|
||||||
|
},
|
||||||
|
fightingmasteries: {
|
||||||
|
label: "SW5E.ItemTypeFightingMasteryPl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "fightingmastery"},
|
||||||
|
isFightingmastery: true
|
||||||
|
},
|
||||||
|
lightsaberforms: {
|
||||||
|
label: "SW5E.ItemTypeLightsaberFormPl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "lightsaberform"},
|
||||||
|
isLightsaberform: true
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
label: "SW5E.FeatureActive",
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {"type": "feat", "activation.type": "action"}
|
||||||
|
},
|
||||||
passive: {label: "SW5E.FeaturePassive", items: [], hasActions: false, dataset: {type: "feat"}}
|
passive: {label: "SW5E.FeaturePassive", items: [], hasActions: false, dataset: {type: "feat"}}
|
||||||
};
|
};
|
||||||
for (let f of feats) {
|
for (let f of feats) {
|
||||||
|
@ -187,8 +256,7 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
if (isAlways) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always;
|
if (isAlways) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always;
|
||||||
else if (isPrepared) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared;
|
else if (isPrepared) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared;
|
||||||
else item.toggleTitle = game.i18n.localize("SW5E.PowerUnprepared");
|
else item.toggleTitle = game.i18n.localize("SW5E.PowerUnprepared");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const isActive = getProperty(item.data, "equipped");
|
const isActive = getProperty(item.data, "equipped");
|
||||||
item.toggleClass = isActive ? "active" : "";
|
item.toggleClass = isActive ? "active" : "";
|
||||||
item.toggleTitle = game.i18n.localize(isActive ? "SW5E.Equipped" : "SW5E.Unequipped");
|
item.toggleTitle = game.i18n.localize(isActive ? "SW5E.Equipped" : "SW5E.Unequipped");
|
||||||
|
@ -208,11 +276,11 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
if (!this.isEditable) return;
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
// Item State Toggling
|
// Item State Toggling
|
||||||
html.find('.item-toggle').click(this._onToggleItem.bind(this));
|
html.find(".item-toggle").click(this._onToggleItem.bind(this));
|
||||||
|
|
||||||
// Short and Long Rest
|
// Short and Long Rest
|
||||||
html.find('.short-rest').click(this._onShortRest.bind(this));
|
html.find(".short-rest").click(this._onShortRest.bind(this));
|
||||||
html.find('.long-rest').click(this._onLongRest.bind(this));
|
html.find(".long-rest").click(this._onLongRest.bind(this));
|
||||||
|
|
||||||
// Rollable sheet actions
|
// Rollable sheet actions
|
||||||
html.find(".rollable[data-action]").click(this._onSheetAction.bind(this));
|
html.find(".rollable[data-action]").click(this._onSheetAction.bind(this));
|
||||||
|
@ -281,10 +349,9 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
|
|
||||||
// Increment the number of class levels a character instead of creating a new item
|
// Increment the number of class levels a character instead of creating a new item
|
||||||
if (itemData.type === "class") {
|
if (itemData.type === "class") {
|
||||||
const cls = this.actor.itemTypes.class.find(c => c.name === itemData.name);
|
const cls = this.actor.itemTypes.class.find((c) => c.name === itemData.name);
|
||||||
let priorLevel = cls?.data.data.levels ?? 0;
|
let priorLevel = cls?.data.data.levels ?? 0;
|
||||||
if (!!cls) {
|
if (!!cls) {
|
||||||
const next = Math.min(priorLevel + 1, 20 + priorLevel - this.actor.data.data.details.level);
|
const next = Math.min(priorLevel + 1, 20 + priorLevel - this.actor.data.data.details.level);
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ActorSheet5e from "./base.js";
|
||||||
* @extends {ActorSheet5e}
|
* @extends {ActorSheet5e}
|
||||||
*/
|
*/
|
||||||
export default class ActorSheet5eNPC extends ActorSheet5e {
|
export default class ActorSheet5eNPC extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return mergeObject(super.defaultOptions, {
|
return mergeObject(super.defaultOptions, {
|
||||||
|
@ -28,27 +27,40 @@ export default class ActorSheet5eNPC extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
|
|
||||||
// Categorize Items as Features and Powers
|
// Categorize Items as Features and Powers
|
||||||
const features = {
|
const features = {
|
||||||
weapons: { label: game.i18n.localize("SW5E.AttackPl"), items: [] , hasActions: true, dataset: {type: "weapon", "weapon-type": "natural"} },
|
weapons: {
|
||||||
actions: { label: game.i18n.localize("SW5E.ActionPl"), items: [] , hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
|
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"}},
|
passive: {label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"}},
|
||||||
equipment: {label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}}
|
equipment: {label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start by classifying items into groups for rendering
|
// Start by classifying items into groups for rendering
|
||||||
let [powers, other] = data.items.reduce((arr, item) => {
|
let [powers, other] = data.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
item.img = item.img || CONST.DEFAULT_TOKEN;
|
item.img = item.img || CONST.DEFAULT_TOKEN;
|
||||||
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1;
|
||||||
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
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.isOnCooldown =
|
||||||
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
item.data.recharge && !!item.data.recharge.value && item.data.recharge.charged === false;
|
||||||
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
item.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0;
|
||||||
|
item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type);
|
||||||
if (item.type === "power") arr[0].push(item);
|
if (item.type === "power") arr[0].push(item);
|
||||||
else arr[1].push(item);
|
else arr[1].push(item);
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], []]);
|
},
|
||||||
|
[[], []]
|
||||||
|
);
|
||||||
|
|
||||||
// Apply item filters
|
// Apply item filters
|
||||||
powers = this._filterItems(powers, this._filters.powerbook);
|
powers = this._filterItems(powers, this._filters.powerbook);
|
||||||
|
@ -63,8 +75,7 @@ export default class ActorSheet5eNPC extends ActorSheet5e {
|
||||||
else if (item.type === "feat") {
|
else if (item.type === "feat") {
|
||||||
if (item.data.activation.type) features.actions.items.push(item);
|
if (item.data.activation.type) features.actions.items.push(item);
|
||||||
else features.passive.items.push(item);
|
else features.passive.items.push(item);
|
||||||
}
|
} else features.equipment.items.push(item);
|
||||||
else features.equipment.items.push(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign and return
|
// Assign and return
|
||||||
|
@ -72,7 +83,6 @@ export default class ActorSheet5eNPC extends ActorSheet5e {
|
||||||
data.powerbook = powerbook;
|
data.powerbook = powerbook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
|
@ -95,7 +105,6 @@ export default class ActorSheet5eNPC extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
|
|
||||||
// Format NPC Challenge Rating
|
// Format NPC Challenge Rating
|
||||||
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
||||||
let crv = "data.details.cr";
|
let crv = "data.details.cr";
|
||||||
|
|
|
@ -25,13 +25,12 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new cargo entry for a vehicle Actor.
|
* Creates a new cargo entry for a vehicle Actor.
|
||||||
*/
|
*/
|
||||||
static get newCargo() {
|
static get newCargo() {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: "",
|
||||||
quantity: 1
|
quantity: 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -46,7 +45,6 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_computeEncumbrance(totalWeight, actorData) {
|
_computeEncumbrance(totalWeight, actorData) {
|
||||||
|
|
||||||
// Compute currency weight
|
// Compute currency weight
|
||||||
const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0);
|
const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0);
|
||||||
totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
||||||
|
@ -75,25 +73,24 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareCrewedItem(item) {
|
_prepareCrewedItem(item) {
|
||||||
|
|
||||||
// Determine crewed status
|
// Determine crewed status
|
||||||
const isCrewed = item.data.crewed;
|
const isCrewed = item.data.crewed;
|
||||||
item.toggleClass = isCrewed ? 'active' : '';
|
item.toggleClass = isCrewed ? "active" : "";
|
||||||
item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? 'Crewed' : 'Uncrewed'}`);
|
item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? "Crewed" : "Uncrewed"}`);
|
||||||
|
|
||||||
// Handle crew actions
|
// Handle crew actions
|
||||||
if (item.type === 'feat' && item.data.activation.type === 'crew') {
|
if (item.type === "feat" && item.data.activation.type === "crew") {
|
||||||
item.crew = item.data.activation.cost;
|
item.crew = item.data.activation.cost;
|
||||||
item.cover = game.i18n.localize(`SW5E.${item.data.cover ? 'CoverTotal' : 'None'}`);
|
item.cover = game.i18n.localize(`SW5E.${item.data.cover ? "CoverTotal" : "None"}`);
|
||||||
if (item.data.cover === .5) item.cover = '½';
|
if (item.data.cover === 0.5) item.cover = "½";
|
||||||
else if (item.data.cover === .75) item.cover = '¾';
|
else if (item.data.cover === 0.75) item.cover = "¾";
|
||||||
else if (item.data.cover === null) item.cover = '—';
|
else if (item.data.cover === null) item.cover = "—";
|
||||||
if (item.crew < 1 || item.crew === null) item.crew = '—';
|
if (item.crew < 1 || item.crew === null) item.crew = "—";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare vehicle weapons
|
// Prepare vehicle weapons
|
||||||
if (item.type === 'equipment' || item.type === 'weapon') {
|
if (item.type === "equipment" || item.type === "weapon") {
|
||||||
item.threshold = item.data.hp.dt ? item.data.hp.dt : '—';
|
item.threshold = item.data.hp.dt ? item.data.hp.dt : "—";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,111 +101,125 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
const cargoColumns = [{
|
const cargoColumns = [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'quantity',
|
css: "item-qty",
|
||||||
editable: 'Number'
|
property: "quantity",
|
||||||
}];
|
editable: "Number"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const equipmentColumns = [{
|
const equipmentColumns = [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'data.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.AC"),
|
||||||
}, {
|
css: "item-ac",
|
||||||
label: game.i18n.localize('SW5E.HP'),
|
property: "data.armor.value"
|
||||||
css: 'item-hp',
|
},
|
||||||
property: 'data.hp.value',
|
{
|
||||||
editable: 'Number'
|
label: game.i18n.localize("SW5E.HP"),
|
||||||
}, {
|
css: "item-hp",
|
||||||
label: game.i18n.localize('SW5E.Threshold'),
|
property: "data.hp.value",
|
||||||
css: 'item-threshold',
|
editable: "Number"
|
||||||
property: 'threshold'
|
},
|
||||||
}];
|
{
|
||||||
|
label: game.i18n.localize("SW5E.Threshold"),
|
||||||
|
css: "item-threshold",
|
||||||
|
property: "threshold"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const features = {
|
const features = {
|
||||||
actions: {
|
actions: {
|
||||||
label: game.i18n.localize('SW5E.ActionPl'),
|
label: game.i18n.localize("SW5E.ActionPl"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'feat', 'activation.type': 'crew'},
|
dataset: {"type": "feat", "activation.type": "crew"},
|
||||||
columns: [{
|
columns: [
|
||||||
label: game.i18n.localize('SW5E.VehicleCrew'),
|
{
|
||||||
css: 'item-crew',
|
label: game.i18n.localize("SW5E.VehicleCrew"),
|
||||||
property: 'crew'
|
css: "item-crew",
|
||||||
}, {
|
property: "crew"
|
||||||
label: game.i18n.localize('SW5E.Cover'),
|
},
|
||||||
css: 'item-cover',
|
{
|
||||||
property: 'cover'
|
label: game.i18n.localize("SW5E.Cover"),
|
||||||
}]
|
css: "item-cover",
|
||||||
|
property: "cover"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
equipment: {
|
equipment: {
|
||||||
label: game.i18n.localize('SW5E.ItemTypeEquipment'),
|
label: game.i18n.localize("SW5E.ItemTypeEquipment"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'equipment', 'armor.type': 'vehicle'},
|
dataset: {"type": "equipment", "armor.type": "vehicle"},
|
||||||
columns: equipmentColumns
|
columns: equipmentColumns
|
||||||
},
|
},
|
||||||
passive: {
|
passive: {
|
||||||
label: game.i18n.localize('SW5E.Features'),
|
label: game.i18n.localize("SW5E.Features"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'feat'}
|
dataset: {type: "feat"}
|
||||||
},
|
},
|
||||||
reactions: {
|
reactions: {
|
||||||
label: game.i18n.localize('SW5E.ReactionPl'),
|
label: game.i18n.localize("SW5E.ReactionPl"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'feat', 'activation.type': 'reaction'}
|
dataset: {"type": "feat", "activation.type": "reaction"}
|
||||||
},
|
},
|
||||||
weapons: {
|
weapons: {
|
||||||
label: game.i18n.localize('SW5E.ItemTypeWeaponPl'),
|
label: game.i18n.localize("SW5E.ItemTypeWeaponPl"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'weapon', 'weapon-type': 'siege'},
|
dataset: {"type": "weapon", "weapon-type": "siege"},
|
||||||
columns: equipmentColumns
|
columns: equipmentColumns
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cargo = {
|
const cargo = {
|
||||||
crew: {
|
crew: {
|
||||||
label: game.i18n.localize('SW5E.VehicleCrew'),
|
label: game.i18n.localize("SW5E.VehicleCrew"),
|
||||||
items: data.data.cargo.crew,
|
items: data.data.cargo.crew,
|
||||||
css: 'cargo-row crew',
|
css: "cargo-row crew",
|
||||||
editableName: true,
|
editableName: true,
|
||||||
dataset: {type: 'crew'},
|
dataset: {type: "crew"},
|
||||||
columns: cargoColumns
|
columns: cargoColumns
|
||||||
},
|
},
|
||||||
passengers: {
|
passengers: {
|
||||||
label: game.i18n.localize('SW5E.VehiclePassengers'),
|
label: game.i18n.localize("SW5E.VehiclePassengers"),
|
||||||
items: data.data.cargo.passengers,
|
items: data.data.cargo.passengers,
|
||||||
css: 'cargo-row passengers',
|
css: "cargo-row passengers",
|
||||||
editableName: true,
|
editableName: true,
|
||||||
dataset: {type: 'passengers'},
|
dataset: {type: "passengers"},
|
||||||
columns: cargoColumns
|
columns: cargoColumns
|
||||||
},
|
},
|
||||||
cargo: {
|
cargo: {
|
||||||
label: game.i18n.localize('SW5E.VehicleCargo'),
|
label: game.i18n.localize("SW5E.VehicleCargo"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'loot'},
|
dataset: {type: "loot"},
|
||||||
columns: [{
|
columns: [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'data.quantity',
|
css: "item-qty",
|
||||||
editable: 'Number'
|
property: "data.quantity",
|
||||||
}, {
|
editable: "Number"
|
||||||
label: game.i18n.localize('SW5E.Price'),
|
},
|
||||||
css: 'item-price',
|
{
|
||||||
property: 'data.price',
|
label: game.i18n.localize("SW5E.Price"),
|
||||||
editable: 'Number'
|
css: "item-price",
|
||||||
}, {
|
property: "data.price",
|
||||||
label: game.i18n.localize('SW5E.Weight'),
|
editable: "Number"
|
||||||
css: 'item-weight',
|
},
|
||||||
property: 'data.weight',
|
{
|
||||||
editable: 'Number'
|
label: game.i18n.localize("SW5E.Weight"),
|
||||||
}]
|
css: "item-weight",
|
||||||
|
property: "data.weight",
|
||||||
|
editable: "Number"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -234,8 +245,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
features.equipment.items.push(item);
|
features.equipment.items.push(item);
|
||||||
break;
|
break;
|
||||||
case "feat":
|
case "feat":
|
||||||
if (!item.data.activation.type || (item.data.activation.type === "none")) features.passive.items.push(item);
|
if (!item.data.activation.type || item.data.activation.type === "none")
|
||||||
else if (item.data.activation.type === 'reaction') features.reactions.items.push(item);
|
features.passive.items.push(item);
|
||||||
|
else if (item.data.activation.type === "reaction") features.reactions.items.push(item);
|
||||||
else features.actions.items.push(item);
|
else features.actions.items.push(item);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -259,21 +271,21 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
if (!this.isEditable) return;
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
html.find('.item-toggle').click(this._onToggleItem.bind(this));
|
html.find(".item-toggle").click(this._onToggleItem.bind(this));
|
||||||
html.find('.item-hp input')
|
html.find(".item-hp input")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onHPChange.bind(this));
|
.change(this._onHPChange.bind(this));
|
||||||
|
|
||||||
html.find('.item:not(.cargo-row) input[data-property]')
|
html.find(".item:not(.cargo-row) input[data-property]")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onEditInSheet.bind(this));
|
.change(this._onEditInSheet.bind(this));
|
||||||
|
|
||||||
html.find('.cargo-row input')
|
html.find(".cargo-row input")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onCargoRowChange.bind(this));
|
.change(this._onCargoRowChange.bind(this));
|
||||||
|
|
||||||
if (this.actor.data.data.attributes.actions.stations) {
|
if (this.actor.data.data.attributes.actions.stations) {
|
||||||
html.find('.counter.actions, .counter.action-thresholds').hide();
|
html.find(".counter.actions, .counter.action-thresholds").hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,9 +300,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
_onCargoRowChange(event) {
|
_onCargoRowChange(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const target = event.currentTarget;
|
const target = event.currentTarget;
|
||||||
const row = target.closest('.item');
|
const row = target.closest(".item");
|
||||||
const idx = Number(row.dataset.itemId);
|
const idx = Number(row.dataset.itemId);
|
||||||
const property = row.classList.contains('crew') ? 'crew' : 'passengers';
|
const property = row.classList.contains("crew") ? "crew" : "passengers";
|
||||||
|
|
||||||
// Get the cargo entry
|
// Get the cargo entry
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[property]);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[property]);
|
||||||
|
@ -298,10 +310,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
if (!entry) return null;
|
if (!entry) return null;
|
||||||
|
|
||||||
// Update the cargo value
|
// Update the cargo value
|
||||||
const key = target.dataset.property || 'name';
|
const key = target.dataset.property || "name";
|
||||||
const type = target.dataset.dtype;
|
const type = target.dataset.dtype;
|
||||||
let value = target.value;
|
let value = target.value;
|
||||||
if (type === 'Number') value = Number(value);
|
if (type === "Number") value = Number(value);
|
||||||
entry[key] = value;
|
entry[key] = value;
|
||||||
|
|
||||||
// Perform the Actor update
|
// Perform the Actor update
|
||||||
|
@ -318,14 +330,18 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onEditInSheet(event) {
|
_onEditInSheet(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const property = event.currentTarget.dataset.property;
|
const property = event.currentTarget.dataset.property;
|
||||||
const type = event.currentTarget.dataset.dtype;
|
const type = event.currentTarget.dataset.dtype;
|
||||||
let value = event.currentTarget.value;
|
let value = event.currentTarget.value;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'Number': value = parseInt(value); break;
|
case "Number":
|
||||||
case 'Boolean': value = value === 'true'; break;
|
value = parseInt(value);
|
||||||
|
break;
|
||||||
|
case "Boolean":
|
||||||
|
value = value === "true";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return item.update({[`${property}`]: value});
|
return item.update({[`${property}`]: value});
|
||||||
}
|
}
|
||||||
|
@ -342,7 +358,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const target = event.currentTarget;
|
const target = event.currentTarget;
|
||||||
const type = target.dataset.type;
|
const type = target.dataset.type;
|
||||||
if (type === 'crew' || type === 'passengers') {
|
if (type === "crew" || type === "passengers") {
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]);
|
||||||
cargo.push(this.constructor.newCargo);
|
cargo.push(this.constructor.newCargo);
|
||||||
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
||||||
|
@ -360,10 +376,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onItemDelete(event) {
|
_onItemDelete(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const row = event.currentTarget.closest('.item');
|
const row = event.currentTarget.closest(".item");
|
||||||
if (row.classList.contains('cargo-row')) {
|
if (row.classList.contains("cargo-row")) {
|
||||||
const idx = Number(row.dataset.itemId);
|
const idx = Number(row.dataset.itemId);
|
||||||
const type = row.classList.contains('crew') ? 'crew' : 'passengers';
|
const type = row.classList.contains("crew") ? "crew" : "passengers";
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx);
|
||||||
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
||||||
}
|
}
|
||||||
|
@ -376,7 +392,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
const cargoTypes = ["weapon", "equipment", "consumable", "tool", "loot", "backpack"];
|
const cargoTypes = ["weapon", "equipment", "consumable", "tool", "loot", "backpack"];
|
||||||
const isCargo = cargoTypes.includes(itemData.type) && (this._tabs[0].active === "cargo");
|
const isCargo = cargoTypes.includes(itemData.type) && this._tabs[0].active === "cargo";
|
||||||
foundry.utils.setProperty(itemData, "flags.sw5e.vehicleCargo", isCargo);
|
foundry.utils.setProperty(itemData, "flags.sw5e.vehicleCargo", isCargo);
|
||||||
return super._onDropItemCreate(itemData);
|
return super._onDropItemCreate(itemData);
|
||||||
}
|
}
|
||||||
|
@ -391,11 +407,11 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onHPChange(event) {
|
_onHPChange(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max);
|
const hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max);
|
||||||
event.currentTarget.value = hp;
|
event.currentTarget.value = hp;
|
||||||
return item.update({'data.hp.value': hp});
|
return item.update({"data.hp.value": hp});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -408,9 +424,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onToggleItem(event) {
|
_onToggleItem(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const crewed = !!item.data.data.crewed;
|
const crewed = !!item.data.data.crewed;
|
||||||
return item.update({'data.crewed': !crewed});
|
return item.update({"data.crewed": !crewed});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
|
@ -39,12 +39,15 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
// Prepare dialog form data
|
// Prepare dialog form data
|
||||||
const data = {
|
const data = {
|
||||||
item: item.data,
|
item: item.data,
|
||||||
title: game.i18n.format("SW5E.AbilityUseHint", {type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`), name: item.name}),
|
title: game.i18n.format("SW5E.AbilityUseHint", {
|
||||||
|
type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`),
|
||||||
|
name: item.name
|
||||||
|
}),
|
||||||
note: this._getAbilityUseNote(item.data, uses, recharge),
|
note: this._getAbilityUseNote(item.data, uses, recharge),
|
||||||
consumePowerSlot: false,
|
consumePowerSlot: false,
|
||||||
consumeRecharge: recharges,
|
consumeRecharge: recharges,
|
||||||
consumeResource: !!itemData.consume.target,
|
consumeResource: !!itemData.consume.target,
|
||||||
consumeUses: uses.per && (uses.max > 0),
|
consumeUses: uses.per && uses.max > 0,
|
||||||
canUse: recharges ? recharge.charged : sufficientUses,
|
canUse: recharges ? recharge.charged : sufficientUses,
|
||||||
createTemplate: game.user.can("TEMPLATE_CREATE") && item.hasAreaTarget,
|
createTemplate: game.user.can("TEMPLATE_CREATE") && item.hasAreaTarget,
|
||||||
errors: []
|
errors: []
|
||||||
|
@ -65,7 +68,7 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
use: {
|
use: {
|
||||||
icon: `<i class="fas ${icon}"></i>`,
|
icon: `<i class="fas ${icon}"></i>`,
|
||||||
label: label,
|
label: label,
|
||||||
callback: html => {
|
callback: (html) => {
|
||||||
const fd = new FormDataExtended(html[0].querySelector("form"));
|
const fd = new FormDataExtended(html[0].querySelector("form"));
|
||||||
resolve(fd.toObject());
|
resolve(fd.toObject());
|
||||||
}
|
}
|
||||||
|
@ -87,10 +90,9 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static _getPowerData(actorData, itemData, data) {
|
static _getPowerData(actorData, itemData, data) {
|
||||||
|
|
||||||
// Determine whether the power may be up-cast
|
// Determine whether the power may be up-cast
|
||||||
const lvl = itemData.level;
|
const lvl = itemData.level;
|
||||||
const consumePowerSlot = (lvl > 0) && CONFIG.SW5E.powerUpcastModes.includes(itemData.preparation.mode);
|
const consumePowerSlot = lvl > 0 && CONFIG.SW5E.powerUpcastModes.includes(itemData.preparation.mode);
|
||||||
|
|
||||||
// If can't upcast, return early and don't bother calculating available power slots
|
// If can't upcast, return early and don't bother calculating available power slots
|
||||||
if (!consumePowerSlot) {
|
if (!consumePowerSlot) {
|
||||||
|
@ -106,67 +108,71 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
case "lgt":
|
case "lgt":
|
||||||
case "uni":
|
case "uni":
|
||||||
case "drk": {
|
case "drk": {
|
||||||
powerType = "force"
|
powerType = "force";
|
||||||
points = actorData.attributes.force.points.value + actorData.attributes.force.points.temp;
|
points = actorData.attributes.force.points.value + actorData.attributes.force.points.temp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "tec": {
|
case "tec": {
|
||||||
powerType = "tech"
|
powerType = "tech";
|
||||||
points = actorData.attributes.tech.points.value + actorData.attributes.tech.points.temp;
|
points = actorData.attributes.tech.points.value + actorData.attributes.tech.points.temp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eliminate point usage for innate casters
|
// eliminate point usage for innate casters
|
||||||
if (actorData.attributes.powercasting === 'innate') points = 999;
|
if (actorData.attributes.powercasting === "innate") points = 999;
|
||||||
|
|
||||||
|
let powerLevels;
|
||||||
let powerLevels
|
|
||||||
if (powerType === "force") {
|
if (powerType === "force") {
|
||||||
powerLevels = Array.fromRange(10).reduce((arr, i) => {
|
powerLevels = Array.fromRange(10)
|
||||||
|
.reduce((arr, i) => {
|
||||||
if (i < lvl) return arr;
|
if (i < lvl) return arr;
|
||||||
const label = CONFIG.SW5E.powerLevels[i];
|
const label = CONFIG.SW5E.powerLevels[i];
|
||||||
const l = actorData.powers["power" + i] || {fmax: 0, foverride: null};
|
const l = actorData.powers["power" + i] || {fmax: 0, foverride: null};
|
||||||
let max = parseInt(l.foverride || l.fmax || 0);
|
let max = parseInt(l.foverride || l.fmax || 0);
|
||||||
let slots = Math.clamped(parseInt(l.fvalue || 0), 0, max);
|
let slots = Math.clamped(parseInt(l.fvalue || 0), 0, max);
|
||||||
if (max > 0) lmax = i;
|
if (max > 0) lmax = i;
|
||||||
if ((max > 0) && (slots > 0) && (points > i)){
|
if (max > 0 && slots > 0 && points > i) {
|
||||||
arr.push({
|
arr.push({
|
||||||
level: i,
|
level: i,
|
||||||
label: i > 0 ? game.i18n.format('SW5E.PowerLevelSlot', {level: label, n: slots}) : label,
|
label: i > 0 ? game.i18n.format("SW5E.PowerLevelSlot", {level: label, n: slots}) : label,
|
||||||
canCast: max > 0,
|
canCast: max > 0,
|
||||||
hasSlots: slots > 0
|
hasSlots: slots > 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}, []).filter(sl => sl.level <= lmax);
|
}, [])
|
||||||
|
.filter((sl) => sl.level <= lmax);
|
||||||
} else if (powerType === "tech") {
|
} else if (powerType === "tech") {
|
||||||
powerLevels = Array.fromRange(10).reduce((arr, i) => {
|
powerLevels = Array.fromRange(10)
|
||||||
|
.reduce((arr, i) => {
|
||||||
if (i < lvl) return arr;
|
if (i < lvl) return arr;
|
||||||
const label = CONFIG.SW5E.powerLevels[i];
|
const label = CONFIG.SW5E.powerLevels[i];
|
||||||
const l = actorData.powers["power" + i] || {tmax: 0, toverride: null};
|
const l = actorData.powers["power" + i] || {tmax: 0, toverride: null};
|
||||||
let max = parseInt(l.override || l.tmax || 0);
|
let max = parseInt(l.override || l.tmax || 0);
|
||||||
let slots = Math.clamped(parseInt(l.tvalue || 0), 0, max);
|
let slots = Math.clamped(parseInt(l.tvalue || 0), 0, max);
|
||||||
if (max > 0) lmax = i;
|
if (max > 0) lmax = i;
|
||||||
if ((max > 0) && (slots > 0) && (points > i)){
|
if (max > 0 && slots > 0 && points > i) {
|
||||||
arr.push({
|
arr.push({
|
||||||
level: i,
|
level: i,
|
||||||
label: i > 0 ? game.i18n.format('SW5E.PowerLevelSlot', {level: label, n: slots}) : label,
|
label: i > 0 ? game.i18n.format("SW5E.PowerLevelSlot", {level: label, n: slots}) : label,
|
||||||
canCast: max > 0,
|
canCast: max > 0,
|
||||||
hasSlots: slots > 0
|
hasSlots: slots > 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}, []).filter(sl => sl.level <= lmax);
|
}, [])
|
||||||
|
.filter((sl) => sl.level <= lmax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const canCast = powerLevels.some((l) => l.hasSlots);
|
||||||
|
if (!canCast)
|
||||||
const canCast = powerLevels.some(l => l.hasSlots);
|
data.errors.push(
|
||||||
if ( !canCast ) data.errors.push(game.i18n.format("SW5E.PowerCastNoSlots", {
|
game.i18n.format("SW5E.PowerCastNoSlots", {
|
||||||
level: CONFIG.SW5E.powerLevels[lvl],
|
level: CONFIG.SW5E.powerLevels[lvl],
|
||||||
name: data.item.name
|
name: data.item.name
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Merge power casting data
|
// Merge power casting data
|
||||||
return foundry.utils.mergeObject(data, {isPower: true, consumePowerSlot, powerLevels});
|
return foundry.utils.mergeObject(data, {isPower: true, consumePowerSlot, powerLevels});
|
||||||
|
@ -179,7 +185,6 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static _getAbilityUseNote(item, uses, recharge) {
|
static _getAbilityUseNote(item, uses, recharge) {
|
||||||
|
|
||||||
// Zero quantity
|
// Zero quantity
|
||||||
const quantity = item.data.quantity;
|
const quantity = item.data.quantity;
|
||||||
if (quantity <= 0) return game.i18n.localize("SW5E.AbilityUseUnavailableHint");
|
if (quantity <= 0) return game.i18n.localize("SW5E.AbilityUseUnavailableHint");
|
||||||
|
@ -187,8 +192,8 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
// Abilities which use Recharge
|
// Abilities which use Recharge
|
||||||
if (!!recharge.value) {
|
if (!!recharge.value) {
|
||||||
return game.i18n.format(recharge.charged ? "SW5E.AbilityUseChargedHint" : "SW5E.AbilityUseRechargeHint", {
|
return game.i18n.format(recharge.charged ? "SW5E.AbilityUseChargedHint" : "SW5E.AbilityUseRechargeHint", {
|
||||||
type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`),
|
type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`)
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does not use any resource
|
// Does not use any resource
|
||||||
|
|
|
@ -17,7 +17,7 @@ export default class ActorSheetFlags extends DocumentSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get title() {
|
get title() {
|
||||||
return `${game.i18n.localize('SW5E.FlagsTitle')}: ${this.object.name}`;
|
return `${game.i18n.localize("SW5E.FlagsTitle")}: ${this.object.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -40,8 +40,10 @@ export default class ActorSheetFlags extends DocumentSheet {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_getClasses() {
|
_getClasses() {
|
||||||
const classes = this.object.items.filter(i => i.type === "class");
|
const classes = this.object.items.filter((i) => i.type === "class");
|
||||||
return classes.sort((a, b) => a.name.localeCompare(b.name)).reduce((obj, i) => {
|
return classes
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
.reduce((obj, i) => {
|
||||||
obj[i.id] = i.name;
|
obj[i.id] = i.name;
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
|
@ -63,7 +65,7 @@ export default class ActorSheetFlags extends DocumentSheet {
|
||||||
let flag = foundry.utils.deepClone(v);
|
let flag = foundry.utils.deepClone(v);
|
||||||
flag.type = v.type.name;
|
flag.type = v.type.name;
|
||||||
flag.isCheckbox = v.type === Boolean;
|
flag.isCheckbox = v.type === Boolean;
|
||||||
flag.isSelect = v.hasOwnProperty('choices');
|
flag.isSelect = v.hasOwnProperty("choices");
|
||||||
flag.value = getProperty(baseData.flags, `sw5e.${k}`);
|
flag.value = getProperty(baseData.flags, `sw5e.${k}`);
|
||||||
flags[v.section][`flags.sw5e.${k}`] = flag;
|
flags[v.section][`flags.sw5e.${k}`] = flag;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +115,7 @@ export default class ActorSheetFlags extends DocumentSheet {
|
||||||
let unset = false;
|
let unset = false;
|
||||||
const flags = updateData.flags.sw5e;
|
const flags = updateData.flags.sw5e;
|
||||||
//clone flags to dnd5e for module compatability
|
//clone flags to dnd5e for module compatability
|
||||||
updateData.flags.dnd5e = updateData.flags.sw5e
|
updateData.flags.dnd5e = updateData.flags.sw5e;
|
||||||
for (let [k, v] of Object.entries(flags)) {
|
for (let [k, v] of Object.entries(flags)) {
|
||||||
if ([undefined, null, "", false, 0].includes(v)) {
|
if ([undefined, null, "", false, 0].includes(v)) {
|
||||||
delete flags[k];
|
delete flags[k];
|
||||||
|
|
|
@ -5,7 +5,6 @@ import Actor5e from "../actor/entity.js";
|
||||||
* @extends {FormApplication}
|
* @extends {FormApplication}
|
||||||
*/
|
*/
|
||||||
export default class ActorTypeConfig extends FormApplication {
|
export default class ActorTypeConfig extends FormApplication {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
@ -32,11 +31,11 @@ export default class ActorTypeConfig extends FormApplication {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
getData(options) {
|
getData(options) {
|
||||||
|
|
||||||
// Get current value or new default
|
// Get current value or new default
|
||||||
let attr = foundry.utils.getProperty(this.object.data.data, 'details.type');
|
let attr = foundry.utils.getProperty(this.object.data.data, "details.type");
|
||||||
if ( foundry.utils.getType(attr) !== "Object" ) attr = {
|
if (foundry.utils.getType(attr) !== "Object")
|
||||||
value: (attr in CONFIG.SW5E.creatureTypes) ? attr : "humanoid",
|
attr = {
|
||||||
|
value: attr in CONFIG.SW5E.creatureTypes ? attr : "humanoid",
|
||||||
subtype: "",
|
subtype: "",
|
||||||
swarm: "",
|
swarm: "",
|
||||||
custom: ""
|
custom: ""
|
||||||
|
@ -48,7 +47,7 @@ export default class ActorTypeConfig extends FormApplication {
|
||||||
types[k] = {
|
types[k] = {
|
||||||
label: game.i18n.localize(v),
|
label: game.i18n.localize(v),
|
||||||
chosen: attr.value === k
|
chosen: attr.value === k
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return data for rendering
|
// Return data for rendering
|
||||||
|
@ -61,12 +60,14 @@ export default class ActorTypeConfig extends FormApplication {
|
||||||
},
|
},
|
||||||
subtype: attr.subtype,
|
subtype: attr.subtype,
|
||||||
swarm: attr.swarm,
|
swarm: attr.swarm,
|
||||||
sizes: Array.from(Object.entries(CONFIG.SW5E.actorSizes)).reverse().reduce((obj, e) => {
|
sizes: Array.from(Object.entries(CONFIG.SW5E.actorSizes))
|
||||||
|
.reverse()
|
||||||
|
.reduce((obj, e) => {
|
||||||
obj[e[0]] = e[1];
|
obj[e[0]] = e[1];
|
||||||
return obj;
|
return obj;
|
||||||
}, {}),
|
}, {}),
|
||||||
preview: Actor5e.formatCreatureType(attr) || "–"
|
preview: Actor5e.formatCreatureType(attr) || "–"
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -74,7 +75,7 @@ export default class ActorTypeConfig extends FormApplication {
|
||||||
/** @override */
|
/** @override */
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
const typeObject = foundry.utils.expandObject(formData);
|
const typeObject = foundry.utils.expandObject(formData);
|
||||||
return this.object.update({ 'data.details.type': typeObject });
|
return this.object.update({"data.details.type": typeObject});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* @implements {DocumentSheet}
|
* @implements {DocumentSheet}
|
||||||
*/
|
*/
|
||||||
export default class ActorHitDiceConfig extends DocumentSheet {
|
export default class ActorHitDiceConfig extends DocumentSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
@ -26,7 +25,8 @@ export default class ActorHitDiceConfig extends DocumentSheet {
|
||||||
/** @override */
|
/** @override */
|
||||||
getData(options) {
|
getData(options) {
|
||||||
return {
|
return {
|
||||||
classes: this.object.items.reduce((classes, item) => {
|
classes: this.object.items
|
||||||
|
.reduce((classes, item) => {
|
||||||
if (item.data.type === "class") {
|
if (item.data.type === "class") {
|
||||||
// Add the appropriate data only if this item is a "class"
|
// Add the appropriate data only if this item is a "class"
|
||||||
classes.push({
|
classes.push({
|
||||||
|
@ -35,11 +35,12 @@ export default class ActorHitDiceConfig extends DocumentSheet {
|
||||||
diceDenom: item.data.data.hitDice,
|
diceDenom: item.data.data.hitDice,
|
||||||
currentHitDice: item.data.data.levels - item.data.data.hitDiceUsed,
|
currentHitDice: item.data.data.levels - item.data.data.hitDiceUsed,
|
||||||
maxHitDice: item.data.data.levels,
|
maxHitDice: item.data.data.levels,
|
||||||
canRoll: (item.data.data.levels - item.data.data.hitDiceUsed) > 0
|
canRoll: item.data.data.levels - item.data.data.hitDiceUsed > 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return classes;
|
return classes;
|
||||||
}, []).sort((a, b) => parseInt(b.diceDenom.slice(1)) - parseInt(a.diceDenom.slice(1)))
|
}, [])
|
||||||
|
.sort((a, b) => parseInt(b.diceDenom.slice(1)) - parseInt(a.diceDenom.slice(1)))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ export default class ActorHitDiceConfig extends DocumentSheet {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
|
|
||||||
// Hook up -/+ buttons to adjust the current value in the form
|
// Hook up -/+ buttons to adjust the current value in the form
|
||||||
html.find("button.increment,button.decrement").click(event => {
|
html.find("button.increment,button.decrement").click((event) => {
|
||||||
const button = event.currentTarget;
|
const button = event.currentTarget;
|
||||||
const current = button.parentElement.querySelector(".current");
|
const current = button.parentElement.querySelector(".current");
|
||||||
const max = button.parentElement.querySelector(".max");
|
const max = button.parentElement.querySelector(".max");
|
||||||
|
@ -67,8 +68,8 @@ export default class ActorHitDiceConfig extends DocumentSheet {
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
const actorItems = this.object.items;
|
const actorItems = this.object.items;
|
||||||
const classUpdates = Object.entries(formData).map(([id, hd]) => ({
|
const classUpdates = Object.entries(formData).map(([id, hd]) => ({
|
||||||
_id: id,
|
"_id": id,
|
||||||
"data.hitDiceUsed": actorItems.get(id).data.data.levels - hd,
|
"data.hitDiceUsed": actorItems.get(id).data.data.levels - hd
|
||||||
}));
|
}));
|
||||||
return this.object.updateEmbeddedDocuments("Item", classUpdates);
|
return this.object.updateEmbeddedDocuments("Item", classUpdates);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default class LongRestDialog extends Dialog {
|
||||||
rest: {
|
rest: {
|
||||||
icon: '<i class="fas fa-bed"></i>',
|
icon: '<i class="fas fa-bed"></i>',
|
||||||
label: game.i18n.localize("SW5E.Rest"),
|
label: game.i18n.localize("SW5E.Rest"),
|
||||||
callback: html => {
|
callback: (html) => {
|
||||||
let newDay = true;
|
let newDay = true;
|
||||||
if (game.settings.get("sw5e", "restVariant") !== "gritty")
|
if (game.settings.get("sw5e", "restVariant") !== "gritty")
|
||||||
newDay = html.find('input[name="newDay"]')[0].checked;
|
newDay = html.find('input[name="newDay"]')[0].checked;
|
||||||
|
@ -58,7 +58,7 @@ export default class LongRestDialog extends Dialog {
|
||||||
callback: reject
|
callback: reject
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
default: 'rest',
|
default: "rest",
|
||||||
close: reject
|
close: reject
|
||||||
});
|
});
|
||||||
dlg.render(true);
|
dlg.render(true);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* @extends {DocumentSheet}
|
* @extends {DocumentSheet}
|
||||||
*/
|
*/
|
||||||
export default class ActorMovementConfig extends DocumentSheet {
|
export default class ActorMovementConfig extends DocumentSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
/**
|
|
||||||
* A helper Dialog subclass for rolling Hit Dice on a recharge rest
|
|
||||||
* @extends {Dialog}
|
|
||||||
*/
|
|
||||||
export default class RechargeRestDialog extends Dialog {
|
|
||||||
constructor(actor, dialogData={}, options={}) {
|
|
||||||
super(dialogData, options);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a reference to the Actor entity which is resting
|
|
||||||
* @type {Actor}
|
|
||||||
*/
|
|
||||||
this.actor = actor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Track the most recently used HD denomination for re-rendering the form
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this._denom = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
static get defaultOptions() {
|
|
||||||
return mergeObject(super.defaultOptions, {
|
|
||||||
template: "systems/sw5e/templates/apps/recharge-rest.html",
|
|
||||||
classes: ["sw5e", "dialog"]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
getData() {
|
|
||||||
const data = super.getData();
|
|
||||||
|
|
||||||
// Determine Hull Dice
|
|
||||||
data.availableHD = this.actor.data.items.reduce((hd, item) => {
|
|
||||||
if ( item.type === "starship" ) {
|
|
||||||
const d = item.data;
|
|
||||||
const denom = d.hullDice || "d6";
|
|
||||||
const available = parseInt(d.hullDiceStart || 1) + parseInt(d.tier || 0) - parseInt(d.hullDiceUsed || 0);
|
|
||||||
hd[denom] = denom in hd ? hd[denom] + available : available;
|
|
||||||
}
|
|
||||||
return hd;
|
|
||||||
}, {});
|
|
||||||
data.canRoll = this.actor.data.data.attributes.hull.dice > 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
activateListeners(html) {
|
|
||||||
super.activateListeners(html);
|
|
||||||
let btn = html.find("#roll-hulld");
|
|
||||||
btn.click(this._onRollHullDie.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle rolling a Hull Die as part of a Recharge Rest action
|
|
||||||
* @param {Event} event The triggering click event
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
async _onRollHullDie(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const btn = event.currentTarget;
|
|
||||||
this._denom = btn.form.hulld.value;
|
|
||||||
await this.actor.rollHullDie(this._denom);
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper constructor function which displays the Short Rest dialog and returns a Promise once it's workflow has
|
|
||||||
* been resolved.
|
|
||||||
* @param {Actor5e} actor
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
static async rechargeRestDialog({actor}={}) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const dlg = new this(actor, {
|
|
||||||
title: "Recharge Rest",
|
|
||||||
buttons: {
|
|
||||||
rest: {
|
|
||||||
icon: '<i class="fas fa-bed"></i>',
|
|
||||||
label: "Rest",
|
|
||||||
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: reject
|
|
||||||
}
|
|
||||||
},
|
|
||||||
close: reject
|
|
||||||
});
|
|
||||||
dlg.render(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/**
|
|
||||||
* A helper Dialog subclass for completing a refitting rest
|
|
||||||
* @extends {Dialog}
|
|
||||||
*/
|
|
||||||
export default class RefittingRestDialog 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/refitting-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 Refitting Rest confirmation dialog and returns a Promise once it's
|
|
||||||
* workflow has been resolved.
|
|
||||||
* @param {Actor5e} actor
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
static async refittingRestDialog({ actor } = {}) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const dlg = new this(actor, {
|
|
||||||
title: "Refitting 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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,11 +18,11 @@ export default class SelectItemsPrompt extends Dialog {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
|
|
||||||
// render the item's sheet if its image is clicked
|
// render the item's sheet if its image is clicked
|
||||||
html.on('click', '.item-image', (event) => {
|
html.on("click", ".item-image", (event) => {
|
||||||
const item = this.items.find((feature) => feature.id === event.currentTarget.dataset?.itemId);
|
const item = this.items.find((feature) => feature.id === event.currentTarget.dataset?.itemId);
|
||||||
|
|
||||||
item?.sheet.render(true);
|
item?.sheet.render(true);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,29 +33,27 @@ export default class SelectItemsPrompt extends Dialog {
|
||||||
* @param {string} options.hint - Localized hint to display at the top of the prompt
|
* @param {string} options.hint - Localized hint to display at the top of the prompt
|
||||||
* @return {Promise<string[]>} - list of item ids which the user has selected
|
* @return {Promise<string[]>} - list of item ids which the user has selected
|
||||||
*/
|
*/
|
||||||
static async create(items, {
|
static async create(items, {hint}) {
|
||||||
hint
|
|
||||||
}) {
|
|
||||||
// Render the ability usage template
|
// Render the ability usage template
|
||||||
const html = await renderTemplate("systems/sw5e/templates/apps/select-items-prompt.html", {items, hint});
|
const html = await renderTemplate("systems/sw5e/templates/apps/select-items-prompt.html", {items, hint});
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const dlg = new this(items, {
|
const dlg = new this(items, {
|
||||||
title: game.i18n.localize('SW5E.SelectItemsPromptTitle'),
|
title: game.i18n.localize("SW5E.SelectItemsPromptTitle"),
|
||||||
content: html,
|
content: html,
|
||||||
buttons: {
|
buttons: {
|
||||||
apply: {
|
apply: {
|
||||||
icon: `<i class="fas fa-user-plus"></i>`,
|
icon: `<i class="fas fa-user-plus"></i>`,
|
||||||
label: game.i18n.localize('SW5E.Apply'),
|
label: game.i18n.localize("SW5E.Apply"),
|
||||||
callback: html => {
|
callback: (html) => {
|
||||||
const fd = new FormDataExtended(html[0].querySelector("form")).toObject();
|
const fd = new FormDataExtended(html[0].querySelector("form")).toObject();
|
||||||
const selectedIds = Object.keys(fd).filter(itemId => fd[itemId]);
|
const selectedIds = Object.keys(fd).filter((itemId) => fd[itemId]);
|
||||||
resolve(selectedIds);
|
resolve(selectedIds);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
icon: '<i class="fas fa-forward"></i>',
|
icon: '<i class="fas fa-forward"></i>',
|
||||||
label: game.i18n.localize('SW5E.Skip'),
|
label: game.i18n.localize("SW5E.Skip"),
|
||||||
callback: () => resolve([])
|
callback: () => resolve([])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* @extends {DocumentSheet}
|
* @extends {DocumentSheet}
|
||||||
*/
|
*/
|
||||||
export default class ActorSensesConfig extends DocumentSheet {
|
export default class ActorSensesConfig extends DocumentSheet {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
@ -29,14 +28,15 @@ export default class ActorSensesConfig extends DocumentSheet {
|
||||||
const data = {
|
const data = {
|
||||||
senses: {},
|
senses: {},
|
||||||
special: senses.special ?? "",
|
special: senses.special ?? "",
|
||||||
units: senses.units, movementUnits: CONFIG.SW5E.movementUnits
|
units: senses.units,
|
||||||
|
movementUnits: CONFIG.SW5E.movementUnits
|
||||||
};
|
};
|
||||||
for (let [name, label] of Object.entries(CONFIG.SW5E.senses)) {
|
for (let [name, label] of Object.entries(CONFIG.SW5E.senses)) {
|
||||||
const v = senses[name];
|
const v = senses[name];
|
||||||
data.senses[name] = {
|
data.senses[name] = {
|
||||||
label: game.i18n.localize(label),
|
label: game.i18n.localize(label),
|
||||||
value: Number.isNumeric(v) ? v.toNearest(0.1) : 0
|
value: Number.isNumeric(v) ? v.toNearest(0.1) : 0
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ export default class ShortRestDialog extends Dialog {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
activateListeners(html) {
|
activateListeners(html) {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
|
@ -98,7 +97,7 @@ export default class ShortRestDialog extends Dialog {
|
||||||
rest: {
|
rest: {
|
||||||
icon: '<i class="fas fa-bed"></i>',
|
icon: '<i class="fas fa-bed"></i>',
|
||||||
label: game.i18n.localize("SW5E.Rest"),
|
label: game.i18n.localize("SW5E.Rest"),
|
||||||
callback: html => {
|
callback: (html) => {
|
||||||
let newDay = false;
|
let newDay = false;
|
||||||
if (game.settings.get("sw5e", "restVariant") === "gritty")
|
if (game.settings.get("sw5e", "restVariant") === "gritty")
|
||||||
newDay = html.find('input[name="newDay"]')[0].checked;
|
newDay = html.find('input[name="newDay"]')[0].checked;
|
||||||
|
@ -127,7 +126,9 @@ export default class ShortRestDialog extends Dialog {
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
static async longRestDialog({actor} = {}) {
|
static async longRestDialog({actor} = {}) {
|
||||||
console.warn("WARNING! ShortRestDialog.longRestDialog has been deprecated, use LongRestDialog.longRestDialog instead.");
|
console.warn(
|
||||||
|
"WARNING! ShortRestDialog.longRestDialog has been deprecated, use LongRestDialog.longRestDialog instead."
|
||||||
|
);
|
||||||
return LongRestDialog.longRestDialog(...arguments);
|
return LongRestDialog.longRestDialog(...arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* @extends {DocumentSheet}
|
* @extends {DocumentSheet}
|
||||||
*/
|
*/
|
||||||
export default class TraitSelector extends DocumentSheet {
|
export default class TraitSelector extends DocumentSheet {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
@ -38,22 +37,22 @@ export default class TraitSelector extends DocumentSheet {
|
||||||
getData() {
|
getData() {
|
||||||
const attr = foundry.utils.getProperty(this.object.data, this.attribute);
|
const attr = foundry.utils.getProperty(this.object.data, this.attribute);
|
||||||
const o = this.options;
|
const o = this.options;
|
||||||
const value = (o.valueKey) ? attr[o.valueKey] ?? [] : attr;
|
const value = o.valueKey ? attr[o.valueKey] ?? [] : attr;
|
||||||
const custom = (o.customKey) ? attr[o.customKey] ?? "" : "";
|
const custom = o.customKey ? attr[o.customKey] ?? "" : "";
|
||||||
|
|
||||||
// Populate choices
|
// Populate choices
|
||||||
const choices = Object.entries(o.choices).reduce((obj, e) => {
|
const choices = Object.entries(o.choices).reduce((obj, e) => {
|
||||||
let [k, v] = e;
|
let [k, v] = e;
|
||||||
obj[k] = {label: v, chosen: attr ? value.includes(k) : false};
|
obj[k] = {label: v, chosen: attr ? value.includes(k) : false};
|
||||||
return obj;
|
return obj;
|
||||||
}, {})
|
}, {});
|
||||||
|
|
||||||
// Return data
|
// Return data
|
||||||
return {
|
return {
|
||||||
allowCustom: o.allowCustom,
|
allowCustom: o.allowCustom,
|
||||||
choices: choices,
|
choices: choices,
|
||||||
custom: custom
|
custom: custom
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -65,7 +64,7 @@ export default class TraitSelector extends DocumentSheet {
|
||||||
// Obtain choices
|
// Obtain choices
|
||||||
const chosen = [];
|
const chosen = [];
|
||||||
for (let [k, v] of Object.entries(formData)) {
|
for (let [k, v] of Object.entries(formData)) {
|
||||||
if ( (k !== "custom") && v ) chosen.push(k);
|
if (k !== "custom" && v) chosen.push(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object including custom data
|
// Object including custom data
|
||||||
|
@ -75,10 +74,10 @@ export default class TraitSelector extends DocumentSheet {
|
||||||
if (o.allowCustom) updateData[`${this.attribute}.${o.customKey}`] = formData.custom;
|
if (o.allowCustom) updateData[`${this.attribute}.${o.customKey}`] = formData.custom;
|
||||||
|
|
||||||
// Validate the number chosen
|
// Validate the number chosen
|
||||||
if ( o.minimum && (chosen.length < o.minimum) ) {
|
if (o.minimum && chosen.length < o.minimum) {
|
||||||
return ui.notifications.error(`You must choose at least ${o.minimum} options`);
|
return ui.notifications.error(`You must choose at least ${o.minimum} options`);
|
||||||
}
|
}
|
||||||
if ( o.maximum && (chosen.length > o.maximum) ) {
|
if (o.maximum && chosen.length > o.maximum) {
|
||||||
return ui.notifications.error(`You may choose no more than ${o.maximum} options`);
|
return ui.notifications.error(`You may choose no more than ${o.maximum} options`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const measureDistances = function(segments, options={}) {
|
||||||
const d = canvas.dimensions;
|
const d = canvas.dimensions;
|
||||||
|
|
||||||
// Iterate over measured segments
|
// Iterate over measured segments
|
||||||
return segments.map(s => {
|
return segments.map((s) => {
|
||||||
let r = s.ray;
|
let r = s.ray;
|
||||||
|
|
||||||
// Determine the total distance traveled
|
// Determine the total distance traveled
|
||||||
|
@ -23,7 +23,7 @@ export const measureDistances = function(segments, options={}) {
|
||||||
// Alternative DMG Movement
|
// Alternative DMG Movement
|
||||||
if (rule === "5105") {
|
if (rule === "5105") {
|
||||||
let nd10 = Math.floor(nDiagonal / 2) - Math.floor((nDiagonal - nd) / 2);
|
let nd10 = Math.floor(nDiagonal / 2) - Math.floor((nDiagonal - nd) / 2);
|
||||||
let spaces = (nd10 * 2) + (nd - nd10) + ns;
|
let spaces = nd10 * 2 + (nd - nd10) + ns;
|
||||||
return spaces * canvas.dimensions.distance;
|
return spaces * canvas.dimensions.distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,9 @@ export default class CharacterImporter {
|
||||||
this.addSpecies(sourceCharacter.attribs.find((e) => e.name == "race").current, actor);
|
this.addSpecies(sourceCharacter.attribs.find((e) => e.name == "race").current, actor);
|
||||||
|
|
||||||
this.addPowers(
|
this.addPowers(
|
||||||
sourceCharacter.attribs.filter((e) => e.name.search(/repeating_power.+_powername/g) != -1).map((e) => e.current),
|
sourceCharacter.attribs
|
||||||
|
.filter((e) => e.name.search(/repeating_power.+_powername/g) != -1)
|
||||||
|
.map((e) => e.current),
|
||||||
actor
|
actor
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlight critical success or failure on d20 rolls
|
* Highlight critical success or failure on d20 rolls
|
||||||
*/
|
*/
|
||||||
|
@ -11,9 +10,9 @@ export const highlightCriticalSuccessFailure = function(message, html, data) {
|
||||||
const d = roll.dice[0];
|
const d = roll.dice[0];
|
||||||
|
|
||||||
// Ensure it is an un-modified d20 roll
|
// Ensure it is an un-modified d20 roll
|
||||||
const isD20 = (d.faces === 20) && ( d.values.length === 1 );
|
const isD20 = d.faces === 20 && d.values.length === 1;
|
||||||
if (!isD20) return;
|
if (!isD20) return;
|
||||||
const isModifiedRoll = ("success" in d.results[0]) || d.options.marginSuccess || d.options.marginFailure;
|
const isModifiedRoll = "success" in d.results[0] || d.options.marginSuccess || d.options.marginFailure;
|
||||||
if (isModifiedRoll) return;
|
if (isModifiedRoll) return;
|
||||||
|
|
||||||
// Highlight successes and failures
|
// Highlight successes and failures
|
||||||
|
@ -41,13 +40,13 @@ export const displayChatActionButtons = function(message, html, data) {
|
||||||
// If the user is the message author or the actor owner, proceed
|
// If the user is the message author or the actor owner, proceed
|
||||||
let actor = game.actors.get(data.message.speaker.actor);
|
let actor = game.actors.get(data.message.speaker.actor);
|
||||||
if (actor && actor.isOwner) return;
|
if (actor && actor.isOwner) return;
|
||||||
else if ( game.user.isGM || (data.author.id === game.user.id)) return;
|
else if (game.user.isGM || data.author.id === game.user.id) return;
|
||||||
|
|
||||||
// Otherwise conceal action buttons except for saving throw
|
// Otherwise conceal action buttons except for saving throw
|
||||||
const buttons = chatCard.find("button[data-action]");
|
const buttons = chatCard.find("button[data-action]");
|
||||||
buttons.each((i, btn) => {
|
buttons.each((i, btn) => {
|
||||||
if (btn.dataset.action === "save") return;
|
if (btn.dataset.action === "save") return;
|
||||||
btn.style.display = "none"
|
btn.style.display = "none";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -64,7 +63,7 @@ export const displayChatActionButtons = function(message, html, data) {
|
||||||
* @return {Array} The extended options Array including new context choices
|
* @return {Array} The extended options Array including new context choices
|
||||||
*/
|
*/
|
||||||
export const addChatMessageContextOptions = function (html, options) {
|
export const addChatMessageContextOptions = function (html, options) {
|
||||||
let canApply = li => {
|
let canApply = (li) => {
|
||||||
const message = game.messages.get(li.data("messageId"));
|
const message = game.messages.get(li.data("messageId"));
|
||||||
return message?.isRoll && message?.isContentVisible && canvas.tokens?.controlled.length;
|
return message?.isRoll && message?.isContentVisible && canvas.tokens?.controlled.length;
|
||||||
};
|
};
|
||||||
|
@ -73,25 +72,25 @@ export const addChatMessageContextOptions = function(html, options) {
|
||||||
name: game.i18n.localize("SW5E.ChatContextDamage"),
|
name: game.i18n.localize("SW5E.ChatContextDamage"),
|
||||||
icon: '<i class="fas fa-user-minus"></i>',
|
icon: '<i class="fas fa-user-minus"></i>',
|
||||||
condition: canApply,
|
condition: canApply,
|
||||||
callback: li => applyChatCardDamage(li, 1)
|
callback: (li) => applyChatCardDamage(li, 1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: game.i18n.localize("SW5E.ChatContextHealing"),
|
name: game.i18n.localize("SW5E.ChatContextHealing"),
|
||||||
icon: '<i class="fas fa-user-plus"></i>',
|
icon: '<i class="fas fa-user-plus"></i>',
|
||||||
condition: canApply,
|
condition: canApply,
|
||||||
callback: li => applyChatCardDamage(li, -1)
|
callback: (li) => applyChatCardDamage(li, -1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: game.i18n.localize("SW5E.ChatContextDoubleDamage"),
|
name: game.i18n.localize("SW5E.ChatContextDoubleDamage"),
|
||||||
icon: '<i class="fas fa-user-injured"></i>',
|
icon: '<i class="fas fa-user-injured"></i>',
|
||||||
condition: canApply,
|
condition: canApply,
|
||||||
callback: li => applyChatCardDamage(li, 2)
|
callback: (li) => applyChatCardDamage(li, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: game.i18n.localize("SW5E.ChatContextHalfDamage"),
|
name: game.i18n.localize("SW5E.ChatContextHalfDamage"),
|
||||||
icon: '<i class="fas fa-user-shield"></i>',
|
icon: '<i class="fas fa-user-shield"></i>',
|
||||||
condition: canApply,
|
condition: canApply,
|
||||||
callback: li => applyChatCardDamage(li, 0.5)
|
callback: (li) => applyChatCardDamage(li, 0.5)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return options;
|
return options;
|
||||||
|
@ -110,10 +109,12 @@ export const addChatMessageContextOptions = function(html, options) {
|
||||||
function applyChatCardDamage(li, multiplier) {
|
function applyChatCardDamage(li, multiplier) {
|
||||||
const message = game.messages.get(li.data("messageId"));
|
const message = game.messages.get(li.data("messageId"));
|
||||||
const roll = message.roll;
|
const roll = message.roll;
|
||||||
return Promise.all(canvas.tokens.controlled.map(t => {
|
return Promise.all(
|
||||||
|
canvas.tokens.controlled.map((t) => {
|
||||||
const a = t.actor;
|
const a = t.actor;
|
||||||
return a.applyDamage(roll.total, multiplier);
|
return a.applyDamage(roll.total, multiplier);
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
@ -1,4 +1 @@
|
||||||
export const ClassFeatures = {
|
export const ClassFeatures = {};
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the default Initiative formula to customize special behaviors of the SW5e system.
|
* Override the default Initiative formula to customize special behaviors of the SW5e system.
|
||||||
* Apply advantage, proficiency, or bonuses where appropriate
|
* Apply advantage, proficiency, or bonuses where appropriate
|
||||||
|
@ -18,10 +17,15 @@ export const _getInitiativeFormula = function() {
|
||||||
nd = 2;
|
nd = 2;
|
||||||
mods += "kh";
|
mods += "kh";
|
||||||
}
|
}
|
||||||
const parts = [`${nd}d20${mods}`, init.mod, (init.prof !== 0) ? init.prof : null, (init.bonus !== 0) ? init.bonus : null];
|
const parts = [
|
||||||
|
`${nd}d20${mods}`,
|
||||||
|
init.mod,
|
||||||
|
init.prof !== 0 ? init.prof : null,
|
||||||
|
init.bonus !== 0 ? init.bonus : null
|
||||||
|
];
|
||||||
|
|
||||||
// Optionally apply Dexterity tiebreaker
|
// Optionally apply Dexterity tiebreaker
|
||||||
const tiebreaker = game.settings.get("sw5e", "initiativeDexTiebreaker");
|
const tiebreaker = game.settings.get("sw5e", "initiativeDexTiebreaker");
|
||||||
if (tiebreaker) parts.push(actor.data.data.abilities.dex.value / 100);
|
if (tiebreaker) parts.push(actor.data.data.abilities.dex.value / 100);
|
||||||
return parts.filter(p => p !== null).join(" + ");
|
return parts.filter((p) => p !== null).join(" + ");
|
||||||
};
|
};
|
||||||
|
|
1307
module/config.js
1307
module/config.js
File diff suppressed because it is too large
Load diff
|
@ -23,14 +23,19 @@ export function simplifyRollFormula(formula, data, {constantFirst = false} = {})
|
||||||
const constantTerms = []; // Terms that are constant, and their associated operators
|
const constantTerms = []; // Terms that are constant, and their associated operators
|
||||||
let operators = []; // Temporary storage for operators before they are moved to one of the above
|
let operators = []; // Temporary storage for operators before they are moved to one of the above
|
||||||
|
|
||||||
for (let term of terms) { // For each term
|
for (let term of terms) {
|
||||||
if (term instanceof OperatorTerm) operators.push(term); // If the term is an addition/subtraction operator, push the term into the operators array
|
// For each term
|
||||||
else { // Otherwise the term is not an operator
|
if (term instanceof OperatorTerm) operators.push(term);
|
||||||
if (term instanceof DiceTerm) { // If the term is something rollable
|
// If the term is an addition/subtraction operator, push the term into the operators array
|
||||||
|
else {
|
||||||
|
// Otherwise the term is not an operator
|
||||||
|
if (term instanceof DiceTerm) {
|
||||||
|
// If the term is something rollable
|
||||||
rollableTerms.push(...operators); // Place all the operators into the rollableTerms array
|
rollableTerms.push(...operators); // Place all the operators into the rollableTerms array
|
||||||
rollableTerms.push(term); // Then place this rollable term into it as well
|
rollableTerms.push(term); // Then place this rollable term into it as well
|
||||||
} //
|
} //
|
||||||
else { // Otherwise, this must be a constant
|
else {
|
||||||
|
// Otherwise, this must be a constant
|
||||||
constantTerms.push(...operators); // Place the operators into the constantTerms array
|
constantTerms.push(...operators); // Place the operators into the constantTerms array
|
||||||
constantTerms.push(term); // Then also add this constant term to that array.
|
constantTerms.push(term); // Then also add this constant term to that array.
|
||||||
} //
|
} //
|
||||||
|
@ -45,7 +50,7 @@ export function simplifyRollFormula(formula, data, {constantFirst = false} = {})
|
||||||
let constantPart = undefined;
|
let constantPart = undefined;
|
||||||
if (constantFormula) {
|
if (constantFormula) {
|
||||||
try {
|
try {
|
||||||
constantPart = Roll.safeEval(constantFormula)
|
constantPart = Roll.safeEval(constantFormula);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`Unable to evaluate constant term ${constantFormula} in simplifyRollFormula`);
|
console.warn(`Unable to evaluate constant term ${constantFormula} in simplifyRollFormula`);
|
||||||
}
|
}
|
||||||
|
@ -111,12 +116,28 @@ function _isUnsupportedTerm(term) {
|
||||||
* @return {Promise<D20Roll|null>} The evaluated D20Roll, or null if the workflow was cancelled
|
* @return {Promise<D20Roll|null>} The evaluated D20Roll, or null if the workflow was cancelled
|
||||||
*/
|
*/
|
||||||
export async function d20Roll({
|
export async function d20Roll({
|
||||||
parts=[], data={}, // Roll creation
|
parts = [],
|
||||||
advantage, disadvantage, fumble=1, critical=20, targetValue, elvenAccuracy, halflingLucky, reliableTalent, // Roll customization
|
data = {}, // Roll creation
|
||||||
chooseModifier=false, fastForward=false, event, template, title, dialogOptions, // Dialog configuration
|
advantage,
|
||||||
chatMessage=true, messageData={}, rollMode, speaker, flavor // Chat Message customization
|
disadvantage,
|
||||||
|
fumble = 1,
|
||||||
|
critical = 20,
|
||||||
|
targetValue,
|
||||||
|
elvenAccuracy,
|
||||||
|
halflingLucky,
|
||||||
|
reliableTalent, // Roll customization
|
||||||
|
chooseModifier = false,
|
||||||
|
fastForward = false,
|
||||||
|
event,
|
||||||
|
template,
|
||||||
|
title,
|
||||||
|
dialogOptions, // Dialog configuration
|
||||||
|
chatMessage = true,
|
||||||
|
messageData = {},
|
||||||
|
rollMode,
|
||||||
|
speaker,
|
||||||
|
flavor // Chat Message customization
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
|
||||||
// Handle input arguments
|
// Handle input arguments
|
||||||
const formula = ["1d20"].concat(parts).join(" + ");
|
const formula = ["1d20"].concat(parts).join(" + ");
|
||||||
const {advantageMode, isFF} = _determineAdvantageMode({advantage, disadvantage, fastForward, event});
|
const {advantageMode, isFF} = _determineAdvantageMode({advantage, disadvantage, fastForward, event});
|
||||||
|
@ -138,14 +159,17 @@ export async function d20Roll({
|
||||||
|
|
||||||
// Prompt a Dialog to further configure the D20Roll
|
// Prompt a Dialog to further configure the D20Roll
|
||||||
if (!isFF) {
|
if (!isFF) {
|
||||||
const configured = await roll.configureDialog({
|
const configured = await roll.configureDialog(
|
||||||
|
{
|
||||||
title,
|
title,
|
||||||
chooseModifier,
|
chooseModifier,
|
||||||
defaultRollMode: defaultRollMode,
|
defaultRollMode: defaultRollMode,
|
||||||
defaultAction: advantageMode,
|
defaultAction: advantageMode,
|
||||||
defaultAbility: data?.item?.ability,
|
defaultAbility: data?.item?.ability,
|
||||||
template
|
template
|
||||||
}, dialogOptions);
|
},
|
||||||
|
dialogOptions
|
||||||
|
);
|
||||||
if (configured === null) return null;
|
if (configured === null) return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +178,9 @@ export async function d20Roll({
|
||||||
|
|
||||||
// Create a Chat Message
|
// Create a Chat Message
|
||||||
if (speaker) {
|
if (speaker) {
|
||||||
console.warn(`You are passing the speaker argument to the d20Roll function directly which should instead be passed as an internal key of messageData`);
|
console.warn(
|
||||||
|
`You are passing the speaker argument to the d20Roll function directly which should instead be passed as an internal key of messageData`
|
||||||
|
);
|
||||||
messageData.speaker = speaker;
|
messageData.speaker = speaker;
|
||||||
}
|
}
|
||||||
if (roll && chatMessage) await roll.toMessage(messageData);
|
if (roll && chatMessage) await roll.toMessage(messageData);
|
||||||
|
@ -171,7 +197,8 @@ function _determineAdvantageMode({event, advantage=false, disadvantage=false, fa
|
||||||
const isFF = fastForward || (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey));
|
const isFF = fastForward || (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey));
|
||||||
let advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.NORMAL;
|
let advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.NORMAL;
|
||||||
if (advantage || event?.altKey) advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.ADVANTAGE;
|
if (advantage || event?.altKey) advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.ADVANTAGE;
|
||||||
else if ( disadvantage || event?.ctrlKey || event?.metaKey ) advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.DISADVANTAGE;
|
else if (disadvantage || event?.ctrlKey || event?.metaKey)
|
||||||
|
advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.DISADVANTAGE;
|
||||||
return {isFF, advantageMode};
|
return {isFF, advantageMode};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,12 +237,25 @@ function _determineAdvantageMode({event, advantage=false, disadvantage=false, fa
|
||||||
* @return {Promise<DamageRoll|null>} The evaluated DamageRoll, or null if the workflow was canceled
|
* @return {Promise<DamageRoll|null>} The evaluated DamageRoll, or null if the workflow was canceled
|
||||||
*/
|
*/
|
||||||
export async function damageRoll({
|
export async function damageRoll({
|
||||||
parts=[], data, // Roll creation
|
parts = [],
|
||||||
critical=false, criticalBonusDice, criticalMultiplier, multiplyNumeric, powerfulCritical, // Damage customization
|
data, // Roll creation
|
||||||
fastForward=false, event, allowCritical=true, template, title, dialogOptions, // Dialog configuration
|
critical = false,
|
||||||
chatMessage=true, messageData={}, rollMode, speaker, flavor, // Chat Message customization
|
criticalBonusDice,
|
||||||
|
criticalMultiplier,
|
||||||
|
multiplyNumeric,
|
||||||
|
powerfulCritical, // Damage customization
|
||||||
|
fastForward = false,
|
||||||
|
event,
|
||||||
|
allowCritical = true,
|
||||||
|
template,
|
||||||
|
title,
|
||||||
|
dialogOptions, // Dialog configuration
|
||||||
|
chatMessage = true,
|
||||||
|
messageData = {},
|
||||||
|
rollMode,
|
||||||
|
speaker,
|
||||||
|
flavor // Chat Message customization
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
|
||||||
// Handle input arguments
|
// Handle input arguments
|
||||||
const defaultRollMode = rollMode || game.settings.get("core", "rollMode");
|
const defaultRollMode = rollMode || game.settings.get("core", "rollMode");
|
||||||
|
|
||||||
|
@ -233,13 +273,16 @@ export async function damageRoll({
|
||||||
|
|
||||||
// Prompt a Dialog to further configure the DamageRoll
|
// Prompt a Dialog to further configure the DamageRoll
|
||||||
if (!isFF) {
|
if (!isFF) {
|
||||||
const configured = await roll.configureDialog({
|
const configured = await roll.configureDialog(
|
||||||
|
{
|
||||||
title,
|
title,
|
||||||
defaultRollMode: defaultRollMode,
|
defaultRollMode: defaultRollMode,
|
||||||
defaultCritical: isCritical,
|
defaultCritical: isCritical,
|
||||||
template,
|
template,
|
||||||
allowCritical
|
allowCritical
|
||||||
}, dialogOptions);
|
},
|
||||||
|
dialogOptions
|
||||||
|
);
|
||||||
if (configured === null) return null;
|
if (configured === null) return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +291,9 @@ export async function damageRoll({
|
||||||
|
|
||||||
// Create a Chat Message
|
// Create a Chat Message
|
||||||
if (speaker) {
|
if (speaker) {
|
||||||
console.warn(`You are passing the speaker argument to the damageRoll function directly which should instead be passed as an internal key of messageData`);
|
console.warn(
|
||||||
|
`You are passing the speaker argument to the damageRoll function directly which should instead be passed as an internal key of messageData`
|
||||||
|
);
|
||||||
messageData.speaker = speaker;
|
messageData.speaker = speaker;
|
||||||
}
|
}
|
||||||
if (roll && chatMessage) await roll.toMessage(messageData);
|
if (roll && chatMessage) await roll.toMessage(messageData);
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
export default class D20Roll extends Roll {
|
export default class D20Roll extends Roll {
|
||||||
constructor(formula, data, options) {
|
constructor(formula, data, options) {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
if ( !((this.terms[0] instanceof Die) && (this.terms[0].faces === 20)) ) {
|
if (!(this.terms[0] instanceof Die && this.terms[0].faces === 20)) {
|
||||||
throw new Error(`Invalid D20Roll formula provided ${this._formula}`);
|
throw new Error(`Invalid D20Roll formula provided ${this._formula}`);
|
||||||
}
|
}
|
||||||
this.configureModifiers();
|
this.configureModifiers();
|
||||||
|
@ -31,8 +31,8 @@ export default class D20Roll extends Roll {
|
||||||
static ADV_MODE = {
|
static ADV_MODE = {
|
||||||
NORMAL: 0,
|
NORMAL: 0,
|
||||||
ADVANTAGE: 1,
|
ADVANTAGE: 1,
|
||||||
DISADVANTAGE: -1,
|
DISADVANTAGE: -1
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTML template path used to configure evaluation of this Roll
|
* The HTML template path used to configure evaluation of this Roll
|
||||||
|
@ -81,13 +81,11 @@ export default class D20Roll extends Roll {
|
||||||
d20.number = this.options.elvenAccuracy ? 3 : 2;
|
d20.number = this.options.elvenAccuracy ? 3 : 2;
|
||||||
d20.modifiers.push("kh");
|
d20.modifiers.push("kh");
|
||||||
d20.options.advantage = true;
|
d20.options.advantage = true;
|
||||||
}
|
} else if (this.hasDisadvantage) {
|
||||||
else if ( this.hasDisadvantage ) {
|
|
||||||
d20.number = 2;
|
d20.number = 2;
|
||||||
d20.modifiers.push("kl");
|
d20.modifiers.push("kl");
|
||||||
d20.options.disadvantage = true;
|
d20.options.disadvantage = true;
|
||||||
}
|
} else d20.number = 1;
|
||||||
else d20.number = 1;
|
|
||||||
|
|
||||||
// Assign critical and fumble thresholds
|
// Assign critical and fumble thresholds
|
||||||
if (this.options.critical) d20.options.critical = this.options.critical;
|
if (this.options.critical) d20.options.critical = this.options.critical;
|
||||||
|
@ -102,7 +100,6 @@ export default class D20Roll extends Roll {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
async toMessage(messageData = {}, options = {}) {
|
async toMessage(messageData = {}, options = {}) {
|
||||||
|
|
||||||
// Evaluate the roll now so we have the results available to determine whether reliable talent came into play
|
// Evaluate the roll now so we have the results available to determine whether reliable talent came into play
|
||||||
if (!this._evaluated) await this.evaluate({async: true});
|
if (!this._evaluated) await this.evaluate({async: true});
|
||||||
|
|
||||||
|
@ -114,7 +111,7 @@ export default class D20Roll extends Roll {
|
||||||
// Add reliable talent to the d20-term flavor text if it applied
|
// Add reliable talent to the d20-term flavor text if it applied
|
||||||
if (this.options.reliableTalent) {
|
if (this.options.reliableTalent) {
|
||||||
const d20 = this.dice[0];
|
const d20 = this.dice[0];
|
||||||
const isRT = d20.results.every(r => !r.active || (r.result < 10));
|
const isRT = d20.results.every((r) => !r.active || r.result < 10);
|
||||||
const label = `(${game.i18n.localize("SW5E.FlagsReliableTalent")})`;
|
const label = `(${game.i18n.localize("SW5E.FlagsReliableTalent")})`;
|
||||||
if (isRT) d20.options.flavor = d20.options.flavor ? `${d20.options.flavor} (${label})` : label;
|
if (isRT) d20.options.flavor = d20.options.flavor ? `${d20.options.flavor} (${label})` : label;
|
||||||
}
|
}
|
||||||
|
@ -140,8 +137,17 @@ export default class D20Roll extends Roll {
|
||||||
* @param {object} options Additional Dialog customization options
|
* @param {object} options Additional Dialog customization options
|
||||||
* @returns {Promise<D20Roll|null>} A resulting D20Roll object constructed with the dialog, or null if the dialog was closed
|
* @returns {Promise<D20Roll|null>} A resulting D20Roll object constructed with the dialog, or null if the dialog was closed
|
||||||
*/
|
*/
|
||||||
async configureDialog({title, defaultRollMode, defaultAction=D20Roll.ADV_MODE.NORMAL, chooseModifier=false, defaultAbility, template}={}, options={}) {
|
async configureDialog(
|
||||||
|
{
|
||||||
|
title,
|
||||||
|
defaultRollMode,
|
||||||
|
defaultAction = D20Roll.ADV_MODE.NORMAL,
|
||||||
|
chooseModifier = false,
|
||||||
|
defaultAbility,
|
||||||
|
template
|
||||||
|
} = {},
|
||||||
|
options = {}
|
||||||
|
) {
|
||||||
// Render the Dialog inner HTML
|
// Render the Dialog inner HTML
|
||||||
const content = await renderTemplate(template ?? this.constructor.EVALUATION_TEMPLATE, {
|
const content = await renderTemplate(template ?? this.constructor.EVALUATION_TEMPLATE, {
|
||||||
formula: `${this.formula} + @bonus`,
|
formula: `${this.formula} + @bonus`,
|
||||||
|
@ -154,32 +160,39 @@ export default class D20Roll extends Roll {
|
||||||
|
|
||||||
let defaultButton = "normal";
|
let defaultButton = "normal";
|
||||||
switch (defaultAction) {
|
switch (defaultAction) {
|
||||||
case D20Roll.ADV_MODE.ADVANTAGE: defaultButton = "advantage"; break;
|
case D20Roll.ADV_MODE.ADVANTAGE:
|
||||||
case D20Roll.ADV_MODE.DISADVANTAGE: defaultButton = "disadvantage"; break;
|
defaultButton = "advantage";
|
||||||
|
break;
|
||||||
|
case D20Roll.ADV_MODE.DISADVANTAGE:
|
||||||
|
defaultButton = "disadvantage";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the Dialog window and await submission of the form
|
// Create the Dialog window and await submission of the form
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
new Dialog({
|
new Dialog(
|
||||||
|
{
|
||||||
title,
|
title,
|
||||||
content,
|
content,
|
||||||
buttons: {
|
buttons: {
|
||||||
advantage: {
|
advantage: {
|
||||||
label: game.i18n.localize("SW5E.Advantage"),
|
label: game.i18n.localize("SW5E.Advantage"),
|
||||||
callback: html => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.ADVANTAGE))
|
callback: (html) => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.ADVANTAGE))
|
||||||
},
|
},
|
||||||
normal: {
|
normal: {
|
||||||
label: game.i18n.localize("SW5E.Normal"),
|
label: game.i18n.localize("SW5E.Normal"),
|
||||||
callback: html => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.NORMAL))
|
callback: (html) => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.NORMAL))
|
||||||
},
|
},
|
||||||
disadvantage: {
|
disadvantage: {
|
||||||
label: game.i18n.localize("SW5E.Disadvantage"),
|
label: game.i18n.localize("SW5E.Disadvantage"),
|
||||||
callback: html => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.DISADVANTAGE))
|
callback: (html) => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.DISADVANTAGE))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
default: defaultButton,
|
default: defaultButton,
|
||||||
close: () => resolve(null)
|
close: () => resolve(null)
|
||||||
}, options).render(true);
|
},
|
||||||
|
options
|
||||||
|
).render(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +217,7 @@ export default class D20Roll extends Roll {
|
||||||
// Customize the modifier
|
// Customize the modifier
|
||||||
if (form.ability?.value) {
|
if (form.ability?.value) {
|
||||||
const abl = this.data.abilities[form.ability.value];
|
const abl = this.data.abilities[form.ability.value];
|
||||||
this.terms.findSplice(t => t.term === "@mod", new NumericTerm({number: abl.mod}));
|
this.terms.findSplice((t) => t.term === "@mod", new NumericTerm({number: abl.mod}));
|
||||||
this.options.flavor += ` (${CONFIG.SW5E.abilities[form.ability.value]})`;
|
this.options.flavor += ` (${CONFIG.SW5E.abilities[form.ability.value]})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ export default class DamageRoll extends Roll {
|
||||||
configureDamage() {
|
configureDamage() {
|
||||||
let flatBonus = 0;
|
let flatBonus = 0;
|
||||||
for (let [i, term] of this.terms.entries()) {
|
for (let [i, term] of this.terms.entries()) {
|
||||||
|
|
||||||
// Multiply dice terms
|
// Multiply dice terms
|
||||||
if (term instanceof DiceTerm) {
|
if (term instanceof DiceTerm) {
|
||||||
term.options.baseNumber = term.options.baseNumber ?? term.number; // Reset back
|
term.options.baseNumber = term.options.baseNumber ?? term.number; // Reset back
|
||||||
|
@ -53,33 +52,34 @@ export default class DamageRoll extends Roll {
|
||||||
|
|
||||||
// Powerful critical - maximize damage and reduce the multiplier by 1
|
// Powerful critical - maximize damage and reduce the multiplier by 1
|
||||||
if (this.options.powerfulCritical) {
|
if (this.options.powerfulCritical) {
|
||||||
flatBonus += (term.number * term.faces);
|
flatBonus += term.number * term.faces;
|
||||||
cm = Math.max(1, cm - 1);
|
cm = Math.max(1, cm - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alter the damage term
|
// Alter the damage term
|
||||||
let cb = (this.options.criticalBonusDice && (i === 0)) ? this.options.criticalBonusDice : 0;
|
let cb = this.options.criticalBonusDice && i === 0 ? this.options.criticalBonusDice : 0;
|
||||||
term.alter(cm, cb);
|
term.alter(cm, cb);
|
||||||
term.options.critical = true;
|
term.options.critical = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiply numeric terms
|
// Multiply numeric terms
|
||||||
else if ( this.options.multiplyNumeric && (term instanceof NumericTerm) ) {
|
else if (this.options.multiplyNumeric && term instanceof NumericTerm) {
|
||||||
term.options.baseNumber = term.options.baseNumber ?? term.number; // Reset back
|
term.options.baseNumber = term.options.baseNumber ?? term.number; // Reset back
|
||||||
term.number = term.options.baseNumber;
|
term.number = term.options.baseNumber;
|
||||||
if (this.isCritical) {
|
if (this.isCritical) {
|
||||||
term.number *= (this.options.criticalMultiplier ?? 2);
|
term.number *= this.options.criticalMultiplier ?? 2;
|
||||||
term.options.critical = true;
|
term.options.critical = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add powerful critical bonus
|
// Add powerful critical bonus
|
||||||
if ( this.options.powerfulCritical && (flatBonus > 0) ) {
|
if (this.options.powerfulCritical && flatBonus > 0) {
|
||||||
this.terms.push(new OperatorTerm({operator: "+"}));
|
this.terms.push(new OperatorTerm({operator: "+"}));
|
||||||
this.terms.push(new NumericTerm({number: flatBonus}, {flavor: game.i18n.localize("SW5E.PowerfulCritical")}));
|
this.terms.push(
|
||||||
|
new NumericTerm({number: flatBonus}, {flavor: game.i18n.localize("SW5E.PowerfulCritical")})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-compile the underlying formula
|
// Re-compile the underlying formula
|
||||||
|
@ -114,34 +114,39 @@ export default class DamageRoll extends Roll {
|
||||||
* @param {object} options Additional Dialog customization options
|
* @param {object} options Additional Dialog customization options
|
||||||
* @returns {Promise<D20Roll|null>} A resulting D20Roll object constructed with the dialog, or null if the dialog was closed
|
* @returns {Promise<D20Roll|null>} A resulting D20Roll object constructed with the dialog, or null if the dialog was closed
|
||||||
*/
|
*/
|
||||||
async configureDialog({title, defaultRollMode, defaultCritical=false, template, allowCritical=true}={}, options={}) {
|
async configureDialog(
|
||||||
|
{title, defaultRollMode, defaultCritical = false, template, allowCritical = true} = {},
|
||||||
|
options = {}
|
||||||
|
) {
|
||||||
// Render the Dialog inner HTML
|
// Render the Dialog inner HTML
|
||||||
const content = await renderTemplate(template ?? this.constructor.EVALUATION_TEMPLATE, {
|
const content = await renderTemplate(template ?? this.constructor.EVALUATION_TEMPLATE, {
|
||||||
formula: `${this.formula} + @bonus`,
|
formula: `${this.formula} + @bonus`,
|
||||||
defaultRollMode,
|
defaultRollMode,
|
||||||
rollModes: CONFIG.Dice.rollModes,
|
rollModes: CONFIG.Dice.rollModes
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the Dialog window and await submission of the form
|
// Create the Dialog window and await submission of the form
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
new Dialog({
|
new Dialog(
|
||||||
|
{
|
||||||
title,
|
title,
|
||||||
content,
|
content,
|
||||||
buttons: {
|
buttons: {
|
||||||
critical: {
|
critical: {
|
||||||
condition: allowCritical,
|
condition: allowCritical,
|
||||||
label: game.i18n.localize("SW5E.CriticalHit"),
|
label: game.i18n.localize("SW5E.CriticalHit"),
|
||||||
callback: html => resolve(this._onDialogSubmit(html, true))
|
callback: (html) => resolve(this._onDialogSubmit(html, true))
|
||||||
},
|
},
|
||||||
normal: {
|
normal: {
|
||||||
label: game.i18n.localize(allowCritical ? "SW5E.Normal" : "SW5E.Roll"),
|
label: game.i18n.localize(allowCritical ? "SW5E.Normal" : "SW5E.Roll"),
|
||||||
callback: html => resolve(this._onDialogSubmit(html, false))
|
callback: (html) => resolve(this._onDialogSubmit(html, false))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
default: defaultCritical ? "critical" : "normal",
|
default: defaultCritical ? "critical" : "normal",
|
||||||
close: () => resolve(null)
|
close: () => resolve(null)
|
||||||
}, options).render(true);
|
},
|
||||||
|
options
|
||||||
|
).render(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
module/effects.js
vendored
15
module/effects.js
vendored
|
@ -10,13 +10,15 @@ export function onManageActiveEffect(event, owner) {
|
||||||
const effect = li.dataset.effectId ? owner.effects.get(li.dataset.effectId) : null;
|
const effect = li.dataset.effectId ? owner.effects.get(li.dataset.effectId) : null;
|
||||||
switch (a.dataset.action) {
|
switch (a.dataset.action) {
|
||||||
case "create":
|
case "create":
|
||||||
return owner.createEmbeddedDocuments("ActiveEffect", [{
|
return owner.createEmbeddedDocuments("ActiveEffect", [
|
||||||
label: game.i18n.localize("SW5E.EffectNew"),
|
{
|
||||||
icon: "icons/svg/aura.svg",
|
"label": game.i18n.localize("SW5E.EffectNew"),
|
||||||
origin: owner.uuid,
|
"icon": "icons/svg/aura.svg",
|
||||||
|
"origin": owner.uuid,
|
||||||
"duration.rounds": li.dataset.effectType === "temporary" ? 1 : undefined,
|
"duration.rounds": li.dataset.effectType === "temporary" ? 1 : undefined,
|
||||||
disabled: li.dataset.effectType === "inactive"
|
"disabled": li.dataset.effectType === "inactive"
|
||||||
}]);
|
}
|
||||||
|
]);
|
||||||
case "edit":
|
case "edit":
|
||||||
return effect.sheet.render(true);
|
return effect.sheet.render(true);
|
||||||
case "delete":
|
case "delete":
|
||||||
|
@ -32,7 +34,6 @@ export function onManageActiveEffect(event, owner) {
|
||||||
* @return {object} Data for rendering
|
* @return {object} Data for rendering
|
||||||
*/
|
*/
|
||||||
export function prepareActiveEffectCategories(effects) {
|
export function prepareActiveEffectCategories(effects) {
|
||||||
|
|
||||||
// Define effect header categories
|
// Define effect header categories
|
||||||
const categories = {
|
const categories = {
|
||||||
temporary: {
|
temporary: {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import AbilityUseDialog from "../apps/ability-use-dialog.js";
|
||||||
* @extends {Item}
|
* @extends {Item}
|
||||||
*/
|
*/
|
||||||
export default class Item5e extends Item {
|
export default class Item5e extends Item {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Item Properties */
|
/* Item Properties */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -21,7 +20,6 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Case 1 - defined directly by the item
|
// Case 1 - defined directly by the item
|
||||||
if (itemData.ability) return itemData.ability;
|
if (itemData.ability) return itemData.ability;
|
||||||
|
|
||||||
// Case 2 - inferred from a parent actor
|
// Case 2 - inferred from a parent actor
|
||||||
else if (this.actor) {
|
else if (this.actor) {
|
||||||
const actorData = this.actor.data.data;
|
const actorData = this.actor.data.data;
|
||||||
|
@ -29,18 +27,20 @@ export default class Item5e extends Item {
|
||||||
// Powers - Use Actor powercasting modifier based on power school
|
// Powers - Use Actor powercasting modifier based on power school
|
||||||
if (this.data.type === "power") {
|
if (this.data.type === "power") {
|
||||||
switch (this.data.data.school) {
|
switch (this.data.data.school) {
|
||||||
case "lgt": return "wis";
|
case "lgt":
|
||||||
case "uni": return (actorData.abilities["wis"].mod >= actorData.abilities["cha"].mod) ? "wis" : "cha";
|
return "wis";
|
||||||
case "drk": return "cha";
|
case "uni":
|
||||||
case "tec": return "int";
|
return actorData.abilities["wis"].mod >= actorData.abilities["cha"].mod ? "wis" : "cha";
|
||||||
|
case "drk":
|
||||||
|
return "cha";
|
||||||
|
case "tec":
|
||||||
|
return "int";
|
||||||
}
|
}
|
||||||
return "none";
|
return "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Tools - default to Intelligence
|
// Tools - default to Intelligence
|
||||||
else if (this.data.type === "tool") return "int";
|
else if (this.data.type === "tool") return "int";
|
||||||
|
|
||||||
// Weapons
|
// Weapons
|
||||||
else if (this.data.type === "weapon") {
|
else if (this.data.type === "weapon") {
|
||||||
const wt = itemData.weaponType;
|
const wt = itemData.weaponType;
|
||||||
|
@ -53,7 +53,7 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Finesse weapons - Str or Dex (PHB pg. 147)
|
// Finesse weapons - Str or Dex (PHB pg. 147)
|
||||||
else if (itemData.properties.fin === true) {
|
else if (itemData.properties.fin === true) {
|
||||||
return (actorData.abilities["dex"].mod >= actorData.abilities["str"].mod) ? "dex" : "str";
|
return actorData.abilities["dex"].mod >= actorData.abilities["str"].mod ? "dex" : "str";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ranged weapons - Dex (PH p.194)
|
// Ranged weapons - Dex (PH p.194)
|
||||||
|
@ -63,7 +63,7 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 3 - unknown
|
// Case 3 - unknown
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -103,7 +103,7 @@ export default class Item5e extends Item {
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
get isHealing() {
|
get isHealing() {
|
||||||
return (this.data.data.actionType === "heal") && this.data.data.damage.parts.length;
|
return this.data.data.actionType === "heal" && this.data.data.damage.parts.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -136,7 +136,7 @@ export default class Item5e extends Item {
|
||||||
*/
|
*/
|
||||||
get hasAreaTarget() {
|
get hasAreaTarget() {
|
||||||
const target = this.data.data.target;
|
const target = this.data.data.target;
|
||||||
return target && (target.type in CONFIG.SW5E.areaTargetTypes);
|
return target && target.type in CONFIG.SW5E.areaTargetTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -148,7 +148,7 @@ export default class Item5e extends Item {
|
||||||
get hasLimitedUses() {
|
get hasLimitedUses() {
|
||||||
let chg = this.data.data.recharge || {};
|
let chg = this.data.data.recharge || {};
|
||||||
let uses = this.data.data.uses || {};
|
let uses = this.data.data.uses || {};
|
||||||
return !!chg.value || (uses.per && (uses.max > 0));
|
return !!chg.value || (uses.per && uses.max > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -165,7 +165,7 @@ export default class Item5e extends Item {
|
||||||
const itemData = this.data;
|
const itemData = this.data;
|
||||||
const data = itemData.data;
|
const data = itemData.data;
|
||||||
const C = CONFIG.SW5E;
|
const C = CONFIG.SW5E;
|
||||||
const labels = this.labels = {};
|
const labels = (this.labels = {});
|
||||||
|
|
||||||
// Classes
|
// Classes
|
||||||
if (itemData.type === "class") {
|
if (itemData.type === "class") {
|
||||||
|
@ -188,9 +188,12 @@ export default class Item5e extends Item {
|
||||||
// Feat Items
|
// Feat Items
|
||||||
else if (itemData.type === "feat") {
|
else if (itemData.type === "feat") {
|
||||||
const act = data.activation;
|
const act = data.activation;
|
||||||
if ( act && (act.type === C.abilityActivationTypes.legendary) ) labels.featType = game.i18n.localize("SW5E.LegendaryActionLabel");
|
if (act && act.type === C.abilityActivationTypes.legendary)
|
||||||
else if ( act && (act.type === C.abilityActivationTypes.lair) ) labels.featType = game.i18n.localize("SW5E.LairActionLabel");
|
labels.featType = game.i18n.localize("SW5E.LegendaryActionLabel");
|
||||||
else if ( act && act.type ) labels.featType = game.i18n.localize(data.damage.length ? "SW5E.Attack" : "SW5E.Action");
|
else if (act && act.type === C.abilityActivationTypes.lair)
|
||||||
|
labels.featType = game.i18n.localize("SW5E.LairActionLabel");
|
||||||
|
else if (act && act.type)
|
||||||
|
labels.featType = game.i18n.localize(data.damage.length ? "SW5E.Attack" : "SW5E.Action");
|
||||||
else labels.featType = game.i18n.localize("SW5E.Passive");
|
else labels.featType = game.i18n.localize("SW5E.Passive");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +242,6 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Activated Items
|
// Activated Items
|
||||||
if (data.hasOwnProperty("activation")) {
|
if (data.hasOwnProperty("activation")) {
|
||||||
|
|
||||||
// Ability Activation Label
|
// Ability Activation Label
|
||||||
let act = data.activation || {};
|
let act = data.activation || {};
|
||||||
if (act) labels.activation = [act.cost, C.abilityActivationTypes[act.type]].filterJoin(" ");
|
if (act) labels.activation = [act.cost, C.abilityActivationTypes[act.type]].filterJoin(" ");
|
||||||
|
@ -268,7 +270,9 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Recharge Label
|
// Recharge Label
|
||||||
let chg = data.recharge || {};
|
let chg = data.recharge || {};
|
||||||
labels.recharge = `${game.i18n.localize("SW5E.Recharge")} [${chg.value}${parseInt(chg.value) < 6 ? "+" : ""}]`;
|
labels.recharge = `${game.i18n.localize("SW5E.Recharge")} [${chg.value}${
|
||||||
|
parseInt(chg.value) < 6 ? "+" : ""
|
||||||
|
}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item Actions
|
// Item Actions
|
||||||
|
@ -276,8 +280,11 @@ export default class Item5e extends Item {
|
||||||
// Damage
|
// Damage
|
||||||
let dam = data.damage || {};
|
let dam = data.damage || {};
|
||||||
if (dam.parts) {
|
if (dam.parts) {
|
||||||
labels.damage = dam.parts.map(d => d[0]).join(" + ").replace(/\+ -/g, "- ");
|
labels.damage = dam.parts
|
||||||
labels.damageTypes = dam.parts.map(d => C.damageTypes[d[1]]).join(", ");
|
.map((d) => d[0])
|
||||||
|
.join(" + ")
|
||||||
|
.replace(/\+ -/g, "- ");
|
||||||
|
labels.damageTypes = dam.parts.map((d) => C.damageTypes[d[1]]).join(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,10 +330,10 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
const derivedDamage = itemData.damage?.parts?.map((damagePart) => ({
|
const derivedDamage = itemData.damage?.parts?.map((damagePart) => ({
|
||||||
formula: simplifyRollFormula(damagePart[0], rollData, {constantFirst: false}),
|
formula: simplifyRollFormula(damagePart[0], rollData, {constantFirst: false}),
|
||||||
damageType: damagePart[1],
|
damageType: damagePart[1]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.labels.derivedDamage = derivedDamage
|
this.labels.derivedDamage = derivedDamage;
|
||||||
|
|
||||||
return derivedDamage;
|
return derivedDamage;
|
||||||
}
|
}
|
||||||
|
@ -396,7 +403,7 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Include the item's innate attack bonus as the initial value and label
|
// Include the item's innate attack bonus as the initial value and label
|
||||||
if (itemData.attackBonus) {
|
if (itemData.attackBonus) {
|
||||||
parts.push(itemData.attackBonus)
|
parts.push(itemData.attackBonus);
|
||||||
this.labels.toHit = itemData.attackBonus;
|
this.labels.toHit = itemData.attackBonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,14 +423,15 @@ export default class Item5e extends Item {
|
||||||
if (actorBonus.attack) parts.push(actorBonus.attack);
|
if (actorBonus.attack) parts.push(actorBonus.attack);
|
||||||
|
|
||||||
// One-time bonus provided by consumed ammunition
|
// One-time bonus provided by consumed ammunition
|
||||||
if ( (itemData.consume?.type === 'ammo') && !!this.actor.items ) {
|
if (itemData.consume?.type === "ammo" && !!this.actor.items) {
|
||||||
const ammoItemData = this.actor.items.get(itemData.consume.target)?.data;
|
const ammoItemData = this.actor.items.get(itemData.consume.target)?.data;
|
||||||
|
|
||||||
if (ammoItemData) {
|
if (ammoItemData) {
|
||||||
const ammoItemQuantity = ammoItemData.data.quantity;
|
const ammoItemQuantity = ammoItemData.data.quantity;
|
||||||
const ammoCanBeConsumed = ammoItemQuantity && (ammoItemQuantity - (itemData.consume.amount ?? 0) >= 0);
|
const ammoCanBeConsumed = ammoItemQuantity && ammoItemQuantity - (itemData.consume.amount ?? 0) >= 0;
|
||||||
const ammoItemAttackBonus = ammoItemData.data.attackBonus;
|
const ammoItemAttackBonus = ammoItemData.data.attackBonus;
|
||||||
const ammoIsTypeConsumable = (ammoItemData.type === "consumable") && (ammoItemData.data.consumableType === "ammo")
|
const ammoIsTypeConsumable =
|
||||||
|
ammoItemData.type === "consumable" && ammoItemData.data.consumableType === "ammo";
|
||||||
if (ammoCanBeConsumed && ammoItemAttackBonus && ammoIsTypeConsumable) {
|
if (ammoCanBeConsumed && ammoItemAttackBonus && ammoIsTypeConsumable) {
|
||||||
parts.push("@ammo");
|
parts.push("@ammo");
|
||||||
rollData["ammo"] = ammoItemAttackBonus;
|
rollData["ammo"] = ammoItemAttackBonus;
|
||||||
|
@ -432,9 +440,9 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Condense the resulting attack bonus formula into a simplified label
|
// Condense the resulting attack bonus formula into a simplified label
|
||||||
let toHitLabel = simplifyRollFormula(parts.join('+'), rollData).trim();
|
let toHitLabel = simplifyRollFormula(parts.join("+"), rollData).trim();
|
||||||
if (toHitLabel.charAt(0) !== '-') {
|
if (toHitLabel.charAt(0) !== "-") {
|
||||||
toHitLabel = '+ ' + toHitLabel
|
toHitLabel = "+ " + toHitLabel;
|
||||||
}
|
}
|
||||||
this.labels.toHit = toHitLabel;
|
this.labels.toHit = toHitLabel;
|
||||||
|
|
||||||
|
@ -460,7 +468,7 @@ export default class Item5e extends Item {
|
||||||
max = Roll.replaceFormulaData(max, this.actor.getRollData(), {missing: 0, warn: true});
|
max = Roll.replaceFormulaData(max, this.actor.getRollData(), {missing: 0, warn: true});
|
||||||
max = Roll.safeEval(max);
|
max = Roll.safeEval(max);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Problem preparing Max uses for', this.data.name, e);
|
console.error("Problem preparing Max uses for", this.data.name, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,7 +499,7 @@ export default class Item5e extends Item {
|
||||||
const isPower = this.type === "power"; // Does the item require a power slot?
|
const isPower = this.type === "power"; // Does the item require a power slot?
|
||||||
// TODO: Possibly Mod this to not consume slots based on class?
|
// TODO: Possibly Mod this to not consume slots based on class?
|
||||||
// We could use this for feats and architypes that let a character cast one slot every rest or so
|
// We could use this for feats and architypes that let a character cast one slot every rest or so
|
||||||
const requirePowerSlot = isPower && (id.level > 0) && CONFIG.SW5E.powerUpcastModes.includes(id.preparation.mode);
|
const requirePowerSlot = isPower && id.level > 0 && CONFIG.SW5E.powerUpcastModes.includes(id.preparation.mode);
|
||||||
|
|
||||||
// Define follow-up actions resulting from the item usage
|
// Define follow-up actions resulting from the item usage
|
||||||
let createMeasuredTemplate = hasArea; // Trigger a template creation
|
let createMeasuredTemplate = hasArea; // Trigger a template creation
|
||||||
|
@ -504,12 +512,16 @@ export default class Item5e extends Item {
|
||||||
if (requirePowerSlot) consumePowerLevel = id.preparation.mode === "pact" ? "pact" : `power${id.level}`;
|
if (requirePowerSlot) consumePowerLevel = id.preparation.mode === "pact" ? "pact" : `power${id.level}`;
|
||||||
|
|
||||||
// Display a configuration dialog to customize the usage
|
// Display a configuration dialog to customize the usage
|
||||||
const needsConfiguration = createMeasuredTemplate || consumeRecharge || (consumeResource && !['simpleB', 'martialB'].includes(id.weaponType)) || consumePowerSlot || (consumeUsage && !['simpleB', 'martialB'].includes(id.weaponType));
|
const needsConfiguration =
|
||||||
|
createMeasuredTemplate ||
|
||||||
|
consumeRecharge ||
|
||||||
|
(consumeResource && !["simpleB", "martialB"].includes(id.weaponType)) ||
|
||||||
|
consumePowerSlot ||
|
||||||
|
(consumeUsage && !["simpleB", "martialB"].includes(id.weaponType));
|
||||||
if (configureDialog && needsConfiguration) {
|
if (configureDialog && needsConfiguration) {
|
||||||
const configuration = await AbilityUseDialog.create(this);
|
const configuration = await AbilityUseDialog.create(this);
|
||||||
if (!configuration) return;
|
if (!configuration) return;
|
||||||
|
|
||||||
|
|
||||||
// Determine consumption preferences
|
// Determine consumption preferences
|
||||||
createMeasuredTemplate = Boolean(configuration.placeTemplate);
|
createMeasuredTemplate = Boolean(configuration.placeTemplate);
|
||||||
consumeUsage = Boolean(configuration.consumeUse);
|
consumeUsage = Boolean(configuration.consumeUse);
|
||||||
|
@ -531,14 +543,20 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine whether the item can be used by testing for resource consumption
|
// Determine whether the item can be used by testing for resource consumption
|
||||||
const usage = item._getUsageUpdates({consumeRecharge, consumeResource, consumePowerLevel, consumeUsage, consumeQuantity});
|
const usage = item._getUsageUpdates({
|
||||||
|
consumeRecharge,
|
||||||
|
consumeResource,
|
||||||
|
consumePowerLevel,
|
||||||
|
consumeUsage,
|
||||||
|
consumeQuantity
|
||||||
|
});
|
||||||
if (!usage) return;
|
if (!usage) return;
|
||||||
|
|
||||||
const {actorUpdates, itemUpdates, resourceUpdates} = usage;
|
const {actorUpdates, itemUpdates, resourceUpdates} = usage;
|
||||||
|
|
||||||
// Commit pending data updates
|
// Commit pending data updates
|
||||||
if (!foundry.utils.isObjectEmpty(itemUpdates)) await item.update(itemUpdates);
|
if (!foundry.utils.isObjectEmpty(itemUpdates)) await item.update(itemUpdates);
|
||||||
if ( consumeQuantity && (item.data.data.quantity === 0) ) await item.delete();
|
if (consumeQuantity && item.data.data.quantity === 0) await item.delete();
|
||||||
if (!foundry.utils.isObjectEmpty(actorUpdates)) await actor.update(actorUpdates);
|
if (!foundry.utils.isObjectEmpty(actorUpdates)) await actor.update(actorUpdates);
|
||||||
if (!foundry.utils.isObjectEmpty(resourceUpdates)) {
|
if (!foundry.utils.isObjectEmpty(resourceUpdates)) {
|
||||||
const resource = actor.items.get(id.consume?.target);
|
const resource = actor.items.get(id.consume?.target);
|
||||||
|
@ -569,7 +587,6 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_getUsageUpdates({consumeQuantity, consumeRecharge, consumeResource, consumePowerLevel, consumeUsage}) {
|
_getUsageUpdates({consumeQuantity, consumeRecharge, consumeResource, consumePowerLevel, consumeUsage}) {
|
||||||
|
|
||||||
// Reference item data
|
// Reference item data
|
||||||
const id = this.data.data;
|
const id = this.data.data;
|
||||||
const actorUpdates = {};
|
const actorUpdates = {};
|
||||||
|
@ -598,8 +615,8 @@ export default class Item5e extends Item {
|
||||||
const level = this.actor?.data.data.powers[consumePowerLevel];
|
const level = this.actor?.data.data.powers[consumePowerLevel];
|
||||||
const fp = this.actor.data.data.attributes.force.points;
|
const fp = this.actor.data.data.attributes.force.points;
|
||||||
const tp = this.actor.data.data.attributes.tech.points;
|
const tp = this.actor.data.data.attributes.tech.points;
|
||||||
const powerCost = parseInt(id.level,10) + 1;
|
const powerCost = id.level + 1;
|
||||||
const innatePower = this.actor.data.data.attributes.powercasting === 'innate';
|
const innatePower = this.actor.data.data.attributes.powercasting === "innate";
|
||||||
if (!innatePower) {
|
if (!innatePower) {
|
||||||
switch (id.school) {
|
switch (id.school) {
|
||||||
case "lgt":
|
case "lgt":
|
||||||
|
@ -608,7 +625,9 @@ export default class Item5e extends Item {
|
||||||
const powers = Number(level?.fvalue ?? 0);
|
const powers = Number(level?.fvalue ?? 0);
|
||||||
if (powers === 0) {
|
if (powers === 0) {
|
||||||
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`);
|
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`);
|
||||||
ui.notifications.warn(game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label}));
|
ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label})
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
actorUpdates[`data.powers.${consumePowerLevel}.fvalue`] = Math.max(powers - 1, 0);
|
actorUpdates[`data.powers.${consumePowerLevel}.fvalue`] = Math.max(powers - 1, 0);
|
||||||
|
@ -624,7 +643,9 @@ export default class Item5e extends Item {
|
||||||
const powers = Number(level?.tvalue ?? 0);
|
const powers = Number(level?.tvalue ?? 0);
|
||||||
if (powers === 0) {
|
if (powers === 0) {
|
||||||
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`);
|
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`);
|
||||||
ui.notifications.warn(game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label}));
|
ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label})
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
actorUpdates[`data.powers.${consumePowerLevel}.tvalue`] = Math.max(powers - 1, 0);
|
actorUpdates[`data.powers.${consumePowerLevel}.tvalue`] = Math.max(powers - 1, 0);
|
||||||
|
@ -640,7 +661,6 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Consume Limited Usage
|
// Consume Limited Usage
|
||||||
if (consumeUsage) {
|
if (consumeUsage) {
|
||||||
const uses = id.uses || {};
|
const uses = id.uses || {};
|
||||||
|
@ -655,7 +675,7 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reduce quantity if not reducing usages or if usages hit 0 and we are set to consumeQuantity
|
// Reduce quantity if not reducing usages or if usages hit 0 and we are set to consumeQuantity
|
||||||
if ( consumeQuantity && (!used || (remaining === 0)) ) {
|
if (consumeQuantity && (!used || remaining === 0)) {
|
||||||
const q = Number(id.quantity ?? 1);
|
const q = Number(id.quantity ?? 1);
|
||||||
if (q >= 1) {
|
if (q >= 1) {
|
||||||
used = true;
|
used = true;
|
||||||
|
@ -694,7 +714,9 @@ export default class Item5e extends Item {
|
||||||
// No consumed target
|
// No consumed target
|
||||||
const typeLabel = CONFIG.SW5E.abilityConsumptionTypes[consume.type];
|
const typeLabel = CONFIG.SW5E.abilityConsumptionTypes[consume.type];
|
||||||
if (!consume.target) {
|
if (!consume.target) {
|
||||||
ui.notifications.warn(game.i18n.format("SW5E.ConsumeWarningNoResource", {name: this.name, type: typeLabel}));
|
ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.ConsumeWarningNoResource", {name: this.name, type: typeLabel})
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,7 +755,9 @@ export default class Item5e extends Item {
|
||||||
// Verify that the required quantity is available
|
// Verify that the required quantity is available
|
||||||
let remaining = quantity - amount;
|
let remaining = quantity - amount;
|
||||||
if (remaining < 0) {
|
if (remaining < 0) {
|
||||||
ui.notifications.warn(game.i18n.format("SW5E.ConsumeWarningNoQuantity", {name: this.name, type: typeLabel}));
|
ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.ConsumeWarningNoQuantity", {name: this.name, type: typeLabel})
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,7 +789,6 @@ export default class Item5e extends Item {
|
||||||
* the prepared message data (if false)
|
* the prepared message data (if false)
|
||||||
*/
|
*/
|
||||||
async displayCard({rollMode, createMessage = true} = {}) {
|
async displayCard({rollMode, createMessage = true} = {}) {
|
||||||
|
|
||||||
// Render the chat card template
|
// Render the chat card template
|
||||||
const token = this.actor.token;
|
const token = this.actor.token;
|
||||||
const templateData = {
|
const templateData = {
|
||||||
|
@ -796,7 +819,7 @@ export default class Item5e extends Item {
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the Item was destroyed in the process of displaying its card - embed the item data in the chat message
|
// If the Item was destroyed in the process of displaying its card - embed the item data in the chat message
|
||||||
if ( (this.data.type === "consumable") && !this.actor.items.has(this.id) ) {
|
if (this.data.type === "consumable" && !this.actor.items.has(this.id)) {
|
||||||
chatData.flags["sw5e.itemData"] = this.data;
|
chatData.flags["sw5e.itemData"] = this.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,10 +853,11 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Equipment properties
|
// Equipment properties
|
||||||
if (data.hasOwnProperty("equipped") && !["loot", "tool"].includes(this.data.type)) {
|
if (data.hasOwnProperty("equipped") && !["loot", "tool"].includes(this.data.type)) {
|
||||||
if ( data.attunement === CONFIG.SW5E.attunementTypes.REQUIRED ) props.push(game.i18n.localize(CONFIG.SW5E.attunements[CONFIG.SW5E.attunementTypes.REQUIRED]));
|
if (data.attunement === CONFIG.SW5E.attunementTypes.REQUIRED)
|
||||||
|
props.push(game.i18n.localize(CONFIG.SW5E.attunements[CONFIG.SW5E.attunementTypes.REQUIRED]));
|
||||||
props.push(
|
props.push(
|
||||||
game.i18n.localize(data.equipped ? "SW5E.Equipped" : "SW5E.Unequipped"),
|
game.i18n.localize(data.equipped ? "SW5E.Equipped" : "SW5E.Unequipped"),
|
||||||
game.i18n.localize(data.proficient ? "SW5E.Proficient" : "SW5E.NotProficient"),
|
game.i18n.localize(data.proficient ? "SW5E.Proficient" : "SW5E.NotProficient")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,7 +872,7 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter properties and return
|
// Filter properties and return
|
||||||
data.properties = props.filter(p => !!p);
|
data.properties = props.filter((p) => !!p);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,9 +897,7 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_weaponChatData(data, labels, props) {
|
_weaponChatData(data, labels, props) {
|
||||||
props.push(
|
props.push(CONFIG.SW5E.weaponTypes[data.weaponType]);
|
||||||
CONFIG.SW5E.weaponTypes[data.weaponType],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -899,10 +921,7 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_toolChatData(data, labels, props) {
|
_toolChatData(data, labels, props) {
|
||||||
props.push(
|
props.push(CONFIG.SW5E.abilities[data.ability] || null, CONFIG.SW5E.proficiencyLevels[data.proficient || 0]);
|
||||||
CONFIG.SW5E.abilities[data.ability] || null,
|
|
||||||
CONFIG.SW5E.proficiencyLevels[data.proficient || 0]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -926,10 +945,7 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_powerChatData(data, labels, props) {
|
_powerChatData(data, labels, props) {
|
||||||
props.push(
|
props.push(labels.level, labels.components + (labels.materials ? ` (${labels.materials})` : ""));
|
||||||
labels.level,
|
|
||||||
labels.components + (labels.materials ? ` (${labels.materials})` : "")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -974,7 +990,7 @@ export default class Item5e extends Item {
|
||||||
if (ammo?.data) {
|
if (ammo?.data) {
|
||||||
const q = ammo.data.data.quantity;
|
const q = ammo.data.data.quantity;
|
||||||
const consumeAmount = consume.amount ?? 0;
|
const consumeAmount = consume.amount ?? 0;
|
||||||
if ( q && (q - consumeAmount >= 0) ) {
|
if (q && q - consumeAmount >= 0) {
|
||||||
this._ammo = ammo;
|
this._ammo = ammo;
|
||||||
title += ` [${ammo.name}]`;
|
title += ` [${ammo.name}]`;
|
||||||
}
|
}
|
||||||
|
@ -987,7 +1003,8 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose roll options
|
// Compose roll options
|
||||||
const rollConfig = mergeObject({
|
const rollConfig = mergeObject(
|
||||||
|
{
|
||||||
parts: parts,
|
parts: parts,
|
||||||
actor: this.actor,
|
actor: this.actor,
|
||||||
data: rollData,
|
data: rollData,
|
||||||
|
@ -1000,13 +1017,15 @@ export default class Item5e extends Item {
|
||||||
left: window.innerWidth - 710
|
left: window.innerWidth - 710
|
||||||
},
|
},
|
||||||
messageData: {"flags.sw5e.roll": {type: "attack", itemId: this.id}}
|
messageData: {"flags.sw5e.roll": {type: "attack", itemId: this.id}}
|
||||||
}, options);
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
rollConfig.event = options.event;
|
rollConfig.event = options.event;
|
||||||
|
|
||||||
// Expanded critical hit thresholds
|
// Expanded critical hit thresholds
|
||||||
if (( this.data.type === "weapon" ) && flags.weaponCriticalThreshold) {
|
if (this.data.type === "weapon" && flags.weaponCriticalThreshold) {
|
||||||
rollConfig.critical = parseInt(flags.weaponCriticalThreshold);
|
rollConfig.critical = parseInt(flags.weaponCriticalThreshold);
|
||||||
} else if (( this.data.type === "power" ) && flags.powerCriticalThreshold) {
|
} else if (this.data.type === "power" && flags.powerCriticalThreshold) {
|
||||||
rollConfig.critical = parseInt(flags.powerCriticalThreshold);
|
rollConfig.critical = parseInt(flags.powerCriticalThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1015,7 +1034,6 @@ export default class Item5e extends Item {
|
||||||
rollConfig.elvenAccuracy = true;
|
rollConfig.elvenAccuracy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Apply Halfling Lucky
|
// Apply Halfling Lucky
|
||||||
if (flags.halflingLucky) rollConfig.halflingLucky = true;
|
if (flags.halflingLucky) rollConfig.halflingLucky = true;
|
||||||
|
|
||||||
|
@ -1047,7 +1065,7 @@ export default class Item5e extends Item {
|
||||||
const messageData = {"flags.sw5e.roll": {type: "damage", itemId: this.id}};
|
const messageData = {"flags.sw5e.roll": {type: "damage", itemId: this.id}};
|
||||||
|
|
||||||
// Get roll data
|
// Get roll data
|
||||||
const parts = itemData.damage.parts.map(d => d[0]);
|
const parts = itemData.damage.parts.map((d) => d[0]);
|
||||||
const rollData = this.getRollData();
|
const rollData = this.getRollData();
|
||||||
if (powerLevel) rollData.item.level = powerLevel;
|
if (powerLevel) rollData.item.level = powerLevel;
|
||||||
|
|
||||||
|
@ -1079,12 +1097,12 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale damage from up-casting powers
|
// Scale damage from up-casting powers
|
||||||
if ( (this.data.type === "power") ) {
|
if (this.data.type === "power") {
|
||||||
if ( (itemData.scaling.mode === "atwill") ) {
|
if (itemData.scaling.mode === "atwill") {
|
||||||
const level = this.actor.data.type === "character" ? actorData.details.level : actorData.details.powerLevel;
|
const level =
|
||||||
|
this.actor.data.type === "character" ? actorData.details.level : actorData.details.powerLevel;
|
||||||
this._scaleAtWillDamage(parts, itemData.scaling.formula, level, rollData);
|
this._scaleAtWillDamage(parts, itemData.scaling.formula, level, rollData);
|
||||||
}
|
} else if (powerLevel && itemData.scaling.mode === "level" && itemData.scaling.formula) {
|
||||||
else if ( powerLevel && (itemData.scaling.mode === "level") && itemData.scaling.formula ) {
|
|
||||||
const scaling = itemData.scaling.formula;
|
const scaling = itemData.scaling.formula;
|
||||||
this._scalePowerDamage(parts, itemData.level, powerLevel, scaling, rollData);
|
this._scalePowerDamage(parts, itemData.level, powerLevel, scaling, rollData);
|
||||||
}
|
}
|
||||||
|
@ -1092,7 +1110,7 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Add damage bonus formula
|
// Add damage bonus formula
|
||||||
const actorBonus = getProperty(actorData, `bonuses.${itemData.actionType}`) || {};
|
const actorBonus = getProperty(actorData, `bonuses.${itemData.actionType}`) || {};
|
||||||
if ( actorBonus.damage && (parseInt(actorBonus.damage) !== 0) ) {
|
if (actorBonus.damage && parseInt(actorBonus.damage) !== 0) {
|
||||||
parts.push(actorBonus.damage);
|
parts.push(actorBonus.damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1100,9 +1118,9 @@ export default class Item5e extends Item {
|
||||||
const ammoData = this._ammo?.data;
|
const ammoData = this._ammo?.data;
|
||||||
|
|
||||||
// only add the ammunition damage if the ammution is a consumable with type 'ammo'
|
// only add the ammunition damage if the ammution is a consumable with type 'ammo'
|
||||||
if ( this._ammo && (ammoData.type === "consumable") && (ammoData.data.consumableType === "ammo") ) {
|
if (this._ammo && ammoData.type === "consumable" && ammoData.data.consumableType === "ammo") {
|
||||||
parts.push("@ammo");
|
parts.push("@ammo");
|
||||||
rollData["ammo"] = ammoData.data.damage.parts.map(p => p[0]).join("+");
|
rollData["ammo"] = ammoData.data.damage.parts.map((p) => p[0]).join("+");
|
||||||
rollConfig.flavor += ` [${this._ammo.name}]`;
|
rollConfig.flavor += ` [${this._ammo.name}]`;
|
||||||
delete this._ammo;
|
delete this._ammo;
|
||||||
}
|
}
|
||||||
|
@ -1164,10 +1182,10 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Attempt to simplify by combining like dice terms
|
// Attempt to simplify by combining like dice terms
|
||||||
let simplified = false;
|
let simplified = false;
|
||||||
if ( (s.terms[0] instanceof Die) && (s.terms.length === 1) ) {
|
if (s.terms[0] instanceof Die && s.terms.length === 1) {
|
||||||
const d0 = p0.terms[0];
|
const d0 = p0.terms[0];
|
||||||
const s0 = s.terms[0];
|
const s0 = s.terms[0];
|
||||||
if ( (d0 instanceof Die) && (d0.faces === s0.faces) && d0.modifiers.equals(s0.modifiers) ) {
|
if (d0 instanceof Die && d0.faces === s0.faces && d0.modifiers.equals(s0.modifiers)) {
|
||||||
d0.number += s0.number;
|
d0.number += s0.number;
|
||||||
parts[0] = p0.formula;
|
parts[0] = p0.formula;
|
||||||
simplified = true;
|
simplified = true;
|
||||||
|
@ -1225,10 +1243,14 @@ export default class Item5e extends Item {
|
||||||
const success = roll.total >= parseInt(data.recharge.value);
|
const success = roll.total >= parseInt(data.recharge.value);
|
||||||
|
|
||||||
// Display a Chat Message
|
// Display a Chat Message
|
||||||
const promises = [roll.toMessage({
|
const promises = [
|
||||||
flavor: `${game.i18n.format("SW5E.ItemRechargeCheck", {name: this.name})} - ${game.i18n.localize(success ? "SW5E.ItemRechargeSuccess" : "SW5E.ItemRechargeFailure")}`,
|
roll.toMessage({
|
||||||
|
flavor: `${game.i18n.format("SW5E.ItemRechargeCheck", {name: this.name})} - ${game.i18n.localize(
|
||||||
|
success ? "SW5E.ItemRechargeSuccess" : "SW5E.ItemRechargeFailure"
|
||||||
|
)}`,
|
||||||
speaker: ChatMessage.getSpeaker({actor: this.actor, token: this.actor.token})
|
speaker: ChatMessage.getSpeaker({actor: this.actor, token: this.actor.token})
|
||||||
})];
|
})
|
||||||
|
];
|
||||||
|
|
||||||
// Update the Item data
|
// Update the Item data
|
||||||
if (success) promises.push(this.update({"data.recharge.charged": true}));
|
if (success) promises.push(this.update({"data.recharge.charged": true}));
|
||||||
|
@ -1258,7 +1280,8 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose the roll data
|
// Compose the roll data
|
||||||
const rollConfig = mergeObject({
|
const rollConfig = mergeObject(
|
||||||
|
{
|
||||||
parts: parts,
|
parts: parts,
|
||||||
data: rollData,
|
data: rollData,
|
||||||
title: title,
|
title: title,
|
||||||
|
@ -1267,13 +1290,15 @@ export default class Item5e extends Item {
|
||||||
dialogOptions: {
|
dialogOptions: {
|
||||||
width: 400,
|
width: 400,
|
||||||
top: options.event ? options.event.clientY - 80 : null,
|
top: options.event ? options.event.clientY - 80 : null,
|
||||||
left: window.innerWidth - 710,
|
left: window.innerWidth - 710
|
||||||
},
|
},
|
||||||
chooseModifier: true,
|
chooseModifier: true,
|
||||||
halflingLucky: this.actor.getFlag("sw5e", "halflingLucky") || false,
|
halflingLucky: this.actor.getFlag("sw5e", "halflingLucky") || false,
|
||||||
reliableTalent: (this.data.data.proficient >= 1) && this.actor.getFlag("sw5e", "reliableTalent"),
|
reliableTalent: this.data.data.proficient >= 1 && this.actor.getFlag("sw5e", "reliableTalent"),
|
||||||
messageData: {"flags.sw5e.roll": {type: "tool", itemId: this.id}}
|
messageData: {"flags.sw5e.roll": {type: "tool", itemId: this.id}}
|
||||||
}, options);
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
rollConfig.event = options.event;
|
rollConfig.event = options.event;
|
||||||
|
|
||||||
// Call the roll helper utility
|
// Call the roll helper utility
|
||||||
|
@ -1296,13 +1321,15 @@ export default class Item5e extends Item {
|
||||||
if (abl) {
|
if (abl) {
|
||||||
const ability = rollData.abilities[abl];
|
const ability = rollData.abilities[abl];
|
||||||
if (!ability) {
|
if (!ability) {
|
||||||
console.warn(`Item ${this.name} in Actor ${this.actor.name} has an invalid item ability modifier of ${abl} defined`);
|
console.warn(
|
||||||
|
`Item ${this.name} in Actor ${this.actor.name} has an invalid item ability modifier of ${abl} defined`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
rollData["mod"] = ability?.mod || 0;
|
rollData["mod"] = ability?.mod || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include a proficiency score
|
// Include a proficiency score
|
||||||
const prof = ("proficient" in rollData.item) ? (rollData.item.proficient || 0) : 1;
|
const prof = "proficient" in rollData.item ? rollData.item.proficient || 0 : 1;
|
||||||
rollData["prof"] = Math.floor(prof * (rollData.attributes.prof || 0));
|
rollData["prof"] = Math.floor(prof * (rollData.attributes.prof || 0));
|
||||||
return rollData;
|
return rollData;
|
||||||
}
|
}
|
||||||
|
@ -1312,8 +1339,8 @@ export default class Item5e extends Item {
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
static chatListeners(html) {
|
static chatListeners(html) {
|
||||||
html.on('click', '.card-buttons button', this._onChatCardAction.bind(this));
|
html.on("click", ".card-buttons button", this._onChatCardAction.bind(this));
|
||||||
html.on('click', '.item-name', this._onChatCardToggleContent.bind(this));
|
html.on("click", ".item-name", this._onChatCardToggleContent.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -1347,14 +1374,17 @@ export default class Item5e extends Item {
|
||||||
const storedData = message.getFlag("sw5e", "itemData");
|
const storedData = message.getFlag("sw5e", "itemData");
|
||||||
const item = storedData ? new this(storedData, {parent: actor}) : actor.items.get(card.dataset.itemId);
|
const item = storedData ? new this(storedData, {parent: actor}) : actor.items.get(card.dataset.itemId);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return ui.notifications.error(game.i18n.format("SW5E.ActionWarningNoItem", {item: card.dataset.itemId, name: actor.name}))
|
return ui.notifications.error(
|
||||||
|
game.i18n.format("SW5E.ActionWarningNoItem", {item: card.dataset.itemId, name: actor.name})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const powerLevel = parseInt(card.dataset.powerLevel) || null;
|
const powerLevel = parseInt(card.dataset.powerLevel) || null;
|
||||||
|
|
||||||
// Handle different actions
|
// Handle different actions
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "attack":
|
case "attack":
|
||||||
await item.rollAttack({event}); break;
|
await item.rollAttack({event});
|
||||||
|
break;
|
||||||
case "damage":
|
case "damage":
|
||||||
case "versatile":
|
case "versatile":
|
||||||
await item.rollDamage({
|
await item.rollDamage({
|
||||||
|
@ -1365,7 +1395,8 @@ export default class Item5e extends Item {
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "formula":
|
case "formula":
|
||||||
await item.rollFormula({event, powerLevel}); break;
|
await item.rollFormula({event, powerLevel});
|
||||||
|
break;
|
||||||
case "save":
|
case "save":
|
||||||
const targets = this._getChatCardTargets(card);
|
const targets = this._getChatCardTargets(card);
|
||||||
for (let token of targets) {
|
for (let token of targets) {
|
||||||
|
@ -1374,7 +1405,8 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "toolCheck":
|
case "toolCheck":
|
||||||
await item.rollToolCheck({event}); break;
|
await item.rollToolCheck({event});
|
||||||
|
break;
|
||||||
case "placeTemplate":
|
case "placeTemplate":
|
||||||
const template = game.sw5e.canvas.AbilityTemplate.fromItem(item);
|
const template = game.sw5e.canvas.AbilityTemplate.fromItem(item);
|
||||||
if (template) template.drawPreview();
|
if (template) template.drawPreview();
|
||||||
|
@ -1409,7 +1441,6 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static async _getChatCardActor(card) {
|
static async _getChatCardActor(card) {
|
||||||
|
|
||||||
// Case 1 - a synthetic actor from a Token
|
// Case 1 - a synthetic actor from a Token
|
||||||
if (card.dataset.tokenId) {
|
if (card.dataset.tokenId) {
|
||||||
const token = await fromUuid(card.dataset.tokenId);
|
const token = await fromUuid(card.dataset.tokenId);
|
||||||
|
@ -1431,7 +1462,7 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static _getChatCardTargets(card) {
|
static _getChatCardTargets(card) {
|
||||||
let targets = canvas.tokens.controlled.filter(t => !!t.actor);
|
let targets = canvas.tokens.controlled.filter((t) => !!t.actor);
|
||||||
if (!targets.length && game.user.character) targets = targets.concat(game.user.character.getActiveTokens());
|
if (!targets.length && game.user.character) targets = targets.concat(game.user.character.getActiveTokens());
|
||||||
if (!targets.length) ui.notifications.warn(game.i18n.localize("SW5E.ActionWarningNoToken"));
|
if (!targets.length) ui.notifications.warn(game.i18n.localize("SW5E.ActionWarningNoToken"));
|
||||||
return targets;
|
return targets;
|
||||||
|
@ -1444,7 +1475,7 @@ export default class Item5e extends Item {
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
await super._preCreate(data, options, user);
|
await super._preCreate(data, options, user);
|
||||||
if ( !this.isEmbedded || (this.parent.type === "vehicle") ) return;
|
if (!this.isEmbedded || this.parent.type === "vehicle") return;
|
||||||
const actorData = this.parent.data;
|
const actorData = this.parent.data;
|
||||||
const isNPC = this.parent.type === "npc";
|
const isNPC = this.parent.type === "npc";
|
||||||
let updates;
|
let updates;
|
||||||
|
@ -1470,7 +1501,7 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// The below options are only needed for character classes
|
// The below options are only needed for character classes
|
||||||
if (userId !== game.user.id) return;
|
if (userId !== game.user.id) return;
|
||||||
const isCharacterClass = this.parent && (this.parent.type !== "vehicle") && (this.type === "class");
|
const isCharacterClass = this.parent && this.parent.type !== "vehicle" && this.type === "class";
|
||||||
if (!isCharacterClass) return;
|
if (!isCharacterClass) return;
|
||||||
|
|
||||||
// Assign a new primary class
|
// Assign a new primary class
|
||||||
|
@ -1479,11 +1510,13 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Prompt to add new class features
|
// Prompt to add new class features
|
||||||
if (options.addFeatures === false) return;
|
if (options.addFeatures === false) return;
|
||||||
this.parent.getClassFeatures({
|
this.parent
|
||||||
|
.getClassFeatures({
|
||||||
className: this.name,
|
className: this.name,
|
||||||
archetypeName: this.data.data.archetype,
|
archetypeName: this.data.data.archetype,
|
||||||
level: this.data.data.levels
|
level: this.data.data.levels
|
||||||
}).then(features => {
|
})
|
||||||
|
.then((features) => {
|
||||||
return this.parent.addEmbeddedItems(features, options.promptAddFeatures);
|
return this.parent.addEmbeddedItems(features, options.promptAddFeatures);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1496,17 +1529,19 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// The below options are only needed for character classes
|
// The below options are only needed for character classes
|
||||||
if (userId !== game.user.id) return;
|
if (userId !== game.user.id) return;
|
||||||
const isCharacterClass = this.parent && (this.parent.type !== "vehicle") && (this.type === "class");
|
const isCharacterClass = this.parent && this.parent.type !== "vehicle" && this.type === "class";
|
||||||
if (!isCharacterClass) return;
|
if (!isCharacterClass) return;
|
||||||
|
|
||||||
// Prompt to add new class features
|
// Prompt to add new class features
|
||||||
const addFeatures = changed["name"] || (changed.data && ["archetype", "levels"].some(k => k in changed.data));
|
const addFeatures = changed["name"] || (changed.data && ["archetype", "levels"].some((k) => k in changed.data));
|
||||||
if ( !addFeatures || (options.addFeatures === false) ) return;
|
if (!addFeatures || options.addFeatures === false) return;
|
||||||
this.parent.getClassFeatures({
|
this.parent
|
||||||
|
.getClassFeatures({
|
||||||
className: changed.name || this.name,
|
className: changed.name || this.name,
|
||||||
archetypeName: changed.data?.archetype || this.data.data.archetype,
|
archetypeName: changed.data?.archetype || this.data.data.archetype,
|
||||||
level: changed.data?.levels || this.data.data.levels
|
level: changed.data?.levels || this.data.data.levels
|
||||||
}).then(features => {
|
})
|
||||||
|
.then((features) => {
|
||||||
return this.parent.addEmbeddedItems(features, options.promptAddFeatures);
|
return this.parent.addEmbeddedItems(features, options.promptAddFeatures);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1518,7 +1553,7 @@ export default class Item5e extends Item {
|
||||||
super._onDelete(options, userId);
|
super._onDelete(options, userId);
|
||||||
|
|
||||||
// Assign a new primary class
|
// Assign a new primary class
|
||||||
if ( this.parent && (this.type === "class") && (userId === game.user.id) ) {
|
if (this.parent && this.type === "class" && userId === game.user.id) {
|
||||||
if (this.id !== this.parent.data.data.details.originalClass) return;
|
if (this.id !== this.parent.data.data.details.originalClass) return;
|
||||||
this.parent._assignPrimaryClass();
|
this.parent._assignPrimaryClass();
|
||||||
}
|
}
|
||||||
|
@ -1541,7 +1576,7 @@ export default class Item5e extends Item {
|
||||||
} else {
|
} else {
|
||||||
const armorProf = CONFIG.SW5E.armorProficienciesMap[data.data?.armor?.type]; // Player characters check proficiency
|
const armorProf = CONFIG.SW5E.armorProficienciesMap[data.data?.armor?.type]; // Player characters check proficiency
|
||||||
const actorArmorProfs = actorData.data.traits?.armorProf?.value || [];
|
const actorArmorProfs = actorData.data.traits?.armorProf?.value || [];
|
||||||
updates["data.proficient"] = (armorProf === true) || actorArmorProfs.includes(armorProf);
|
updates["data.proficient"] = armorProf === true || actorArmorProfs.includes(armorProf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return updates;
|
return updates;
|
||||||
|
@ -1577,7 +1612,7 @@ export default class Item5e extends Item {
|
||||||
// TODO: With the changes to make weapon proficiencies more verbose, this may need revising
|
// TODO: With the changes to make weapon proficiencies more verbose, this may need revising
|
||||||
const weaponProf = CONFIG.SW5E.weaponProficienciesMap[data.data?.weaponType]; // Player characters check proficiency
|
const weaponProf = CONFIG.SW5E.weaponProficienciesMap[data.data?.weaponType]; // Player characters check proficiency
|
||||||
const actorWeaponProfs = actorData.data.traits?.weaponProf?.value || [];
|
const actorWeaponProfs = actorData.data.traits?.weaponProf?.value || [];
|
||||||
updates["data.proficient"] = (weaponProf === true) || actorWeaponProfs.includes(weaponProf);
|
updates["data.proficient"] = weaponProf === true || actorWeaponProfs.includes(weaponProf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return updates;
|
return updates;
|
||||||
|
@ -1593,10 +1628,10 @@ export default class Item5e extends Item {
|
||||||
* @return {Item5e} The created scroll consumable item
|
* @return {Item5e} The created scroll consumable item
|
||||||
*/
|
*/
|
||||||
static async createScrollFromPower(power) {
|
static async createScrollFromPower(power) {
|
||||||
|
|
||||||
// Get power data
|
// Get power data
|
||||||
const itemData = power instanceof Item5e ? power.data : power;
|
const itemData = power instanceof Item5e ? power.data : power;
|
||||||
const {actionType, description, source, activation, duration, target, range, damage, save, level} = itemData.data;
|
const {actionType, description, source, activation, duration, target, range, damage, save, level} =
|
||||||
|
itemData.data;
|
||||||
|
|
||||||
// Get scroll data
|
// Get scroll data
|
||||||
const scrollUuid = `Compendium.${CONFIG.SW5E.sourcePacks.ITEMS}.${CONFIG.SW5E.powerScrollIds[level]}`;
|
const scrollUuid = `Compendium.${CONFIG.SW5E.sourcePacks.ITEMS}.${CONFIG.SW5E.powerScrollIds[level]}`;
|
||||||
|
@ -1606,7 +1641,7 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Split the scroll description into an intro paragraph and the remaining details
|
// Split the scroll description into an intro paragraph and the remaining details
|
||||||
const scrollDescription = scrollData.data.description.value;
|
const scrollDescription = scrollData.data.description.value;
|
||||||
const pdel = '</p>';
|
const pdel = "</p>";
|
||||||
const scrollIntroEnd = scrollDescription.indexOf(pdel);
|
const scrollIntroEnd = scrollDescription.indexOf(pdel);
|
||||||
const scrollIntro = scrollDescription.slice(0, scrollIntroEnd + pdel.length);
|
const scrollIntro = scrollDescription.slice(0, scrollIntroEnd + pdel.length);
|
||||||
const scrollDetails = scrollDescription.slice(scrollIntroEnd + pdel.length);
|
const scrollDetails = scrollDescription.slice(scrollIntroEnd + pdel.length);
|
||||||
|
|
|
@ -109,7 +109,7 @@ export default class ItemSheet5e extends ItemSheet {
|
||||||
// Attributes
|
// Attributes
|
||||||
else if (consume.type === "attribute") {
|
else if (consume.type === "attribute") {
|
||||||
const attributes = TokenDocument.getTrackedAttributes(actor.data.data);
|
const attributes = TokenDocument.getTrackedAttributes(actor.data.data);
|
||||||
attributes.bar.forEach(a => a.push("value"));
|
attributes.bar.forEach((a) => a.push("value"));
|
||||||
return attributes.bar.concat(attributes.value).reduce((obj, a) => {
|
return attributes.bar.concat(attributes.value).reduce((obj, a) => {
|
||||||
let k = a.join(".");
|
let k = a.join(".");
|
||||||
obj[k] = k;
|
obj[k] = k;
|
||||||
|
@ -136,7 +136,10 @@ export default class ItemSheet5e extends ItemSheet {
|
||||||
const label =
|
const label =
|
||||||
uses.per === "charges"
|
uses.per === "charges"
|
||||||
? ` (${game.i18n.format("SW5E.AbilityUseChargesLabel", {value: uses.value})})`
|
? ` (${game.i18n.format("SW5E.AbilityUseChargesLabel", {value: uses.value})})`
|
||||||
: ` (${game.i18n.format("SW5E.AbilityUseConsumableLabel", { max: uses.max, per: uses.per })})`;
|
: ` (${game.i18n.format("SW5E.AbilityUseConsumableLabel", {
|
||||||
|
max: uses.max,
|
||||||
|
per: uses.per
|
||||||
|
})})`;
|
||||||
obj[i.id] = i.name + label;
|
obj[i.id] = i.name + label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +286,10 @@ export default class ItemSheet5e extends ItemSheet {
|
||||||
html.find(".damage-control").click(this._onDamageControl.bind(this));
|
html.find(".damage-control").click(this._onDamageControl.bind(this));
|
||||||
html.find(".trait-selector.class-skills").click(this._onConfigureTraits.bind(this));
|
html.find(".trait-selector.class-skills").click(this._onConfigureTraits.bind(this));
|
||||||
html.find(".effect-control").click((ev) => {
|
html.find(".effect-control").click((ev) => {
|
||||||
if (this.item.isOwned) return ui.notifications.warn("Managing Active Effects within an Owned Item is not currently supported and will be added in a subsequent update.");
|
if (this.item.isOwned)
|
||||||
|
return ui.notifications.warn(
|
||||||
|
"Managing Active Effects within an Owned Item is not currently supported and will be added in a subsequent update."
|
||||||
|
);
|
||||||
onManageActiveEffect(ev, this.item);
|
onManageActiveEffect(ev, this.item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -337,14 +343,17 @@ export default class ItemSheet5e extends ItemSheet {
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (a.dataset.options) {
|
switch (a.dataset.options) {
|
||||||
case 'saves':
|
case "saves":
|
||||||
options.choices = CONFIG.SW5E.abilities;
|
options.choices = CONFIG.SW5E.abilities;
|
||||||
options.valueKey = null;
|
options.valueKey = null;
|
||||||
break;
|
break;
|
||||||
case 'skills':
|
case "skills":
|
||||||
const skills = this.item.data.data.skills;
|
const skills = this.item.data.data.skills;
|
||||||
const choiceSet = skills.choices && skills.choices.length ? skills.choices : Object.keys(CONFIG.SW5E.skills);
|
const choiceSet =
|
||||||
options.choices = Object.fromEntries(Object.entries(CONFIG.SW5E.skills).filter(skill => choiceSet.includes(skill[0])));
|
skills.choices && skills.choices.length ? skills.choices : Object.keys(CONFIG.SW5E.skills);
|
||||||
|
options.choices = Object.fromEntries(
|
||||||
|
Object.entries(CONFIG.SW5E.skills).filter((skill) => choiceSet.includes(skill[0]))
|
||||||
|
);
|
||||||
options.maximum = skills.number;
|
options.maximum = skills.number;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Hotbar Macros */
|
/* Hotbar Macros */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -17,7 +16,7 @@ export async function create5eMacro(data, slot) {
|
||||||
|
|
||||||
// Create the macro command
|
// Create the macro command
|
||||||
const command = `game.sw5e.rollItemMacro("${item.name}");`;
|
const command = `game.sw5e.rollItemMacro("${item.name}");`;
|
||||||
let macro = game.macros.entities.find(m => (m.name === item.name) && (m.command === command));
|
let macro = game.macros.entities.find((m) => m.name === item.name && m.command === command);
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
macro = await Macro.create({
|
macro = await Macro.create({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
|
@ -46,9 +45,11 @@ export function rollItemMacro(itemName) {
|
||||||
if (!actor) actor = game.actors.get(speaker.actor);
|
if (!actor) actor = game.actors.get(speaker.actor);
|
||||||
|
|
||||||
// Get matching items
|
// Get matching items
|
||||||
const items = actor ? actor.items.filter(i => i.name === itemName) : [];
|
const items = actor ? actor.items.filter((i) => i.name === itemName) : [];
|
||||||
if (items.length > 1) {
|
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.`);
|
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) {
|
} else if (items.length === 0) {
|
||||||
return ui.notifications.warn(`Your controlled Actor does not have an item named ${itemName}`);
|
return ui.notifications.warn(`Your controlled Actor does not have an item named ${itemName}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
* @return {Promise} A Promise which resolves once the migration is completed
|
* @return {Promise} A Promise which resolves once the migration is completed
|
||||||
*/
|
*/
|
||||||
export const migrateWorld = async function () {
|
export const migrateWorld = async function () {
|
||||||
ui.notifications.info(`Applying SW5e System Migration for version ${game.system.data.version}. Please be patient and do not close your game or shut down your server.`, {permanent: true});
|
ui.notifications.info(
|
||||||
|
`Applying SW5e System Migration for version ${game.system.data.version}. Please be patient and do not close your game or shut down your server.`,
|
||||||
|
{permanent: true}
|
||||||
|
);
|
||||||
|
|
||||||
// Migrate World Actors
|
// Migrate World Actors
|
||||||
for await (let a of game.actors.contents) {
|
for await (let a of game.actors.contents) {
|
||||||
|
@ -43,7 +46,7 @@ export const migrateWorld = async function() {
|
||||||
await s.update(updateData, {enforceTypes: false});
|
await s.update(updateData, {enforceTypes: false});
|
||||||
// If we do not do this, then synthetic token actors remain in cache
|
// If we do not do this, then synthetic token actors remain in cache
|
||||||
// with the un-updated actorData.
|
// with the un-updated actorData.
|
||||||
s.tokens.contents.forEach(t => t._actor = null);
|
s.tokens.contents.forEach((t) => (t._actor = null));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
err.message = `Failed sw5e system migration for Scene ${s.name}: ${err.message}`;
|
err.message = `Failed sw5e system migration for Scene ${s.name}: ${err.message}`;
|
||||||
|
@ -102,10 +105,8 @@ export const migrateCompendium = async function(pack) {
|
||||||
// Save the entry, if data was changed
|
// Save the entry, if data was changed
|
||||||
await doc.update(updateData);
|
await doc.update(updateData);
|
||||||
console.log(`Migrated ${entity} entity ${doc.name} in Compendium ${pack.collection}`);
|
console.log(`Migrated ${entity} entity ${doc.name} in Compendium ${pack.collection}`);
|
||||||
}
|
} catch (err) {
|
||||||
|
|
||||||
// Handle migration failures
|
// Handle migration failures
|
||||||
catch(err) {
|
|
||||||
err.message = `Failed sw5e system migration for entity ${doc.name} in pack ${pack.collection}: ${err.message}`;
|
err.message = `Failed sw5e system migration for entity ${doc.name} in pack ${pack.collection}: ${err.message}`;
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +148,8 @@ export const migrateActorData = async function(actor) {
|
||||||
|
|
||||||
// Prepared, Equipped, and Proficient for NPC actors
|
// Prepared, Equipped, and Proficient for NPC actors
|
||||||
if (actor.type === "npc") {
|
if (actor.type === "npc") {
|
||||||
if (getProperty(itemData.data, "preparation.prepared") === false) itemUpdate["data.preparation.prepared"] = true;
|
if (getProperty(itemData.data, "preparation.prepared") === false)
|
||||||
|
itemUpdate["data.preparation.prepared"] = true;
|
||||||
if (getProperty(itemData.data, "equipped") === false) itemUpdate["data.equipped"] = true;
|
if (getProperty(itemData.data, "equipped") === false) itemUpdate["data.equipped"] = true;
|
||||||
if (getProperty(itemData.data, "proficient") === false) itemUpdate["data.proficient"] = true;
|
if (getProperty(itemData.data, "proficient") === false) itemUpdate["data.proficient"] = true;
|
||||||
}
|
}
|
||||||
|
@ -178,14 +180,12 @@ export const migrateActorData = async function(actor) {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scrub an Actor's system data, removing all keys which are not explicitly defined in the system template
|
* 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
|
* @param {Object} actorData The data object for an Actor
|
||||||
* @return {Object} The scrubbed Actor data
|
* @return {Object} The scrubbed Actor data
|
||||||
*/
|
*/
|
||||||
function cleanActorData(actorData) {
|
function cleanActorData(actorData) {
|
||||||
|
|
||||||
// Scrub system data
|
// Scrub system data
|
||||||
const model = game.system.model.Actor[actorData.type];
|
const model = game.system.model.Actor[actorData.type];
|
||||||
actorData.data = filterObject(actorData.data, model);
|
actorData.data = filterObject(actorData.data, model);
|
||||||
|
@ -203,7 +203,6 @@ function cleanActorData(actorData) {
|
||||||
return actorData;
|
return actorData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -243,22 +242,22 @@ export const migrateActorItemData = async function(item, actor) {
|
||||||
* @return {Object} The updateData to apply
|
* @return {Object} The updateData to apply
|
||||||
*/
|
*/
|
||||||
export const migrateSceneData = async function (scene) {
|
export const migrateSceneData = async function (scene) {
|
||||||
const tokens = await Promise.all(scene.tokens.map(async token => {
|
const tokens = await Promise.all(
|
||||||
|
scene.tokens.map(async (token) => {
|
||||||
const t = token.toJSON();
|
const t = token.toJSON();
|
||||||
if (!t.actorId || t.actorLink) {
|
if (!t.actorId || t.actorLink) {
|
||||||
t.actorData = {};
|
t.actorData = {};
|
||||||
}
|
} else if (!game.actors.has(t.actorId)) {
|
||||||
else if (!game.actors.has(t.actorId)) {
|
|
||||||
t.actorId = null;
|
t.actorId = null;
|
||||||
t.actorData = {};
|
t.actorData = {};
|
||||||
} else if (!t.actorLink) {
|
} else if (!t.actorLink) {
|
||||||
const actorData = duplicate(t.actorData);
|
const actorData = duplicate(t.actorData);
|
||||||
actorData.type = token.actor?.type;
|
actorData.type = token.actor?.type;
|
||||||
const update = migrateActorData(actorData);
|
const update = migrateActorData(actorData);
|
||||||
['items', 'effects'].forEach(embeddedName => {
|
["items", "effects"].forEach((embeddedName) => {
|
||||||
if (!update[embeddedName]?.length) return;
|
if (!update[embeddedName]?.length) return;
|
||||||
const updates = new Map(update[embeddedName].map(u => [u._id, u]));
|
const updates = new Map(update[embeddedName].map((u) => [u._id, u]));
|
||||||
t.actorData[embeddedName].forEach(original => {
|
t.actorData[embeddedName].forEach((original) => {
|
||||||
const update = updates.get(original._id);
|
const update = updates.get(original._id);
|
||||||
if (update) mergeObject(original, update);
|
if (update) mergeObject(original, update);
|
||||||
});
|
});
|
||||||
|
@ -268,7 +267,8 @@ export const migrateActorItemData = async function(item, actor) {
|
||||||
mergeObject(t.actorData, update);
|
mergeObject(t.actorData, update);
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
return {tokens};
|
return {tokens};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -284,7 +284,6 @@ export const migrateActorItemData = async function(item, actor) {
|
||||||
* @return {Object} The updated Actor
|
* @return {Object} The updated Actor
|
||||||
*/
|
*/
|
||||||
function _updateNPCData(actor) {
|
function _updateNPCData(actor) {
|
||||||
|
|
||||||
let actorData = actor.data;
|
let actorData = actor.data;
|
||||||
const updateData = {};
|
const updateData = {};
|
||||||
// check for flag.core, if not there is no compendium monster so exit
|
// check for flag.core, if not there is no compendium monster so exit
|
||||||
|
@ -292,13 +291,20 @@ function _updateNPCData(actor) {
|
||||||
if (!hasSource) return actor;
|
if (!hasSource) return actor;
|
||||||
// shortcut out if dataVersion flag is set to 1.2.4 or higher
|
// shortcut out if dataVersion flag is set to 1.2.4 or higher
|
||||||
const hasDataVersion = actor?.flags?.sw5e?.dataVersion !== undefined;
|
const hasDataVersion = actor?.flags?.sw5e?.dataVersion !== undefined;
|
||||||
if (hasDataVersion && (actor.flags.sw5e.dataVersion === "1.2.4" || isNewerVersion("1.2.4", actor.flags.sw5e.dataVersion))) return actor;
|
if (
|
||||||
|
hasDataVersion &&
|
||||||
|
(actor.flags.sw5e.dataVersion === "1.2.4" || isNewerVersion("1.2.4", actor.flags.sw5e.dataVersion))
|
||||||
|
)
|
||||||
|
return actor;
|
||||||
// Check to see what the source of NPC is
|
// Check to see what the source of NPC is
|
||||||
const sourceId = actor.flags.core.sourceId;
|
const sourceId = actor.flags.core.sourceId;
|
||||||
const coreSource = sourceId.substr(0, sourceId.length - 17);
|
const coreSource = sourceId.substr(0, sourceId.length - 17);
|
||||||
const core_id = sourceId.substr(sourceId.length - 16, 16);
|
const core_id = sourceId.substr(sourceId.length - 16, 16);
|
||||||
if (coreSource === "Compendium.sw5e.monsters") {
|
if (coreSource === "Compendium.sw5e.monsters") {
|
||||||
game.packs.get("sw5e.monsters").getEntity(core_id).then(monster => {
|
game.packs
|
||||||
|
.get("sw5e.monsters")
|
||||||
|
.getEntity(core_id)
|
||||||
|
.then((monster) => {
|
||||||
const monsterData = monster.data.data;
|
const monsterData = monster.data.data;
|
||||||
// copy movement[], senses[], powercasting, force[], tech[], powerForceLevel, powerTechLevel
|
// copy movement[], senses[], powercasting, force[], tech[], powerForceLevel, powerTechLevel
|
||||||
updateData["data.attributes.movement"] = monsterData.attributes.movement;
|
updateData["data.attributes.movement"] = monsterData.attributes.movement;
|
||||||
|
@ -314,7 +320,9 @@ function _updateNPCData(actor) {
|
||||||
const itemData = i.data;
|
const itemData = i.data;
|
||||||
if (itemData.type === "power") {
|
if (itemData.type === "power") {
|
||||||
const itemCompendium_id = itemData.flags?.core?.sourceId.split(".").slice(-1)[0];
|
const itemCompendium_id = itemData.flags?.core?.sourceId.split(".").slice(-1)[0];
|
||||||
let hasPower = !!actor.items.find(item => item.flags?.core?.sourceId.split(".").slice(-1)[0] === itemCompendium_id);
|
let hasPower = !!actor.items.find(
|
||||||
|
(item) => item.flags?.core?.sourceId.split(".").slice(-1)[0] === itemCompendium_id
|
||||||
|
);
|
||||||
if (!hasPower) {
|
if (!hasPower) {
|
||||||
// Clone power to new object. Don't know if it is technically needed, but seems to prevent some weirdness.
|
// Clone power to new object. Don't know if it is technically needed, but seems to prevent some weirdness.
|
||||||
const newPower = JSON.parse(JSON.stringify(itemData));
|
const newPower = JSON.parse(JSON.stringify(itemData));
|
||||||
|
@ -331,17 +339,15 @@ function _updateNPCData(actor) {
|
||||||
|
|
||||||
// set flag to check to see if migration has been done so we don't do it again.
|
// set flag to check to see if migration has been done so we don't do it again.
|
||||||
liveActor.setFlag("sw5e", "dataVersion", "1.2.4");
|
liveActor.setFlag("sw5e", "dataVersion", "1.2.4");
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//merge object
|
//merge object
|
||||||
actorData = mergeObject(actorData, updateData);
|
actorData = mergeObject(actorData, updateData);
|
||||||
// Return the scrubbed data
|
// Return the scrubbed data
|
||||||
return actor;
|
return actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Migrate the actor speed string to movement object
|
* Migrate the actor speed string to movement object
|
||||||
* @private
|
* @private
|
||||||
|
@ -350,21 +356,21 @@ function _migrateActorMovement(actorData, updateData) {
|
||||||
const ad = actorData.data;
|
const ad = actorData.data;
|
||||||
|
|
||||||
// Work is needed if old data is present
|
// Work is needed if old data is present
|
||||||
const old = actorData.type === 'vehicle' ? ad?.attributes?.speed : ad?.attributes?.speed?.value;
|
const old = actorData.type === "vehicle" ? ad?.attributes?.speed : ad?.attributes?.speed?.value;
|
||||||
const hasOld = old !== undefined;
|
const hasOld = old !== undefined;
|
||||||
if (hasOld) {
|
if (hasOld) {
|
||||||
|
|
||||||
// If new data is not present, migrate the old data
|
// If new data is not present, migrate the old data
|
||||||
const hasNew = ad?.attributes?.movement?.walk !== undefined;
|
const hasNew = ad?.attributes?.movement?.walk !== undefined;
|
||||||
if ( !hasNew && (typeof old === "string") ) {
|
if (!hasNew && typeof old === "string") {
|
||||||
const s = (old || "").split(" ");
|
const s = (old || "").split(" ");
|
||||||
if ( s.length > 0 ) updateData["data.attributes.movement.walk"] = Number.isNumeric(s[0]) ? parseInt(s[0]) : null;
|
if (s.length > 0)
|
||||||
|
updateData["data.attributes.movement.walk"] = Number.isNumeric(s[0]) ? parseInt(s[0]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the old attribute
|
// Remove the old attribute
|
||||||
updateData["data.attributes.-=speed"] = null;
|
updateData["data.attributes.-=speed"] = null;
|
||||||
}
|
}
|
||||||
return updateData
|
return updateData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -425,7 +431,7 @@ function _migrateActorPowers(actorData, updateData) {
|
||||||
// Remove the Power DC Bonus
|
// Remove the Power DC Bonus
|
||||||
updateData["data.bonuses.power.-=dc"] = null;
|
updateData["data.bonuses.power.-=dc"] = null;
|
||||||
|
|
||||||
return updateData
|
return updateData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -479,11 +485,11 @@ function _migrateActorType(actor, updateData) {
|
||||||
|
|
||||||
// New default data structure
|
// New default data structure
|
||||||
let data = {
|
let data = {
|
||||||
"value": "",
|
value: "",
|
||||||
"subtype": "",
|
subtype: "",
|
||||||
"swarm": "",
|
swarm: "",
|
||||||
"custom": ""
|
custom: ""
|
||||||
}
|
};
|
||||||
|
|
||||||
// Specifics
|
// Specifics
|
||||||
// (Some of these have weird names, these need to be addressed individually)
|
// (Some of these have weird names, these need to be addressed individually)
|
||||||
|
@ -507,13 +513,14 @@ function _migrateActorType(actor, updateData) {
|
||||||
const pattern = /^(?:swarm of (?<size>[\w\-]+) )?(?<type>[^(]+?)(?:\((?<subtype>[^)]+)\))?$/i;
|
const pattern = /^(?:swarm of (?<size>[\w\-]+) )?(?<type>[^(]+?)(?:\((?<subtype>[^)]+)\))?$/i;
|
||||||
const match = original.trim().match(pattern);
|
const match = original.trim().match(pattern);
|
||||||
if (match) {
|
if (match) {
|
||||||
|
|
||||||
// Match a known creature type
|
// Match a known creature type
|
||||||
const typeLc = match.groups.type.trim().toLowerCase();
|
const typeLc = match.groups.type.trim().toLowerCase();
|
||||||
const typeMatch = Object.entries(CONFIG.SW5E.creatureTypes).find(([k, v]) => {
|
const typeMatch = Object.entries(CONFIG.SW5E.creatureTypes).find(([k, v]) => {
|
||||||
return (typeLc === k) ||
|
return (
|
||||||
(typeLc === game.i18n.localize(v).toLowerCase()) ||
|
typeLc === k ||
|
||||||
(typeLc === game.i18n.localize(`${v}Pl`).toLowerCase());
|
typeLc === game.i18n.localize(v).toLowerCase() ||
|
||||||
|
typeLc === game.i18n.localize(`${v}Pl`).toLowerCase()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
if (typeMatch) data.value = typeMatch[0];
|
if (typeMatch) data.value = typeMatch[0];
|
||||||
else {
|
else {
|
||||||
|
@ -527,7 +534,7 @@ function _migrateActorType(actor, updateData) {
|
||||||
if (match.groups.size || isNamedSwarm) {
|
if (match.groups.size || isNamedSwarm) {
|
||||||
const sizeLc = match.groups.size ? match.groups.size.trim().toLowerCase() : "tiny";
|
const sizeLc = match.groups.size ? match.groups.size.trim().toLowerCase() : "tiny";
|
||||||
const sizeMatch = Object.entries(CONFIG.SW5E.actorSizes).find(([k, v]) => {
|
const sizeMatch = Object.entries(CONFIG.SW5E.actorSizes).find(([k, v]) => {
|
||||||
return (sizeLc === k) || (sizeLc === game.i18n.localize(v).toLowerCase());
|
return sizeLc === k || sizeLc === game.i18n.localize(v).toLowerCase();
|
||||||
});
|
});
|
||||||
data.swarm = sizeMatch ? sizeMatch[0] : "tiny";
|
data.swarm = sizeMatch ? sizeMatch[0] : "tiny";
|
||||||
} else data.swarm = "";
|
} else data.swarm = "";
|
||||||
|
@ -560,7 +567,6 @@ function _migrateItemClassPowerCasting(item, updateData) {
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case "Engineer":
|
case "Engineer":
|
||||||
|
|
||||||
updateData["data.powercasting"] = {
|
updateData["data.powercasting"] = {
|
||||||
progression: "engineer",
|
progression: "engineer",
|
||||||
ability: ""
|
ability: ""
|
||||||
|
@ -608,7 +614,11 @@ async function _migrateItemPower(item, actor, updateData) {
|
||||||
|
|
||||||
// shortcut out if dataVersion flag is set to 1.2.4 or higher
|
// shortcut out if dataVersion flag is set to 1.2.4 or higher
|
||||||
const hasDataVersion = item?.flags?.sw5e?.dataVersion !== undefined;
|
const hasDataVersion = item?.flags?.sw5e?.dataVersion !== undefined;
|
||||||
if (hasDataVersion && (item.flags.sw5e.dataVersion === "1.2.4" || isNewerVersion("1.2.4", item.flags.sw5e.dataVersion))) return updateData;
|
if (
|
||||||
|
hasDataVersion &&
|
||||||
|
(item.flags.sw5e.dataVersion === "1.2.4" || isNewerVersion("1.2.4", item.flags.sw5e.dataVersion))
|
||||||
|
)
|
||||||
|
return updateData;
|
||||||
|
|
||||||
// Check to see what the source of Power is
|
// Check to see what the source of Power is
|
||||||
const sourceId = item.flags.core.sourceId;
|
const sourceId = item.flags.core.sourceId;
|
||||||
|
@ -626,11 +636,10 @@ async function _migrateItemPower(item, actor, updateData) {
|
||||||
const corePowerData = corePower.data;
|
const corePowerData = corePower.data;
|
||||||
// copy Core Power Data over original Power
|
// copy Core Power Data over original Power
|
||||||
updateData["data"] = corePowerData;
|
updateData["data"] = corePowerData;
|
||||||
updateData["flags"] = {"sw5e": {"dataVersion": "1.2.4"}};
|
updateData["flags"] = {sw5e: {dataVersion: "1.2.4"}};
|
||||||
|
|
||||||
return updateData;
|
return updateData;
|
||||||
|
|
||||||
|
|
||||||
//game.packs.get(powerType).getEntity(core_id).then(corePower => {
|
//game.packs.get(powerType).getEntity(core_id).then(corePower => {
|
||||||
|
|
||||||
//})
|
//})
|
||||||
|
@ -670,10 +679,10 @@ export async function purgeFlags(pack) {
|
||||||
for (let entity of content) {
|
for (let entity of content) {
|
||||||
const update = {_id: entity.id, flags: cleanFlags(entity.data.flags)};
|
const update = {_id: entity.id, flags: cleanFlags(entity.data.flags)};
|
||||||
if (pack.entity === "Actor") {
|
if (pack.entity === "Actor") {
|
||||||
update.items = entity.data.items.map(i => {
|
update.items = entity.data.items.map((i) => {
|
||||||
i.flags = cleanFlags(i.flags);
|
i.flags = cleanFlags(i.flags);
|
||||||
return i;
|
return i;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
await pack.updateEntity(update, {recursive: false});
|
await pack.updateEntity(update, {recursive: false});
|
||||||
console.log(`Purged flags from ${entity.name}`);
|
console.log(`Purged flags from ${entity.name}`);
|
||||||
|
@ -683,7 +692,6 @@ export async function purgeFlags(pack) {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Purge the data model of any inner objects which have been flagged as _deprecated.
|
* Purge the data model of any inner objects which have been flagged as _deprecated.
|
||||||
* @param {object} data The data to clean
|
* @param {object} data The data to clean
|
||||||
|
@ -695,8 +703,7 @@ export function removeDeprecatedObjects(data) {
|
||||||
if (v._deprecated === true) {
|
if (v._deprecated === true) {
|
||||||
console.log(`Deleting deprecated object key ${k}`);
|
console.log(`Deleting deprecated object key ${k}`);
|
||||||
delete data[k];
|
delete data[k];
|
||||||
}
|
} else removeDeprecatedObjects(v);
|
||||||
else removeDeprecatedObjects(v);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { SW5E } from "../config.js";
|
||||||
* @extends {MeasuredTemplate}
|
* @extends {MeasuredTemplate}
|
||||||
*/
|
*/
|
||||||
export default 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
|
* A factory method to create an AbilityTemplate instance using provided data from an Item5e instance
|
||||||
* @param {Item5e} item The Item object for which to construct the template
|
* @param {Item5e} item The Item object for which to construct the template
|
||||||
|
@ -84,7 +83,7 @@ export default class AbilityTemplate extends MeasuredTemplate {
|
||||||
let moveTime = 0;
|
let moveTime = 0;
|
||||||
|
|
||||||
// Update placement (mouse-move)
|
// Update placement (mouse-move)
|
||||||
handlers.mm = event => {
|
handlers.mm = (event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
let now = Date.now(); // Apply a 20ms throttle
|
let now = Date.now(); // Apply a 20ms throttle
|
||||||
if (now - moveTime <= 20) return;
|
if (now - moveTime <= 20) return;
|
||||||
|
@ -96,7 +95,7 @@ export default class AbilityTemplate extends MeasuredTemplate {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cancel the workflow (right-click)
|
// Cancel the workflow (right-click)
|
||||||
handlers.rc = event => {
|
handlers.rc = (event) => {
|
||||||
this.layer.preview.removeChildren();
|
this.layer.preview.removeChildren();
|
||||||
canvas.stage.off("mousemove", handlers.mm);
|
canvas.stage.off("mousemove", handlers.mm);
|
||||||
canvas.stage.off("mousedown", handlers.lc);
|
canvas.stage.off("mousedown", handlers.lc);
|
||||||
|
@ -107,7 +106,7 @@ export default class AbilityTemplate extends MeasuredTemplate {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Confirm the workflow (left-click)
|
// Confirm the workflow (left-click)
|
||||||
handlers.lc = event => {
|
handlers.lc = (event) => {
|
||||||
handlers.rc(event);
|
handlers.rc(event);
|
||||||
const destination = canvas.grid.getSnappedPosition(this.data.x, this.data.y, 2);
|
const destination = canvas.grid.getSnappedPosition(this.data.x, this.data.y, 2);
|
||||||
this.data.update(destination);
|
this.data.update(destination);
|
||||||
|
@ -115,12 +114,12 @@ export default class AbilityTemplate extends MeasuredTemplate {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Rotate the template by 3 degree increments (mouse-wheel)
|
// Rotate the template by 3 degree increments (mouse-wheel)
|
||||||
handlers.mw = event => {
|
handlers.mw = (event) => {
|
||||||
if (event.ctrlKey) event.preventDefault(); // Avoid zooming the browser window
|
if (event.ctrlKey) event.preventDefault(); // Avoid zooming the browser window
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
let delta = canvas.grid.type > CONST.GRID_TYPES.SQUARE ? 30 : 15;
|
let delta = canvas.grid.type > CONST.GRID_TYPES.SQUARE ? 30 : 15;
|
||||||
let snap = event.shiftKey ? delta : 5;
|
let snap = event.shiftKey ? delta : 5;
|
||||||
this.data.update({direction: this.data.direction + (snap * Math.sign(event.deltaY))});
|
this.data.update({direction: this.data.direction + snap * Math.sign(event.deltaY)});
|
||||||
this.refresh();
|
this.refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
export const registerSystemSettings = function () {
|
export const registerSystemSettings = function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track the system version upon which point a migration was last applied
|
* Track the system version upon which point a migration was last applied
|
||||||
*/
|
*/
|
||||||
|
@ -22,9 +21,9 @@ export const registerSystemSettings = function() {
|
||||||
default: "normal",
|
default: "normal",
|
||||||
type: String,
|
type: String,
|
||||||
choices: {
|
choices: {
|
||||||
"normal": "SETTINGS.5eRestPHB",
|
normal: "SETTINGS.5eRestPHB",
|
||||||
"gritty": "SETTINGS.5eRestGritty",
|
gritty: "SETTINGS.5eRestGritty",
|
||||||
"epic": "SETTINGS.5eRestEpic",
|
epic: "SETTINGS.5eRestEpic"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -39,11 +38,11 @@ export const registerSystemSettings = function() {
|
||||||
default: "555",
|
default: "555",
|
||||||
type: String,
|
type: String,
|
||||||
choices: {
|
choices: {
|
||||||
"555": "SETTINGS.5eDiagPHB",
|
555: "SETTINGS.5eDiagPHB",
|
||||||
"5105": "SETTINGS.5eDiagDMG",
|
5105: "SETTINGS.5eDiagDMG",
|
||||||
"EUCL": "SETTINGS.5eDiagEuclidean",
|
EUCL: "SETTINGS.5eDiagEuclidean"
|
||||||
},
|
},
|
||||||
onChange: rule => canvas.grid.diagonalRule = rule
|
onChange: (rule) => (canvas.grid.diagonalRule = rule)
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,7 +78,7 @@ export const registerSystemSettings = function() {
|
||||||
scope: "world",
|
scope: "world",
|
||||||
config: true,
|
config: true,
|
||||||
default: false,
|
default: false,
|
||||||
type: Boolean,
|
type: Boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,7 +91,7 @@ export const registerSystemSettings = function() {
|
||||||
config: true,
|
config: true,
|
||||||
default: false,
|
default: false,
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
onChange: s => {
|
onChange: (s) => {
|
||||||
ui.chat.render();
|
ui.chat.render();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -100,10 +99,10 @@ export const registerSystemSettings = function() {
|
||||||
/**
|
/**
|
||||||
* Option to allow GMs to restrict polymorphing to GMs only.
|
* Option to allow GMs to restrict polymorphing to GMs only.
|
||||||
*/
|
*/
|
||||||
game.settings.register('sw5e', 'allowPolymorphing', {
|
game.settings.register("sw5e", "allowPolymorphing", {
|
||||||
name: 'SETTINGS.5eAllowPolymorphingN',
|
name: "SETTINGS.5eAllowPolymorphingN",
|
||||||
hint: 'SETTINGS.5eAllowPolymorphingL',
|
hint: "SETTINGS.5eAllowPolymorphingL",
|
||||||
scope: 'world',
|
scope: "world",
|
||||||
config: true,
|
config: true,
|
||||||
default: false,
|
default: false,
|
||||||
type: Boolean
|
type: Boolean
|
||||||
|
@ -112,8 +111,8 @@ export const registerSystemSettings = function() {
|
||||||
/**
|
/**
|
||||||
* Remember last-used polymorph settings.
|
* Remember last-used polymorph settings.
|
||||||
*/
|
*/
|
||||||
game.settings.register('sw5e', 'polymorphSettings', {
|
game.settings.register("sw5e", "polymorphSettings", {
|
||||||
scope: 'client',
|
scope: "client",
|
||||||
default: {
|
default: {
|
||||||
keepPhysical: false,
|
keepPhysical: false,
|
||||||
keepMental: false,
|
keepMental: false,
|
||||||
|
@ -138,8 +137,8 @@ export const registerSystemSettings = function() {
|
||||||
default: "light",
|
default: "light",
|
||||||
type: String,
|
type: String,
|
||||||
choices: {
|
choices: {
|
||||||
"light": "SETTINGS.SWColorLight",
|
light: "SETTINGS.SWColorLight",
|
||||||
"dark": "SETTINGS.SWColorDark"
|
dark: "SETTINGS.SWColorDark"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
export const preloadHandlebarsTemplates = async function () {
|
export const preloadHandlebarsTemplates = async function () {
|
||||||
return loadTemplates([
|
return loadTemplates([
|
||||||
|
|
||||||
// Shared Partials
|
// Shared Partials
|
||||||
"systems/sw5e/templates/actors/parts/active-effects.html",
|
"systems/sw5e/templates/actors/parts/active-effects.html",
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,10 @@
|
||||||
* @extends {TokenDocument}
|
* @extends {TokenDocument}
|
||||||
*/
|
*/
|
||||||
export class TokenDocument5e extends TokenDocument {
|
export class TokenDocument5e extends TokenDocument {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
getBarAttribute(...args) {
|
getBarAttribute(...args) {
|
||||||
const data = super.getBarAttribute(...args);
|
const data = super.getBarAttribute(...args);
|
||||||
if ( data && (data.attribute === "attributes.hp") ) {
|
if (data && data.attribute === "attributes.hp") {
|
||||||
data.value += parseInt(getProperty(this.actor.data, "data.attributes.hp.temp") || 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);
|
data.max += parseInt(getProperty(this.actor.data, "data.attributes.hp.tempmax") || 0);
|
||||||
}
|
}
|
||||||
|
@ -15,16 +14,13 @@ export class TokenDocument5e extends TokenDocument {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend the base Token class to implement additional system-specific logic.
|
* Extend the base Token class to implement additional system-specific logic.
|
||||||
* @extends {Token}
|
* @extends {Token}
|
||||||
*/
|
*/
|
||||||
export class Token5e extends Token {
|
export class Token5e extends Token {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
_drawBar(number, bar, data) {
|
_drawBar(number, bar, data) {
|
||||||
if (data.attribute === "attributes.hp") return this._drawHPBar(number, bar, data);
|
if (data.attribute === "attributes.hp") return this._drawHPBar(number, bar, data);
|
||||||
|
@ -41,7 +37,6 @@ export class Token5e extends Token {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_drawHPBar(number, bar, data) {
|
_drawHPBar(number, bar, data) {
|
||||||
|
|
||||||
// Extract health data
|
// Extract health data
|
||||||
let {value, max, temp, tempmax} = this.document.actor.data.data.attributes.hp;
|
let {value, max, temp, tempmax} = this.document.actor.data.data.attributes.hp;
|
||||||
temp = Number(temp || 0);
|
temp = Number(temp || 0);
|
||||||
|
@ -58,42 +53,50 @@ export class Token5e extends Token {
|
||||||
|
|
||||||
// Determine colors to use
|
// Determine colors to use
|
||||||
const blk = 0x000000;
|
const blk = 0x000000;
|
||||||
const hpColor = PIXI.utils.rgb2hex([(1-(colorPct/2)), colorPct, 0]);
|
const hpColor = PIXI.utils.rgb2hex([1 - colorPct / 2, colorPct, 0]);
|
||||||
const c = CONFIG.SW5E.tokenHPColors;
|
const c = CONFIG.SW5E.tokenHPColors;
|
||||||
|
|
||||||
// Determine the container size (logic borrowed from core)
|
// Determine the container size (logic borrowed from core)
|
||||||
const w = this.w;
|
const w = this.w;
|
||||||
let h = Math.max((canvas.dimensions.size / 12), 8);
|
let h = Math.max(canvas.dimensions.size / 12, 8);
|
||||||
if (this.data.height >= 2) h *= 1.6;
|
if (this.data.height >= 2) h *= 1.6;
|
||||||
const bs = Math.clamped(h / 8, 1, 2);
|
const bs = Math.clamped(h / 8, 1, 2);
|
||||||
const bs1 = bs + 1;
|
const bs1 = bs + 1;
|
||||||
|
|
||||||
// Overall bar container
|
// Overall bar container
|
||||||
bar.clear()
|
bar.clear();
|
||||||
bar.beginFill(blk, 0.5).lineStyle(bs, blk, 1.0).drawRoundedRect(0, 0, w, h, 3);
|
bar.beginFill(blk, 0.5).lineStyle(bs, blk, 1.0).drawRoundedRect(0, 0, w, h, 3);
|
||||||
|
|
||||||
// Temporary maximum HP
|
// Temporary maximum HP
|
||||||
if (tempmax > 0) {
|
if (tempmax > 0) {
|
||||||
const pct = max / effectiveMax;
|
const pct = max / effectiveMax;
|
||||||
bar.beginFill(c.tempmax, 1.0).lineStyle(1, blk, 1.0).drawRoundedRect(pct*w, 0, (1-pct)*w, h, 2);
|
bar.beginFill(c.tempmax, 1.0)
|
||||||
|
.lineStyle(1, blk, 1.0)
|
||||||
|
.drawRoundedRect(pct * w, 0, (1 - pct) * w, h, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maximum HP penalty
|
// Maximum HP penalty
|
||||||
else if (tempmax < 0) {
|
else if (tempmax < 0) {
|
||||||
const pct = (max + tempmax) / max;
|
const pct = (max + tempmax) / max;
|
||||||
bar.beginFill(c.negmax, 1.0).lineStyle(1, blk, 1.0).drawRoundedRect(pct*w, 0, (1-pct)*w, h, 2);
|
bar.beginFill(c.negmax, 1.0)
|
||||||
|
.lineStyle(1, blk, 1.0)
|
||||||
|
.drawRoundedRect(pct * w, 0, (1 - pct) * w, h, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Health bar
|
// Health bar
|
||||||
bar.beginFill(hpColor, 1.0).lineStyle(bs, blk, 1.0).drawRoundedRect(0, 0, valuePct*w, h, 2)
|
bar.beginFill(hpColor, 1.0)
|
||||||
|
.lineStyle(bs, blk, 1.0)
|
||||||
|
.drawRoundedRect(0, 0, valuePct * w, h, 2);
|
||||||
|
|
||||||
// Temporary hit points
|
// Temporary hit points
|
||||||
if (temp > 0) {
|
if (temp > 0) {
|
||||||
bar.beginFill(c.temp, 1.0).lineStyle(0).drawRoundedRect(bs1, bs1, (tempPct*w)-(2*bs1), h-(2*bs1), 1);
|
bar.beginFill(c.temp, 1.0)
|
||||||
|
.lineStyle(0)
|
||||||
|
.drawRoundedRect(bs1, bs1, tempPct * w - 2 * bs1, h - 2 * bs1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set position
|
// Set position
|
||||||
let posY = (number === 0) ? (this.h - h) : 0;
|
let posY = number === 0 ? this.h - h : 0;
|
||||||
bar.position.set(0, posY);
|
bar.position.set(0, posY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
package-lock.json
generated
12
package-lock.json
generated
|
@ -1266,9 +1266,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hosted-git-info": {
|
"hosted-git-info": {
|
||||||
"version": "2.8.8",
|
"version": "2.8.9",
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||||
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
|
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
|
||||||
},
|
},
|
||||||
"image-size": {
|
"image-size": {
|
||||||
"version": "0.5.5",
|
"version": "0.5.5",
|
||||||
|
@ -3068,9 +3068,9 @@
|
||||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||||
},
|
},
|
||||||
"y18n": {
|
"y18n": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
|
||||||
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
|
"integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ=="
|
||||||
},
|
},
|
||||||
"yargs": {
|
"yargs": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
|
|
|
@ -111,3 +111,4 @@
|
||||||
{"name":"Headcomm","permission":{"default":0,"vXYkFWX6qzvOu2jc":3},"type":"loot","data":{"description":{"value":"<p>A headcomm can be installed in a helmet or worn independently. It functions as a hands-free commlink.</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":1,"price":200,"attuned":false,"equipped":false,"rarity":"","identified":true,"attributes":{"spelldc":10},"damage":{"parts":[]}},"flags":{},"img":"systems/sw5e/packs/Icons/Communications/Headcomm.webp","_id":"zHERdLuCUPpxzaSJ"}
|
{"name":"Headcomm","permission":{"default":0,"vXYkFWX6qzvOu2jc":3},"type":"loot","data":{"description":{"value":"<p>A headcomm can be installed in a helmet or worn independently. It functions as a hands-free commlink.</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":1,"price":200,"attuned":false,"equipped":false,"rarity":"","identified":true,"attributes":{"spelldc":10},"damage":{"parts":[]}},"flags":{},"img":"systems/sw5e/packs/Icons/Communications/Headcomm.webp","_id":"zHERdLuCUPpxzaSJ"}
|
||||||
{"name":"Poisoner's Kit","permission":{"default":0,"vXYkFWX6qzvOu2jc":3},"type":"tool","data":{"description":{"value":"<p>A poisoner’s kit includes the vials, chemicals, and other equipment necessary for the creation of poisons. Proficiency with this kit lets you add your proficiency bonus to any ability checks you make to craft or use poisons.</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":2,"price":500,"attuned":false,"equipped":false,"rarity":"","identified":true,"ability":"int","chatFlavor":"","proficient":0,"attributes":{"spelldc":10},"damage":{"parts":[]}},"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false,"effects":[]}},"img":"systems/sw5e/packs/Icons/Kit/Poisoner_s%20Kit.webp","_id":"zaLzNlsfzRf71Xpl"}
|
{"name":"Poisoner's Kit","permission":{"default":0,"vXYkFWX6qzvOu2jc":3},"type":"tool","data":{"description":{"value":"<p>A poisoner’s kit includes the vials, chemicals, and other equipment necessary for the creation of poisons. Proficiency with this kit lets you add your proficiency bonus to any ability checks you make to craft or use poisons.</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":2,"price":500,"attuned":false,"equipped":false,"rarity":"","identified":true,"ability":"int","chatFlavor":"","proficient":0,"attributes":{"spelldc":10},"damage":{"parts":[]}},"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false,"effects":[]}},"img":"systems/sw5e/packs/Icons/Kit/Poisoner_s%20Kit.webp","_id":"zaLzNlsfzRf71Xpl"}
|
||||||
{"name":"Mine, Plasma","permission":{"default":0,"vXYkFWX6qzvOu2jc":3},"type":"weapon","data":{"description":{"value":"<p>When you use your action to set it, this mine sets an imperceptible laser line extending up to 15 feet. When the laser is tripped, the mine explodes, coating the area in a 15-foot radius around it in fire that burns for 1 minute. When a creature enters the fire or starts its turn there it must make a DC 13 Dexterity saving throw. On a failed save, the creature takes 2d6 fire damage, or half as much on a successful one. A construct makes this save with disadvantage.</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":2,"price":550,"attuned":false,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":15,"units":"ft","type":"radius"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":1,"per":"charges"},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"save","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"dex","dc":13,"scaling":"flat"},"weaponType":"improv","properties":{"amm":false,"fin":false,"fir":false,"foc":false,"hvy":false,"lgt":false,"lod":false,"rch":false,"rel":false,"ret":false,"spc":false,"thr":false,"two":false,"ver":false},"proficient":false,"attributes":{"spelldc":10}},"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false,"effects":[]}},"img":"systems/sw5e/packs/Icons/Explosive/Mine%2C%20Plasma.webp","_id":"zrSOUA8dUg9lHVdS"}
|
{"name":"Mine, Plasma","permission":{"default":0,"vXYkFWX6qzvOu2jc":3},"type":"weapon","data":{"description":{"value":"<p>When you use your action to set it, this mine sets an imperceptible laser line extending up to 15 feet. When the laser is tripped, the mine explodes, coating the area in a 15-foot radius around it in fire that burns for 1 minute. When a creature enters the fire or starts its turn there it must make a DC 13 Dexterity saving throw. On a failed save, the creature takes 2d6 fire damage, or half as much on a successful one. A construct makes this save with disadvantage.</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":2,"price":550,"attuned":false,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":15,"units":"ft","type":"radius"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":1,"per":"charges"},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"save","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"dex","dc":13,"scaling":"flat"},"weaponType":"improv","properties":{"amm":false,"fin":false,"fir":false,"foc":false,"hvy":false,"lgt":false,"lod":false,"rch":false,"rel":false,"ret":false,"spc":false,"thr":false,"two":false,"ver":false},"proficient":false,"attributes":{"spelldc":10}},"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false,"effects":[]}},"img":"systems/sw5e/packs/Icons/Explosive/Mine%2C%20Plasma.webp","_id":"zrSOUA8dUg9lHVdS"}
|
||||||
|
{"name":"Power Cell","permission":{"default":0,"vXYkFWX6qzvOu2jc":3},"type":"consumable","data":{"description":{"value":"<p>Power cells fuel blaster weapons that deal energy or ion damage. Additionally, power cells are used to energize certain tools.</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":1,"price":10,"attuned":false,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":null,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":240,"max":240,"per":"charges","autoDestroy":false},"consume":{"type":"","target":"","amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"ammo","attributes":{"spelldc":10}},"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false,"effects":[]}},"img":"systems/sw5e/packs/Icons/Ammunition/Power%20Cell.webp","_id":"VUkO1T2aYMuUcBZM"}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
||||||
{"_id":"AAA9PWi1rTiSUIIe","name":"Lightweight Armor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Lightweight armor offers a trade-off of a more maneuverable but less resilient ship. A ship with Lightweight Armor installed has a +2 bonus to armor class, but has one fewer maximum hull point per Hull Die.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":3100,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10,"type":"ssarmor","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"dmgred":{"value":"0"},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""},"attributes":{"dr":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Lightweight%20Armor.webp","effects":[]}
|
{"_id":"AAA9PWi1rTiSUIIe","name":"Lightweight Armor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Lightweight armor offers a trade-off of a more maneuverable but less resilient ship. A ship with Lightweight Armor installed has a +2 bonus to armor class, but has one fewer maximum hull point per Hull Die.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":3100,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10,"type":"ssarmor","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":"(-1)"},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""},"attributes":{"dr":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Lightweight%20Armor.webp","effects":[]}
|
||||||
{"_id":"JhX8qXjrDL3pCRmF","name":"Reinforced Armor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Opposite of lightweight armor is reinforced armor. This armor improves a ship's resilience, but makes it less likely to avoid damage. A ship with Reinforced Armor installed has a -1 penalty to armor class, but has one additional maximum hull point per Hull Die.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":3700,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10,"type":"ssarmor","dex":0},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"dmgred":{"value":"6"},"regrateco":{"value":""},"attributes":{"dr":"6"},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Reinforced%20Armor.webp","effects":[]}
|
{"_id":"JhX8qXjrDL3pCRmF","name":"Reinforced Armor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Opposite of lightweight armor is reinforced armor. This armor improves a ship's resilience, but makes it less likely to avoid damage. A ship with Reinforced Armor installed has a -1 penalty to armor class, but has one additional maximum hull point per Hull Die.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":3700,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10,"type":"ssarmor","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":"1"},"regrateco":{"value":""},"attributes":{"dr":"6"},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Reinforced%20Armor.webp","effects":[]}
|
||||||
{"_id":"M7igMGsBIosGA4dS","name":"Quick-Charge Shield","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Quick-Charge Shields, opposite of Fortress Shields, offer a reduced capacity but rapidly replenish.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":4900,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":0,"type":"ssshield","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":"0.667"},"dmgred":{"value":""},"regrateco":{"value":"1.5"},"attributes":{"dr":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Quick-Charge%20Shield.webp","effects":[]}
|
{"_id":"M7igMGsBIosGA4dS","name":"Quick-Charge Shield","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Quick-Charge Shields, opposite of Fortress Shields, offer a reduced capacity but rapidly replenish.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":4900,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":0,"type":"ssshield","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":"(2/3)"},"hpperhd":{"value":""},"regrateco":{"value":"(3/2)"},"attributes":{"dr":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Quick-Charge%20Shield.webp","effects":[]}
|
||||||
{"_id":"RvtLP3FgKLBYBHSf","name":"Directional Shield","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Directional Shields are the most commonly used and balanced shields on the market.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":4300,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":0,"type":"ssshield","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":"1"},"dmgred":{"value":""},"regrateco":{"value":"1"},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""},"attributes":{"dr":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Directional%20Shield.webp","effects":[]}
|
{"_id":"RvtLP3FgKLBYBHSf","name":"Directional Shield","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Directional Shields are the most commonly used and balanced shields on the market.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":4300,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":0,"type":"ssshield","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":"1"},"hpperhd":{"value":""},"regrateco":{"value":"1"},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""},"attributes":{"dr":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Directional%20Shield.webp","effects":[]}
|
||||||
{"_id":"Wj62TEtwKeG1P2DD","name":"Fortress Shield","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Fortress shields offer a higher maximum shield points, but regenerate slower than normal shields.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":4650,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":0,"type":"ssshield","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":"1.5"},"dmgred":{"value":""},"regrateco":{"value":"0.667"},"attributes":{"dr":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Fortress%20Shield.webp","effects":[]}
|
{"_id":"Wj62TEtwKeG1P2DD","name":"Fortress Shield","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Fortress shields offer a higher maximum shield points, but regenerate slower than normal shields.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":4650,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":0,"type":"ssshield","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":"(3/2)"},"hpperhd":{"value":""},"regrateco":{"value":"(2/3)"},"attributes":{"dr":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Fortress%20Shield.webp","effects":[]}
|
||||||
{"_id":"aG6mKPerYCFmkI00","name":"Deflection Armor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Deflection armor is the most common type of armor aboard ships, and offers no benefit or penalty to armor class or hull points.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":3450,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10,"type":"ssarmor","dex":2},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"dmgred":{"value":"3"},"regrateco":{"value":""},"attributes":{"dr":"3"},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Deflection%20Armor.webp","effects":[]}
|
{"_id":"aG6mKPerYCFmkI00","name":"Deflection Armor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Deflection armor is the most common type of armor aboard ships, and offers no benefit or penalty to armor class or hull points.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":0,"price":3450,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10,"type":"ssarmor","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"attributes":{"dr":"3"},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Deflection%20Armor.webp","effects":[]}
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
{"_id":"MVXftcjJ1yzsCU3N","name":"Hyperdrive, Class 0.5","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>The hyperdrive is a propulsion system that allows a starship to reach hyperspeed and traverse the void between stars in the alternate dimension of hyperspace. For a starship to have a hyperdrive, it must have a vacant hyperdrive slot modification.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":50000,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"hyper","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":"0.5"}},"flags":{"core":{"sourceId":"Item.3CA76AXkU73nE53o"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Hyperdrive.webp","effects":[]}
|
{"_id":"MVXftcjJ1yzsCU3N","name":"Hyperdrive, Class 0.5","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>The hyperdrive is a propulsion system that allows a starship to reach hyperspeed and traverse the void between stars in the alternate dimension of hyperspace. For a starship to have a hyperdrive, it must have a vacant hyperdrive slot modification.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":50000,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"hyper","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":"0.5"}},"flags":{"core":{"sourceId":"Item.3CA76AXkU73nE53o"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Hyperdrive.webp","effects":[]}
|
||||||
{"name":"Hyperdrive, Class 8","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>The hyperdrive is a propulsion system that allows a starship to reach hyperspeed and traverse the void between stars in the alternate dimension of hyperspace. For a starship to have a hyperdrive, it must have a vacant hyperdrive slot modification.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":1000,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"hyper","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":"8"}},"flags":{"core":{"sourceId":"Item.pJASNAp63U6Y2Yx8"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Hyperdrive.webp","effects":[],"_id":"P84rgL4vBaWw0GJe"}
|
{"name":"Hyperdrive, Class 8","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>The hyperdrive is a propulsion system that allows a starship to reach hyperspeed and traverse the void between stars in the alternate dimension of hyperspace. For a starship to have a hyperdrive, it must have a vacant hyperdrive slot modification.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":1000,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"hyper","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":"8"}},"flags":{"core":{"sourceId":"Item.pJASNAp63U6Y2Yx8"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Hyperdrive.webp","effects":[],"_id":"P84rgL4vBaWw0GJe"}
|
||||||
{"name":"Hyperdrive, Class 5","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>The hyperdrive is a propulsion system that allows a starship to reach hyperspeed and traverse the void between stars in the alternate dimension of hyperspace. For a starship to have a hyperdrive, it must have a vacant hyperdrive slot modification.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":2500,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"hyper","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":"5"}},"flags":{"core":{"sourceId":"Item.UgLbCq7OWL9G9BD7"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Hyperdrive.webp","effects":[],"_id":"U4BhpyMxJdbnNErJ"}
|
{"name":"Hyperdrive, Class 5","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>The hyperdrive is a propulsion system that allows a starship to reach hyperspeed and traverse the void between stars in the alternate dimension of hyperspace. For a starship to have a hyperdrive, it must have a vacant hyperdrive slot modification.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":2500,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"hyper","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":"5"}},"flags":{"core":{"sourceId":"Item.UgLbCq7OWL9G9BD7"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Hyperdrive.webp","effects":[],"_id":"U4BhpyMxJdbnNErJ"}
|
||||||
{"_id":"UAiau5ZNXVJAJFUn","name":"Power Core Reactor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Power core reactors have highly variable power output capabilities, but sacrifice fuel economy and as a result.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":5750,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"reactor","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":"1.5"},"powdicerec":{"value":"1d2"},"hdclass":{"value":""}},"flags":{"core":{"sourceId":"Item.2MTQUv6r5ePNANyn"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Reactor.webp","effects":[]}
|
{"_id":"UAiau5ZNXVJAJFUn","name":"Power Core Reactor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Power core reactors have highly variable power output capabilities, but sacrifice fuel economy and as a result.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":5750,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"reactor","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":"(3/2)"},"powdicerec":{"value":"1d2"},"hdclass":{"value":""}},"flags":{"core":{"sourceId":"Item.2MTQUv6r5ePNANyn"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Reactor.webp","effects":[]}
|
||||||
{"_id":"VzkRXuQx2sqN9nd0","name":"Distributed Power Coupling","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Distributed power coupling sacrifices flexibility by allocating power separately to each system.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":5100,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":0,"type":"powerc","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":"2"},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{"core":{"sourceId":"Item.4zjcBtJhXgpFW2pb"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Power%20Coupling.webp","effects":[]}
|
{"_id":"VzkRXuQx2sqN9nd0","name":"Distributed Power Coupling","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Distributed power coupling sacrifices flexibility by allocating power separately to each system.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":5100,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":0,"type":"powerc","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":"2"},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{"core":{"sourceId":"Item.4zjcBtJhXgpFW2pb"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Power%20Coupling.webp","effects":[]}
|
||||||
{"_id":"ZyEdKtLwSXuUQs0P","name":"Ionization Reactor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Ionization reactors are highly fuel-efficient reactors that trade power output for fuel economy.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":5100,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"reactor","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":"0.667"},"powdicerec":{"value":"(1d2)-1"},"hdclass":{"value":""}},"flags":{"core":{"sourceId":"Item.kXo8mNp6GFLYWokY"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Reactor.webp","effects":[]}
|
{"_id":"ZyEdKtLwSXuUQs0P","name":"Ionization Reactor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Ionization reactors are highly fuel-efficient reactors that trade power output for fuel economy.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":5100,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"reactor","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":"(2/3)"},"powdicerec":{"value":"(1d2)-1"},"hdclass":{"value":""}},"flags":{"core":{"sourceId":"Item.kXo8mNp6GFLYWokY"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Reactor.webp","effects":[]}
|
||||||
{"name":"Fuel Cell Reactor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Fuel cell reactors are the most common and balanced reactors on the market.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":4500,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"reactor","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":"1"},"powdicerec":{"value":"1"},"hdclass":{"value":""}},"flags":{"core":{"sourceId":"Item.Qwu6WlJiIgFWq9VF"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Reactor.webp","effects":[],"_id":"jk7zL3cqhufDKsuh"}
|
{"name":"Fuel Cell Reactor","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Fuel cell reactors are the most common and balanced reactors on the market.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":4500,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"reactor","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":"1"},"hdclass":{"value":""}},"flags":{"core":{"sourceId":"Item.Qwu6WlJiIgFWq9VF"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Reactor.webp","effects":[],"_id":"jk7zL3cqhufDKsuh"}
|
||||||
{"name":"Hyperdrive, Class 1.0","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>The hyperdrive is a propulsion system that allows a starship to reach hyperspeed and traverse the void between stars in the alternate dimension of hyperspace. For a starship to have a hyperdrive, it must have a vacant hyperdrive slot modification.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":15000,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"hyper","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":"1"}},"flags":{"core":{"sourceId":"Item.MrCZaUig1puXcEVy"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Hyperdrive.webp","effects":[],"_id":"o9DmeVhCJNezkjdI"}
|
{"name":"Hyperdrive, Class 1.0","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>The hyperdrive is a propulsion system that allows a starship to reach hyperspeed and traverse the void between stars in the alternate dimension of hyperspace. For a starship to have a hyperdrive, it must have a vacant hyperdrive slot modification.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":15000,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"hyper","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":"1"}},"flags":{"core":{"sourceId":"Item.MrCZaUig1puXcEVy"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Hyperdrive.webp","effects":[],"_id":"o9DmeVhCJNezkjdI"}
|
||||||
{"_id":"oqB8RltTDjHnaS1Y","name":"Direct Power Coupling","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Direct power coupling has a central power capacitor that feeds power directly to each system.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":4100,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":0,"type":"powerc","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":"4"},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{"core":{"sourceId":"Item.MEiHVZHModsp5w0b"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Power%20Coupling.webp","effects":[]}
|
{"_id":"oqB8RltTDjHnaS1Y","name":"Direct Power Coupling","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>Direct power coupling has a central power capacitor that feeds power directly to each system.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":4100,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":0,"type":"powerc","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":"4"},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":""}},"flags":{"core":{"sourceId":"Item.MEiHVZHModsp5w0b"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Power%20Coupling.webp","effects":[]}
|
||||||
{"name":"Hyperdrive, Class 3","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>The hyperdrive is a propulsion system that allows a starship to reach hyperspeed and traverse the void between stars in the alternate dimension of hyperspace. For a starship to have a hyperdrive, it must have a vacant hyperdrive slot modification.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":7500,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"hyper","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":"3"}},"flags":{"core":{"sourceId":"Item.bzY549C3zaI3H6mt"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Hyperdrive.webp","effects":[],"_id":"rFm20P0THnzZRUxB"}
|
{"name":"Hyperdrive, Class 3","permission":{"default":0,"IpSq6HI4edO6e0Yw":3},"type":"equipment","data":{"description":{"value":"<p>The hyperdrive is a propulsion system that allows a starship to reach hyperspeed and traverse the void between stars in the alternate dimension of hyperspace. For a starship to have a hyperdrive, it must have a vacant hyperdrive slot modification.</p>","chat":"","unidentified":""},"source":"SotG","quantity":1,"weight":null,"price":7500,"attuned":false,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":null,"type":"hyper","dex":null},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":true,"properties":{"Absorptive":false,"Agile":false,"Anchor":false,"Avoidant":false,"Barbed":false,"Bulky":false,"Charging":false,"Concealing":false,"Cumbersome":false,"Gauntleted":false,"Imbalanced":false,"Impermeable":false,"Insulated":false,"Interlocking":false,"Lambent":false,"Lightweight":false,"Magnetic":false,"Obscured":false,"Obtrusive":false,"Powered":false,"Reactive":false,"Regulated":false,"Reinforced":false,"Responsive":false,"Rigid":false,"Silent":false,"Spiked":false,"Steadfast":false,"Strength":false,"Versatile":false},"capx":{"value":""},"hpperhd":{"value":""},"regrateco":{"value":""},"cscap":{"value":""},"sscap":{"value":""},"fuelcostsmod":{"value":""},"powdicerec":{"value":""},"hdclass":{"value":"3"}},"flags":{"core":{"sourceId":"Item.bzY549C3zaI3H6mt"}},"img":"systems/sw5e/packs/Icons/Starship%20Equipment/Hyperdrive.webp","effects":[],"_id":"rFm20P0THnzZRUxB"}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
{"_id":"6BN8l5E8QtYt103T","name":"Small Starship","permission":{"default":0,"yXqD5rPwgjXHtqeZ":3},"type":"starship","data":{"description":{"value":"<p>As the fighters cover his approach, Zik Beskin activates his targeting computer and ignores the explosions surrounding him, instead Focusing on the Destroyer's shield generator. It had to come down soon, or this fight was lost. Arsix, behind him, beeps and whirs, preparing the ion pulse missiles for the attack run, prewarming their engines and arming the warheads.</p>\n<p>Together a long time now, Beskin and Arsix had spilled their share of blood and oil, respectively, for the Rebellion--usually just while improving their ancient Ywing. But today, the blood and oil spilt wouldn't be their own. As Zik let fly a pair of missiles, he knew they would find their target. Today wasn't over just yet. This was her fourth sortie of the day, and Sheena was tired. The terrorists just kept coming. Every time she was about to shut her eyes a new wave of the Rebels came. And every time they did, she rushed to her TIE Interceptor and joined the alert fighters to take the fighters down before they could blow a hole in the planet's defenses. Every time they retreated before suffering heavy losses. But every time they came back. This last time she had decided to just nap in the cramped cockpit, so when the claxon rang out, she and her ship were basically ready to fly. This time she was going to end them quickly. As she repeatedly squeezed her trigger, she executed Koiogran turns and snap rolls galore, her laser blasts striking true, and the debris of A-wings, X-wings, and B-Wings--along with some frozen traitor remains--floated in space the next few days. At night, she leaned back against her beau, sipping some wine and watch-ing the beautiful streaks of light cross the sky as, piece by piece, the wreckage burned up in the atmosphere during reentry.</p>\n<p>R5-S1 locked down the loose stabilizer with his gripper arm as he angled the X-Wing's deflector shields. This ship took a firm gripper to get under control, but R5-S1 was up to the task. As his pilot, Veets, fired his last blast from the overheating cannons on the deployed s-foils, R5-S1 did the work of cooling off the guns, spooling up the hyperdrive, and running the calculations for lightspeed. It was time to go. Small ships have a tiny crew, often only a pilot and perhaps an astromech, but often strike above their weight class, a threat to small and large ships alike.</p>"},"size":"sm","tier":0,"hullDice":"d6","hullDiceStart":3,"hullDiceRolled":[6,4,4],"hullDiceUsed":0,"shldDice":"d6","shldDiceStart":3,"shldDiceRolled":[6,4,4],"shldDiceUsed":0,"pwrDice":"1","buildBaseCost":50000,"buildMinWorkforce":5,"upgrdCostMult":1,"upgrdMinWorkforce":1,"baseSpaceSpeed":300,"baseTurnSpeed":250,"crewMinWorkforce":1,"modBaseCap":20,"modMaxSuitesBase":-1,"modMaxSuitesMult":1,"modMaxSuiteCap":1,"modCostMult":1,"modMinWorkforce":2,"hardpointMult":1,"equipCostMult":1,"equipMinWorkforce":1,"cargoCap":2,"fuelCost":50,"fuelCap":10,"foodCap":10,"source":"SotG"},"folder":null,"sort":200001,"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Features/Small.webp","effects":[{"_id":"aVvrOFBux3t6WMEc","flags":{"dae":{"stackable":false,"transfer":true}},"changes":[{"key":"data.abilities.dex.value","value":2,"mode":2,"priority":1},{"key":"data.abilities.con.value","value":-2,"mode":2,"priority":1}],"disabled":false,"duration":{"startTime":null,"seconds":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"systems/sw5e/packs/Icons/Starship%20Features/Small.webp","label":"Small Starship","origin":"Item.Kgo48vXNxSZkWp7H","tint":"","transfer":true}]}
|
|
||||||
{"_id":"6liD1m4hqKSeS5sp","name":"Medium Starship","permission":{"default":0,"yXqD5rPwgjXHtqeZ":3},"type":"starship","data":{"description":{"value":"<p>The Freighter shudders with the blasts of cannon fire. Despite its maneuvers, the pilot can't quite shake the pursuit. The technician's efforts to reinforce the shields are failing. The mechanic is pumping the reactor for every scrap of energy it can generate. The operator is frantically making the final few calculations for the jump to hyperspace. Finally, just as the ship's shields dissipate, the pilot makes the gut call, jettisoning the illicit cargo. As it distracts and hampers the followers, the freighter shifts power to the thrusters and quickly flies away.</p>\n<p>As the pirates activate their tractor beam to attempt to capture the weaponless frigate and its exotic wares, the gunboat escort intercedes. It flies in the line of the tractor, breaking the lock on the frigate, and unleashes a volley of cannon fire. The pirates, incapable of withstanding the salvo, drop the tractor beam and retreat.</p>\n<p>As the gunboat pursues to be sure the pirates don't come back for a second bite at the apple, the crew finally locks on target and unleashes a long-range night-stinger missile, putting a permanent end to the pirates' illicit and unwelcome activities.</p>\n<p>The captain invites his guests into his well-stocked cantina. He eyes the opposition as they take in his ship, assessing their reactions and noting their expressions. Fully aware of the effect the opulent room has on the unprepared, he easily stifles his grin and gestures for the starry-eyed vis-itors to sit across from him. He indicates for the server to bring drinks as he casually leans back and puts his feet on the table, confident this deal will go his way. He presses a button on the tiny remote in his hand, causing hidden panels to slide away and reveal his mostly-legal wares. \"So,\" he says, \"just how many of these do you need, and where will I deliver them?\"</p>\n<p>Medium ships are the bread and butter of the closeknit group. They are large enough to accommodate all of the immediate needs of a crew, while at the same time being small enough to feel cozy.</p>"},"size":"med","tier":0,"hullDice":"d8","hullDiceStart":5,"hullDiceRolled":[8,5,5,5,5],"hullDiceUsed":0,"shldDice":"d8","shldDiceStart":5,"shldDiceRolled":[8,5,5,5,5],"shldDiceUsed":0,"pwrDice":"1","buildBaseCost":100000,"buildMinWorkforce":10,"upgrdCostMult":2,"upgrdMinWorkforce":5,"baseSpaceSpeed":300,"baseTurnSpeed":200,"crewMinWorkforce":1,"modBaseCap":30,"modMaxSuitesBase":3,"modMaxSuitesMult":1,"modMaxSuiteCap":4,"modCostMult":2,"modMinWorkforce":4,"hardpointMult":1.5,"equipCostMult":2,"equipMinWorkforce":2,"cargoCap":25,"fuelCost":100,"fuelCap":30,"foodCap":120,"source":"SotG"},"folder":null,"sort":300001,"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Features/Medium.webp","effects":[]}
|
|
||||||
{"_id":"FH8iBT4uujRUR0j7","name":"Gargantuan Starship","permission":{"default":0,"yXqD5rPwgjXHtqeZ":3},"type":"starship","data":{"description":{"value":"<p>The smaller ships retreat into the shadow of the blockade ship, fleeing an overwhelming foe. As the dreadnought's shields envelope them, they quickly turn and spring on their pursuers, utilizing the bulwark's shields as they unleash all of the firepower they have to bear.</p>\n<p>Meanwhile, the blockade ship unleashes a storm of electromagnetic energy from its antenna array, cutting of communications between enemy ships, effectively isolating the incoming forces from their distant fleet and from each other.</p>\n<p>In the center of the fleet, the command ship surveys the battlefield. Wherever the line wavers, the command ship quickly directs ships to reinforce. Finally, the formations of the enemy flag, and the command ship directs the fleet to capitalize on their failure as it determines and uploads targeting coordinates to its torpedo ships.</p>\n<p>The warship looms ominously over the battlefield as the two opposing armies crash. Despite the efforts of the enemy line, the warship closes into firing range of the capital ships. Having already determined an ordered targeting precedence, the operating crew confirms final firing solutions for the gunners as they charge up the main super-weapon on the prow of the ship. It unleashes its first devastating blast as the rest of its arsenal begins to lance out at secondary targets nearby.</p>\n<p>Gargantuan ships are the dreadnoughts that strike fear into the hearts of the faithless. They are the embodiment of indomitable might: a symbol of total and complete control.</p>"},"size":"grg","tier":0,"hullDice":"d20","hullDiceStart":11,"hullDiceRolled":[20,11,11,11,11,11,11,11,11,11,11],"hullDiceUsed":0,"shldDice":"d20","shldDiceStart":11,"shldDiceRolled":[20,11,11,11,11,11,11,11,11,11,11],"shldDiceUsed":0,"pwrDice":"1","buildBaseCost":1000000000,"buildMinWorkforce":10000,"upgrdCostMult":1000,"upgrdMinWorkforce":5000,"baseSpaceSpeed":300,"baseTurnSpeed":50,"crewMinWorkforce":80000,"modBaseCap":70,"modMaxSuitesBase":10,"modMaxSuitesMult":4,"modMaxSuiteCap":40000,"modCostMult":500,"modMinWorkforce":1000,"hardpointMult":3,"equipCostMult":500,"equipMinWorkforce":500,"cargoCap":200000,"fuelCost":100000,"fuelCap":1800,"foodCap":576000000,"source":"SotG"},"folder":null,"sort":600001,"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Features/Gargantuan.webp","effects":[{"_id":"3erWl25IS1iaKUiv","flags":{"dae":{"stackable":false,"transfer":true}},"changes":[{"key":"data.abilities.dex.value","value":-6,"mode":2,"priority":1},{"key":"data.abilities.con.value","value":6,"mode":2,"priority":1}],"disabled":false,"duration":{"startTime":null,"seconds":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"systems/sw5e/packs/Icons/Starship%20Features/Gargantuan.webp","label":"Gargantuan Starship","origin":"Item.KjK5001DYmUlFfPF","tint":"","transfer":true}]}
|
|
||||||
{"_id":"RFKvLuqE13INBxqd","name":"Large Starship","permission":{"default":0,"yXqD5rPwgjXHtqeZ":3},"type":"starship","data":{"description":{"value":"<p>Trapped in the capital ship's tractor beam, the ambassador frigate moves slowly towards the cruiser. The crew struggles to squeeze more power out of their reactor while the few marines on board take positions next to the hatches, wiping sweat from their brows as they check and re-check their weapons. Finally, bringing all the power the ship has to bare, the frigate is able to break the hold of the tractor beam and regain its trajectory, slowly but surely increasing the distance, before finally escaping the planet's moon and being able to jump to hyperspace. Shouts of joy echo down the ship's corridors and extra rations are ordered in celebration.</p>\n<p>Engines burning brightly, the corvette sprints through the blockade, trying to minimize the amount of fire its meager shields will have to absorb. Top and bottom turrets swivel to port, unleashing return fire against inbound interceptors. The ground drops from under everyone's feet as the artificial gravity systems flicker for a second as a pulse weapon detonates nearby. The high-pitched, distant whine of the reactor is barely audible over commands issuing from the bridge. The visual readouts indicated that they were now past the picket line, and the interceptors appeared to be breaking off, unsure of their ability to take on the much larger ship without the support of their battle stations. They'd made it. Looking back at the scopes, the coordinator's head hung down. They'd been the only ones to do so.</p>\n<p>As the Pelta-Class Picket ship danced between the larger destroyers and dreadnaughts, it continued its near constant barrage of heavy laser cannon fire, interspersed with individual launches of concussion missiles and proton torpedoes directed at vulnerable parts of the opposing fleet. If too many of those enemy guns came to bear on the the Pelta, it would be in trouble, but it's speed and it's relatively limited firepower made it a less-than juicy target. For now. But that is exactly what it's captain needed. Just a few more clicks and they would be in a perfect flanking position, able to pound the engines of the flag ship as soon as it's shields were brought down by the bombing squad beginning their run now.</p>\n<p>Large ships occupy the pinnacle of size for most private owner/operators in the galaxy. Large ships require an extensive crew and are costly to maintain, but can pack quite a punch and may house various suites and operation centers that allow the ship to operate as an impressive and mobile base of operations for wealthy individuals and successful adventurers.</p>"},"size":"lg","tier":0,"hullDice":"d10","hullDiceStart":7,"hullDiceRolled":[10,6,6,6,6,6,6],"hullDiceUsed":0,"shldDice":"d10","shldDiceStart":7,"shldDiceRolled":[10,6,6,6,6,6,6],"shldDiceUsed":0,"pwrDice":"1","buildBaseCost":1000000,"buildMinWorkforce":100,"upgrdCostMult":10,"upgrdMinWorkforce":50,"baseSpaceSpeed":300,"baseTurnSpeed":150,"crewMinWorkforce":200,"modBaseCap":50,"modMaxSuitesBase":3,"modMaxSuitesMult":2,"modMaxSuiteCap":400,"modCostMult":5,"modMinWorkforce":10,"hardpointMult":2,"equipCostMult":5,"equipMinWorkforce":5,"cargoCap":500,"fuelCost":1000,"fuelCap":300,"foodCap":240000,"source":"SotG"},"folder":null,"sort":400001,"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Features/Large.webp","effects":[{"_id":"NwpCHKQsAOq16TMa","flags":{"dae":{"stackable":false,"transfer":true}},"changes":[{"key":"data.abilities.dex.value","value":-2,"mode":2,"priority":1},{"key":"data.abilities.con.value","value":2,"mode":2,"priority":1}],"disabled":false,"duration":{"startTime":null,"seconds":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"systems/sw5e/packs/Icons/Starship%20Features/Large.webp","label":"Large Ship","origin":"Item.ywTvt1JEvDEoqG3A","tint":"","transfer":true}]}
|
|
||||||
{"_id":"pgmf0rMYLt4LQtfN","name":"Huge Starship","permission":{"default":0,"yXqD5rPwgjXHtqeZ":3},"type":"starship","data":{"description":{"value":"<p>The battleship's shields flicker as it absorbs the blows of the attacking fighters. It continues inexorably past them as it's point-defense system peppers it's vicinity with blaster fire to ward them off. As it a approaches the fragile medical frigate the fighters scramble to protect, its gunners lock on to the target before unleashing a fierce volley of turbolaser fire and snapping it in half.</p>\n<p>As the carrier leaves hyperspace, snubfighters deploy from its hangars in formations and move to intercept the space station's patrol fighters. Before the enemy craft have the opportunity to respond, the fighters fall upon them, quickly decimating their ranks. But before the snubfighters had even left the carrier, a second wave of small bombers had been prepping for takeoff. As they spew forth from the hangars, they quickly lock on to the space station and launch proton bombs, pulverizing the station in minutes.</p>\n<p>With the command given, the operator activates the interdictor's gravity well projectors. the lights inside dim almost imperceptibly as huge amounts of power is drawn from the reactor core and supplemental capacitors to the projectors. Accompanied by a lowpitched hum, the gravity well projectors power up.</p>\n<p>Minutes pass for the ship uneventfully, until finally a frigate lurches unceremoniously out of hyperspace into realspace in front of them. The ship then activates its tractor beam, trapping its quarry.</p>\n<p>Huge starships, regardless of their specific purpose, are the backbone of any military. They provide a mobile base of operations and function as a staging ground for the faction that controls them.</p>"},"size":"huge","tier":0,"hullDice":"d12","hullDiceStart":9,"hullDiceRolled":[12,7,7,7,7,7,7,7,7],"hullDiceUsed":0,"shldDice":"d12","shldDiceStart":9,"shldDiceRolled":[12,7,7,7,7,7,7,7,7],"shldDiceUsed":0,"pwrDice":"1","buildBaseCost":100000000,"buildMinWorkforce":1000,"upgrdCostMult":100,"upgrdMinWorkforce":500,"baseSpaceSpeed":300,"baseTurnSpeed":100,"crewMinWorkforce":4000,"modBaseCap":60,"modMaxSuitesBase":6,"modMaxSuitesMult":3,"modMaxSuiteCap":4000,"modCostMult":50,"modMinWorkforce":100,"hardpointMult":2,"equipCostMult":50,"equipMinWorkforce":50,"cargoCap":10000,"fuelCost":10000,"fuelCap":600,"foodCap":9600000,"source":"SotG"},"folder":null,"sort":500001,"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Features/Huge.webp","effects":[{"_id":"5dnyAxPsWqhRxR2v","flags":{"dae":{"stackable":false,"transfer":true}},"changes":[{"key":"data.abilities.dex.value","value":-4,"mode":2,"priority":1},{"key":"data.abilities.con.value","value":4,"mode":2,"priority":1}],"disabled":false,"duration":{"startTime":null,"seconds":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"systems/sw5e/packs/Icons/Starship%20Features/Huge.webp","label":"Huge Starship","origin":"Item.h8l5MTU8C1pDIQGL","tint":"","transfer":true}]}
|
|
||||||
{"_id":"zC4qM8JMmMzCjMJK","name":"Tiny Starship","permission":{"default":0,"yXqD5rPwgjXHtqeZ":3},"type":"starship","data":{"description":{"value":"<p>As the droid fighter ducked and weaved through flying blaster bolts and dodged the occasional flak, its optical sensors continued to become more and more occluded as they accumulated a dark haze of dust and smoke. Switching to active radar systems, the droid continued to relentlessly pursue its target: the fleeing Jedi and its small padawn that was barely larger than a youngling. Not that it mattered. Whether either target continued to draw breath a few seconds from now didn't really matter to the droid. Of course, that cessation was its goal, but it didn't really care about that goal...it's just what it was doing. What it had to do. But not exactly what it wanted to do. The droid passed power from its fully charged weapon's capacitors into its guns, which blazed to life and spewed plasma towards the small child's back. It was a perfect shot. How could it not be. That's what it did. Then the Jedi's own plasma weapon flared as it darted across the youngling's back, deflecting the droid's blasts directly back at it. Of course, that is what Jedi's did. And as the bolts tore through the droid ship's hull, into its main computer banks, across its power banks and out its engines, the droid's final computations let it know that it was plummeting to the earth at terminal velocity. It's just what it was doing. What it now had to do. But not exactly what it wanted to do.</p>\n<p>The small drone slips silently past the blockade, scanning the defensive formations as it goes. The only chance to save the people on embattled Neth-Feeno was to coordinate a supply drop with them. This little, remote-controlled stealth ship was their only hope. It had to reach the surface to get the plan and maps through. Then it had to return with a full readout of the defenses. Only then could a distracting assault be planned to cover the air drop. As the tiny craft floated past the final sensor pod mounted right next to the final turret canon, the monitoring crew let out a sigh, and wiped sweat from their brows. As their screens, already more static than signal, winked out as the craft's transmissions were cut-off as it passed completely into the black-out zone, the team leader turned to his squad and said, \"May the force be with us today.\"</p>\n<p>One thing all Tiny starships have in common is that they are unmanned. Sometimes they are controlled remotely, but more often they are controlled by droids.</p>"},"size":"tiny","tier":0,"hullDice":"d4","hullDiceStart":1,"hullDiceRolled":[4],"hullDiceUsed":0,"shldDice":"d4","shldDiceStart":1,"shldDiceRolled":[4],"shldDiceUsed":0,"pwrDice":"1","buildBaseCost":10000,"buildMinWorkforce":3,"upgrdCostMult":0.5,"upgrdMinWorkforce":1,"baseSpaceSpeed":300,"baseTurnSpeed":300,"crewMinWorkforce":0,"modBaseCap":10,"modMaxSuitesBase":0,"modMaxSuitesMult":0,"modMaxSuiteCap":0,"modCostMult":0.5,"modMinWorkforce":1,"hardpointMult":1,"equipCostMult":0.5,"equipMinWorkforce":1,"cargoCap":0,"fuelCost":25,"fuelCap":5,"foodCap":0,"source":"SotG"},"folder":null,"sort":100001,"flags":{},"img":"systems/sw5e/packs/Icons/Starship%20Features/Tiny.webp","effects":[{"_id":"5BCYRjiFKUZY8ke9","flags":{"dae":{"stackable":false,"transfer":true}},"changes":[{"key":"data.abilities.dex.value","value":4,"mode":2,"priority":1},{"key":"data.abilities.con.value","value":-4,"mode":2,"priority":1}],"disabled":false,"duration":{"startTime":null,"seconds":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"systems/sw5e/packs/Icons/Starship%20Features/Tiny.webp","label":"Tiny Starship","origin":"Item.k1Cxor0HkSEBpkuN","tint":"","transfer":true}]}
|
|
|
@ -137,6 +137,7 @@
|
||||||
{"_id":"uQ2AXesizBRcTjRl","name":"Ion Carbine","permission":{"default":0,"vXYkFWX6qzvOu2jc":3,"5TZqObbCr9nKC79s":3},"type":"weapon","data":{"description":{"value":"<p>Reload 16</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":8,"price":300,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"enemy"},"range":{"value":60,"long":240,"units":"ft"},"uses":{"value":0,"max":0,"per":""},"consume":{"type":"ammo","target":"","amount":1},"ability":"","actionType":"rwak","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[["1d3 + @mod","ion"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"simpleB","properties":{"amm":true,"aut":false,"bur":false,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":false,"dou":false,"fin":false,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":true,"ret":false,"shk":false,"sil":false,"spc":false,"str":false,"thr":false,"two":true,"ver":false,"vic":false,"mgc":false,"nodam":false,"faulldam":false},"proficient":false},"folder":"7rtfHBtXhhiRSS8u","sort":2600001,"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false}},"img":"systems/sw5e/packs/Icons/Simple%20Blasters/Ion%20Carbine.webp","effects":[]}
|
{"_id":"uQ2AXesizBRcTjRl","name":"Ion Carbine","permission":{"default":0,"vXYkFWX6qzvOu2jc":3,"5TZqObbCr9nKC79s":3},"type":"weapon","data":{"description":{"value":"<p>Reload 16</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":8,"price":300,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"enemy"},"range":{"value":60,"long":240,"units":"ft"},"uses":{"value":0,"max":0,"per":""},"consume":{"type":"ammo","target":"","amount":1},"ability":"","actionType":"rwak","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[["1d3 + @mod","ion"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"simpleB","properties":{"amm":true,"aut":false,"bur":false,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":false,"dou":false,"fin":false,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":true,"ret":false,"shk":false,"sil":false,"spc":false,"str":false,"thr":false,"two":true,"ver":false,"vic":false,"mgc":false,"nodam":false,"faulldam":false},"proficient":false},"folder":"7rtfHBtXhhiRSS8u","sort":2600001,"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false}},"img":"systems/sw5e/packs/Icons/Simple%20Blasters/Ion%20Carbine.webp","effects":[]}
|
||||||
{"_id":"v55dQl0raOAucwgP","name":"Vibromace","permission":{"default":0,"vXYkFWX6qzvOu2jc":3,"5TZqObbCr9nKC79s":3},"type":"weapon","data":{"description":{"value":"","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":12,"price":80,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"enemy"},"range":{"value":5,"long":null,"units":"ft"},"uses":{"value":0,"max":0,"per":""},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"mwak","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[["1d8 + @mod","kinetic"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"simpleVW","properties":{"amm":false,"aut":false,"bur":false,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":false,"dou":false,"fin":false,"fix":false,"foc":false,"hvy":true,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":false,"ret":false,"shk":false,"sil":false,"spc":false,"str":false,"thr":false,"two":true,"ver":false,"vic":false,"mgc":false,"nodam":false,"faulldam":false},"proficient":false},"folder":"7rtfHBtXhhiRSS8u","sort":6400001,"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false}},"img":"systems/sw5e/packs/Icons/Simple%20Vibroweapons/Vibromace.webp","effects":[]}
|
{"_id":"v55dQl0raOAucwgP","name":"Vibromace","permission":{"default":0,"vXYkFWX6qzvOu2jc":3,"5TZqObbCr9nKC79s":3},"type":"weapon","data":{"description":{"value":"","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":12,"price":80,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"enemy"},"range":{"value":5,"long":null,"units":"ft"},"uses":{"value":0,"max":0,"per":""},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"mwak","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[["1d8 + @mod","kinetic"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"simpleVW","properties":{"amm":false,"aut":false,"bur":false,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":false,"dou":false,"fin":false,"fix":false,"foc":false,"hvy":true,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":false,"ret":false,"shk":false,"sil":false,"spc":false,"str":false,"thr":false,"two":true,"ver":false,"vic":false,"mgc":false,"nodam":false,"faulldam":false},"proficient":false},"folder":"7rtfHBtXhhiRSS8u","sort":6400001,"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false}},"img":"systems/sw5e/packs/Icons/Simple%20Vibroweapons/Vibromace.webp","effects":[]}
|
||||||
{"_id":"w62Yd7ahdYyTH61q","name":"Shatter cannon","permission":{"default":0,"MmfWtlBdw3ij5wl9":3},"type":"weapon","data":{"description":{"value":"<p>Ammunition (range 80/320), Burst 4, Reload 8, Silent, Strength 15, Two-handed</p>","chat":"","unidentified":""},"source":"WH","quantity":1,"weight":24,"price":1300,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"creature"},"range":{"value":80,"long":320,"units":"ft"},"uses":{"value":0,"max":"0","per":""},"consume":{"type":"charges","target":"","amount":30},"ability":"","actionType":"rwak","attackBonus":"0","chatFlavor":"","critical":null,"damage":{"parts":[["1d10","kinetic"]],"versatile":""},"formula":"","save":{"ability":"dex","dc":null,"scaling":"dex"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"martialB","properties":{"amm":true,"aut":false,"bur":true,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":false,"dou":false,"fin":false,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":true,"ret":false,"shk":false,"sil":true,"spc":false,"str":true,"thr":false,"two":true,"ver":false,"vic":false,"nodam":false,"fulldam":false},"proficient":true},"folder":"7rtfHBtXhhiRSS8u","sort":6999220,"flags":{},"img":"systems/sw5e/packs/Icons/Martial%20Blasters/Shatter%20Cannon.webp","effects":[]}
|
{"_id":"w62Yd7ahdYyTH61q","name":"Shatter cannon","permission":{"default":0,"MmfWtlBdw3ij5wl9":3},"type":"weapon","data":{"description":{"value":"<p>Ammunition (range 80/320), Burst 4, Reload 8, Silent, Strength 15, Two-handed</p>","chat":"","unidentified":""},"source":"WH","quantity":1,"weight":24,"price":1300,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"creature"},"range":{"value":80,"long":320,"units":"ft"},"uses":{"value":0,"max":"0","per":""},"consume":{"type":"charges","target":"","amount":30},"ability":"","actionType":"rwak","attackBonus":"0","chatFlavor":"","critical":null,"damage":{"parts":[["1d10","kinetic"]],"versatile":""},"formula":"","save":{"ability":"dex","dc":null,"scaling":"dex"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"martialB","properties":{"amm":true,"aut":false,"bur":true,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":false,"dou":false,"fin":false,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":true,"ret":false,"shk":false,"sil":true,"spc":false,"str":true,"thr":false,"two":true,"ver":false,"vic":false,"nodam":false,"fulldam":false},"proficient":true},"folder":"7rtfHBtXhhiRSS8u","sort":6999220,"flags":{},"img":"systems/sw5e/packs/Icons/Martial%20Blasters/Shatter%20Cannon.webp","effects":[]}
|
||||||
|
{"_id":"woDLArHK5OZHsTeU","name":"Disguised Blade","permission":{"default":0,"5TZqObbCr9nKC79s":3},"type":"weapon","data":{"description":{"value":"","chat":"","unidentified":""},"source":"EC","quantity":1,"weight":1,"price":200,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"enemy"},"range":{"value":5,"long":null,"units":"ft"},"uses":{"value":0,"max":"0","per":""},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"mwak","attackBonus":"0","chatFlavor":"","critical":null,"damage":{"parts":[["1d4 + @mod","kinetic"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"martialVW","properties":{"amm":false,"aut":false,"bur":false,"def":false,"dex":false,"dir":false,"drm":true,"dgd":true,"dis":false,"dpt":false,"dou":false,"fin":true,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":true,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":false,"ret":false,"shk":false,"sil":false,"spc":false,"str":false,"thr":false,"two":false,"ver":false,"vic":false},"proficient":true},"folder":"7rtfHBtXhhiRSS8u","sort":7150001,"flags":{},"img":"systems/sw5e/packs/Icons/Disguised%20Blade,"effects":[]}
|
||||||
{"_id":"xfIWfVXfe5ZfD8S2","name":"IWS (Blaster)","permission":{"default":0,"MmfWtlBdw3ij5wl9":3},"type":"weapon","data":{"description":{"value":"<div>\n<p>The IWS is a heavy weapon that can fire in three different modes. On your turn, you can use your object interaction to switch between modes, detailed below.</p>\n<p>Antiarmor. While in this mode, rather than traditional power cells, the IWS fires grenades. When firing a grenade at long range, creatures within the radius of the grenade’s explosion have advantage on the saving throw.</p>\n<p>Blaster. While in this mode, the weapon uses traditional power cells.</p>\n<p>Sniper. While in this mode, the weapon uses traditional power cells.</p>\n</div>\n<div>Antiarmor: Special, Ammunition (range 60/240), reload 1, special</div>\n<div>Blaster: 1d8 Energy, Ammunition (range 80/320), reload 12</div>\n<div>Sniper: 1d12 Energy, Ammunition (range 120/480), reload 4</div>\n<div> </div>\n<div>Special, Strength 13, Two-handed</div>","chat":"","unidentified":""},"source":"WH","quantity":1,"weight":12,"price":7200,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":"space"},"range":{"value":80,"long":320,"units":"ft"},"uses":{"value":0,"max":"0","per":""},"consume":{"type":"charges","target":"","amount":20},"ability":"","actionType":"rwak","attackBonus":"0","chatFlavor":"","critical":null,"damage":{"parts":[["1d8 + @mod","energy"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"martialB","properties":{"amm":true,"aut":false,"bur":false,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":false,"dou":false,"fin":false,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":true,"ret":false,"shk":false,"sil":false,"spc":false,"str":true,"thr":false,"two":true,"ver":false,"vic":false,"nodam":false,"fulldam":false},"proficient":true},"folder":"7rtfHBtXhhiRSS8u","sort":6650001,"flags":{},"img":"systems/sw5e/packs/Icons/Martial%20Blasters/IWS.webp","effects":[]}
|
{"_id":"xfIWfVXfe5ZfD8S2","name":"IWS (Blaster)","permission":{"default":0,"MmfWtlBdw3ij5wl9":3},"type":"weapon","data":{"description":{"value":"<div>\n<p>The IWS is a heavy weapon that can fire in three different modes. On your turn, you can use your object interaction to switch between modes, detailed below.</p>\n<p>Antiarmor. While in this mode, rather than traditional power cells, the IWS fires grenades. When firing a grenade at long range, creatures within the radius of the grenade’s explosion have advantage on the saving throw.</p>\n<p>Blaster. While in this mode, the weapon uses traditional power cells.</p>\n<p>Sniper. While in this mode, the weapon uses traditional power cells.</p>\n</div>\n<div>Antiarmor: Special, Ammunition (range 60/240), reload 1, special</div>\n<div>Blaster: 1d8 Energy, Ammunition (range 80/320), reload 12</div>\n<div>Sniper: 1d12 Energy, Ammunition (range 120/480), reload 4</div>\n<div> </div>\n<div>Special, Strength 13, Two-handed</div>","chat":"","unidentified":""},"source":"WH","quantity":1,"weight":12,"price":7200,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":"space"},"range":{"value":80,"long":320,"units":"ft"},"uses":{"value":0,"max":"0","per":""},"consume":{"type":"charges","target":"","amount":20},"ability":"","actionType":"rwak","attackBonus":"0","chatFlavor":"","critical":null,"damage":{"parts":[["1d8 + @mod","energy"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"martialB","properties":{"amm":true,"aut":false,"bur":false,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":false,"dou":false,"fin":false,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":true,"ret":false,"shk":false,"sil":false,"spc":false,"str":true,"thr":false,"two":true,"ver":false,"vic":false,"nodam":false,"fulldam":false},"proficient":true},"folder":"7rtfHBtXhhiRSS8u","sort":6650001,"flags":{},"img":"systems/sw5e/packs/Icons/Martial%20Blasters/IWS.webp","effects":[]}
|
||||||
{"_id":"y6faozksI3Bhwnpq","name":"Bowcaster","permission":{"default":0,"vXYkFWX6qzvOu2jc":3,"5TZqObbCr9nKC79s":3},"type":"weapon","data":{"description":{"value":"<p>Burst 4, Reload 4, Strength 11</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":16,"price":400,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"enemy"},"range":{"value":50,"long":200,"units":"ft"},"uses":{"value":0,"max":"0","per":""},"consume":{"type":"charges","target":"","amount":1},"ability":"","actionType":"rwak","attackBonus":"0","chatFlavor":"","critical":null,"damage":{"parts":[["1d10 + @mod","energy"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"simpleB","properties":{"amm":true,"aut":false,"bur":true,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":false,"dou":false,"fin":false,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":true,"ret":false,"shk":false,"sil":false,"spc":false,"str":true,"thr":false,"two":true,"ver":false,"vic":false,"mgc":false,"nodam":false,"faulldam":false,"fulldam":false},"proficient":false},"folder":"7rtfHBtXhhiRSS8u","sort":800001,"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false}},"img":"systems/sw5e/packs/Icons/Simple%20Blasters/Bowcaster.webp","effects":[]}
|
{"_id":"y6faozksI3Bhwnpq","name":"Bowcaster","permission":{"default":0,"vXYkFWX6qzvOu2jc":3,"5TZqObbCr9nKC79s":3},"type":"weapon","data":{"description":{"value":"<p>Burst 4, Reload 4, Strength 11</p>","chat":"","unidentified":""},"source":"PHB","quantity":1,"weight":16,"price":400,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"enemy"},"range":{"value":50,"long":200,"units":"ft"},"uses":{"value":0,"max":"0","per":""},"consume":{"type":"charges","target":"","amount":1},"ability":"","actionType":"rwak","attackBonus":"0","chatFlavor":"","critical":null,"damage":{"parts":[["1d10 + @mod","energy"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"power"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"simpleB","properties":{"amm":true,"aut":false,"bur":true,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":false,"dou":false,"fin":false,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":true,"ret":false,"shk":false,"sil":false,"spc":false,"str":true,"thr":false,"two":true,"ver":false,"vic":false,"mgc":false,"nodam":false,"faulldam":false,"fulldam":false},"proficient":false},"folder":"7rtfHBtXhhiRSS8u","sort":800001,"flags":{"dynamiceffects":{"equipActive":false,"alwaysActive":false}},"img":"systems/sw5e/packs/Icons/Simple%20Blasters/Bowcaster.webp","effects":[]}
|
||||||
{"_id":"yVxRMON2OWIGeU4n","name":"Disruptorshiv","permission":{"default":0,"MmfWtlBdw3ij5wl9":3},"type":"weapon","data":{"description":{"value":"<p>Disruptive, Finesse, Shocking 13</p>","chat":"","unidentified":""},"source":"WH","quantity":1,"weight":1,"price":900,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"creature"},"range":{"value":5,"long":null,"units":"ft"},"uses":{"value":0,"max":"0","per":""},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"mwak","attackBonus":"0","chatFlavor":"","critical":null,"damage":{"parts":[["1d4 + @mod","kinetic"]],"versatile":""},"formula":"1d4","save":{"ability":"dex","dc":13,"scaling":"flat"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"martialVW","properties":{"amm":false,"aut":false,"bur":false,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":true,"dou":false,"fin":true,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":false,"ret":false,"shk":true,"sil":false,"spc":false,"str":false,"thr":false,"two":false,"ver":false,"vic":false,"nodam":false,"fulldam":false},"proficient":true},"folder":"7rtfHBtXhhiRSS8u","sort":6750001,"flags":{},"img":"systems/sw5e/packs/Icons/Martial%20Vibroweapons/Disruptorshiv.webp","effects":[]}
|
{"_id":"yVxRMON2OWIGeU4n","name":"Disruptorshiv","permission":{"default":0,"MmfWtlBdw3ij5wl9":3},"type":"weapon","data":{"description":{"value":"<p>Disruptive, Finesse, Shocking 13</p>","chat":"","unidentified":""},"source":"WH","quantity":1,"weight":1,"price":900,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":null,"units":""},"target":{"value":1,"width":null,"units":"","type":"creature"},"range":{"value":5,"long":null,"units":"ft"},"uses":{"value":0,"max":"0","per":""},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"mwak","attackBonus":"0","chatFlavor":"","critical":null,"damage":{"parts":[["1d4 + @mod","kinetic"]],"versatile":""},"formula":"1d4","save":{"ability":"dex","dc":13,"scaling":"flat"},"armor":{"value":10},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"weaponType":"martialVW","properties":{"amm":false,"aut":false,"bur":false,"def":false,"dex":false,"dir":false,"drm":false,"dgd":false,"dis":false,"dpt":true,"dou":false,"fin":true,"fix":false,"foc":false,"hvy":false,"hid":false,"ken":false,"lgt":false,"lum":false,"mig":false,"pic":false,"rap":false,"rch":false,"rel":false,"ret":false,"shk":true,"sil":false,"spc":false,"str":false,"thr":false,"two":false,"ver":false,"vic":false,"nodam":false,"fulldam":false},"proficient":true},"folder":"7rtfHBtXhhiRSS8u","sort":6750001,"flags":{},"img":"systems/sw5e/packs/Icons/Martial%20Vibroweapons/Disruptorshiv.webp","effects":[]}
|
||||||
|
|
|
@ -797,14 +797,3 @@ body.dark-theme .sw5e.sheet.actor.npc .swalt-sheet header div.creature-type:hove
|
||||||
body.dark-theme .sw5e.sheet.actor.npc .swalt-sheet header .experience {
|
body.dark-theme .sw5e.sheet.actor.npc .swalt-sheet header .experience {
|
||||||
color: #4f4f4f;
|
color: #4f4f4f;
|
||||||
}
|
}
|
||||||
body.dark-theme .sw5e.sheet.actor .swalt-sheet .panel.resources .traits .fuel-wrapper .fuel-label {
|
|
||||||
background: #D6D6D6;
|
|
||||||
color: #1C1C1C;
|
|
||||||
border: 1px solid #1C1C1C;
|
|
||||||
}
|
|
||||||
body.dark-theme .sw5e.sheet.actor .swalt-sheet .panel.resources .traits .fuel-wrapper .fuel {
|
|
||||||
background: #c40f0f;
|
|
||||||
}
|
|
||||||
body.dark-theme .sw5e.sheet.actor .swalt-sheet .panel.resources .traits .fuel-wrapper .fuel .fuel-bar {
|
|
||||||
background: #0dce0d;
|
|
||||||
}
|
|
|
@ -1757,78 +1757,3 @@ input[type="reset"]:disabled {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sw5e.sheet.actor .swalt-sheet .panel.resources .traits .fuel-wrapper {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 300px 100px;
|
|
||||||
width: 400px;
|
|
||||||
justify-self: end;
|
|
||||||
}
|
|
||||||
.sw5e.sheet.actor .swalt-sheet .panel.resources .traits .fuel-wrapper .fuel-label {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 14px;
|
|
||||||
width: 100%;
|
|
||||||
text-shadow: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
height: auto;
|
|
||||||
text-align: center;
|
|
||||||
margin-left: -2px;
|
|
||||||
border-radius: 0 4px 4px 0;
|
|
||||||
}
|
|
||||||
.sw5e.sheet.actor .swalt-sheet .panel.resources .traits .fuel-wrapper .fuel {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 4px;
|
|
||||||
height: 16px;
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.sw5e.sheet.actor .swalt-sheet .panel.resources .traits .fuel-wrapper .fuel .fuel-bar {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
input[type=range][orient=vertical] {
|
|
||||||
-webkit-appearance: slider-vertical;
|
|
||||||
width: 10px;
|
|
||||||
height: 60px !important;
|
|
||||||
padding: 0 0 !important;
|
|
||||||
background-color: #c40f0f !important;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
input[type=range][orient=vertical]::-webkit-slider-runnable-track {
|
|
||||||
-webkit-appearance: slider-vertical !important;
|
|
||||||
height: 60px !important;
|
|
||||||
width: 10px !important;
|
|
||||||
line-height: 60px !important;
|
|
||||||
padding-top: 0 !important;
|
|
||||||
padding-bottom: 0 !important;
|
|
||||||
margin-top: 0 0 !important;
|
|
||||||
border-radius: 3px !important;
|
|
||||||
background: linear-gradient(
|
|
||||||
to top,
|
|
||||||
#c40f0f 50%,
|
|
||||||
#0dce0d 50%
|
|
||||||
);
|
|
||||||
}
|
|
||||||
input[type=range][orient=vertical]::-webkit-slider-thumb {
|
|
||||||
-webkit-appearance: none !important;
|
|
||||||
background-color: #c40f0f !important;
|
|
||||||
margin-right: -4px !important;
|
|
||||||
margin-top: 0px !important;
|
|
||||||
cursor: grab !important;
|
|
||||||
border-radius: 0 0 0 0 !important;
|
|
||||||
width: 10px !important;
|
|
||||||
height: 5px !important;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
output {
|
|
||||||
display: block;
|
|
||||||
margin: 5px auto;
|
|
||||||
font-size:1.75em;
|
|
||||||
}
|
|
||||||
input .vertslider {
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
|
@ -784,14 +784,3 @@ body.light-theme .sw5e.sheet.actor.npc .swalt-sheet header div.creature-type:hov
|
||||||
body.light-theme .sw5e.sheet.actor.npc .swalt-sheet header .experience {
|
body.light-theme .sw5e.sheet.actor.npc .swalt-sheet header .experience {
|
||||||
color: #4f4f4f;
|
color: #4f4f4f;
|
||||||
}
|
}
|
||||||
body.light-theme .sw5e.sheet.actor .swalt-sheet .panel.resources .traits .fuel-wrapper .fuel-label {
|
|
||||||
background: #D6D6D6;
|
|
||||||
color: #1C1C1C;
|
|
||||||
border: 1px solid #1C1C1C;
|
|
||||||
}
|
|
||||||
body.light-theme .sw5e.sheet.actor .swalt-sheet .panel.resources .traits .fuel-wrapper .fuel {
|
|
||||||
background: #c40f0f;
|
|
||||||
}
|
|
||||||
body.light-theme .sw5e.sheet.actor .swalt-sheet .panel.resources .traits .fuel-wrapper .fuel .fuel-bar {
|
|
||||||
background: #0dce0d;
|
|
||||||
}
|
|
1
sw5e.css
1
sw5e.css
|
@ -429,7 +429,6 @@
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
.sw5e.sheet .items-list .item-name {
|
.sw5e.sheet .items-list .item-name {
|
||||||
flex: 2;
|
flex: 2;
|
||||||
|
|
152
sw5e.js
152
sw5e.js
|
@ -18,7 +18,7 @@ import { measureDistances } from "./module/canvas.js";
|
||||||
import Actor5e from "./module/actor/entity.js";
|
import Actor5e from "./module/actor/entity.js";
|
||||||
import Item5e from "./module/item/entity.js";
|
import Item5e from "./module/item/entity.js";
|
||||||
import CharacterImporter from "./module/characterImporter.js";
|
import CharacterImporter from "./module/characterImporter.js";
|
||||||
import { TokenDocument5e, Token5e } from "./module/token.js"
|
import {TokenDocument5e, Token5e} from "./module/token.js";
|
||||||
|
|
||||||
// Import Applications
|
// Import Applications
|
||||||
import AbilityTemplate from "./module/pixi/ability-template.js";
|
import AbilityTemplate from "./module/pixi/ability-template.js";
|
||||||
|
@ -46,9 +46,6 @@ import * as migrations from "./module/migration.js";
|
||||||
/* Foundry VTT Initialization */
|
/* Foundry VTT Initialization */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
// Keep on while migrating to Foundry version 0.8
|
|
||||||
CONFIG.debug.hooks = true;
|
|
||||||
|
|
||||||
Hooks.once("init", function () {
|
Hooks.once("init", function () {
|
||||||
console.log(`SW5e | Initializing SW5E System\n${SW5E.ASCII}`);
|
console.log(`SW5e | Initializing SW5E System\n${SW5E.ASCII}`);
|
||||||
|
|
||||||
|
@ -77,7 +74,7 @@ Hooks.once("init", function() {
|
||||||
Actor5e,
|
Actor5e,
|
||||||
Item5e,
|
Item5e,
|
||||||
TokenDocument5e,
|
TokenDocument5e,
|
||||||
Token5e,
|
Token5e
|
||||||
},
|
},
|
||||||
macros: macros,
|
macros: macros,
|
||||||
migrations: migrations,
|
migrations: migrations,
|
||||||
|
@ -91,11 +88,7 @@ Hooks.once("init", function() {
|
||||||
CONFIG.Token.documentClass = TokenDocument5e;
|
CONFIG.Token.documentClass = TokenDocument5e;
|
||||||
CONFIG.Token.objectClass = Token5e;
|
CONFIG.Token.objectClass = Token5e;
|
||||||
CONFIG.time.roundTime = 6;
|
CONFIG.time.roundTime = 6;
|
||||||
CONFIG.fontFamilies = [
|
CONFIG.fontFamilies = ["Engli-Besh", "Open Sans", "Russo One"];
|
||||||
"Engli-Besh",
|
|
||||||
"Open Sans",
|
|
||||||
"Russo One"
|
|
||||||
];
|
|
||||||
|
|
||||||
CONFIG.Dice.DamageRoll = dice.DamageRoll;
|
CONFIG.Dice.DamageRoll = dice.DamageRoll;
|
||||||
CONFIG.Dice.D20Roll = dice.D20Roll;
|
CONFIG.Dice.D20Roll = dice.D20Roll;
|
||||||
|
@ -140,19 +133,42 @@ Hooks.once("init", function() {
|
||||||
makeDefault: false,
|
makeDefault: false,
|
||||||
label: "SW5E.SheetClassNPCOld"
|
label: "SW5E.SheetClassNPCOld"
|
||||||
});
|
});
|
||||||
Actors.registerSheet("sw5e", ActorSheet5eStarship, {
|
// Actors.registerSheet("sw5e", ActorSheet5eStarship, {
|
||||||
types: ["starship"],
|
// types: ["starship"],
|
||||||
makeDefault: true,
|
// makeDefault: true,
|
||||||
label: "SW5E.SheetClassStarship"
|
// label: "SW5E.SheetClassStarship"
|
||||||
});
|
// });
|
||||||
Actors.registerSheet('sw5e', ActorSheet5eVehicle, {
|
Actors.registerSheet("sw5e", ActorSheet5eVehicle, {
|
||||||
types: ['vehicle'],
|
types: ["vehicle"],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "SW5E.SheetClassVehicle"
|
label: "SW5E.SheetClassVehicle"
|
||||||
});
|
});
|
||||||
Items.unregisterSheet("core", ItemSheet);
|
Items.unregisterSheet("core", ItemSheet);
|
||||||
Items.registerSheet("sw5e", ItemSheet5e, {
|
Items.registerSheet("sw5e", ItemSheet5e, {
|
||||||
types: ['weapon', 'equipment', 'consumable', 'tool', 'loot', 'class', 'power', 'feat', 'species', 'backpack', 'archetype', 'classfeature', 'background', 'fightingmastery', 'fightingstyle', 'lightsaberform', 'deployment', 'deploymentfeature', 'starship', 'starshipfeature', 'starshipmod', 'venture'],
|
types: [
|
||||||
|
"weapon",
|
||||||
|
"equipment",
|
||||||
|
"consumable",
|
||||||
|
"tool",
|
||||||
|
"loot",
|
||||||
|
"class",
|
||||||
|
"power",
|
||||||
|
"feat",
|
||||||
|
"species",
|
||||||
|
"backpack",
|
||||||
|
"archetype",
|
||||||
|
"classfeature",
|
||||||
|
"background",
|
||||||
|
"fightingmastery",
|
||||||
|
"fightingstyle",
|
||||||
|
"lightsaberform",
|
||||||
|
"deployment",
|
||||||
|
"deploymentfeature",
|
||||||
|
"starship",
|
||||||
|
"starshipfeature",
|
||||||
|
"starshipmod",
|
||||||
|
"venture"
|
||||||
|
],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "SW5E.SheetClassItem"
|
label: "SW5E.SheetClassItem"
|
||||||
});
|
});
|
||||||
|
@ -161,7 +177,6 @@ Hooks.once("init", function() {
|
||||||
return preloadHandlebarsTemplates();
|
return preloadHandlebarsTemplates();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Foundry VTT Setup */
|
/* Foundry VTT Setup */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -170,26 +185,73 @@ Hooks.once("init", function() {
|
||||||
* This function runs after game data has been requested and loaded from the servers, so entities exist
|
* This function runs after game data has been requested and loaded from the servers, so entities exist
|
||||||
*/
|
*/
|
||||||
Hooks.once("setup", function () {
|
Hooks.once("setup", function () {
|
||||||
|
|
||||||
// Localize CONFIG objects once up-front
|
// Localize CONFIG objects once up-front
|
||||||
const toLocalize = [
|
const toLocalize = [
|
||||||
"abilities", "abilityAbbreviations", "abilityActivationTypes", "abilityConsumptionTypes", "actorSizes", "alignments",
|
"abilities",
|
||||||
"armorProficiencies", "armorPropertiesTypes", "conditionTypes", "consumableTypes", "cover", "currencies", "damageResistanceTypes",
|
"abilityAbbreviations",
|
||||||
"damageTypes", "deploymentTypes", "distanceUnits", "equipmentTypes", "healingTypes", "itemActionTypes", "languages",
|
"abilityActivationTypes",
|
||||||
"limitedUsePeriods", "movementTypes", "movementUnits", "polymorphSettings", "proficiencyLevels", "senses", "skills",
|
"abilityConsumptionTypes",
|
||||||
"starshipSkills", "powerComponents", "powerLevels", "powerPreparationModes", "powerScalingModes", "powerSchools", "targetTypes",
|
"actorSizes",
|
||||||
"timePeriods", "toolProficiencies", "weaponProficiencies", "weaponProperties", "weaponSizes", "weaponTypes"
|
"alignments",
|
||||||
|
"armorProficiencies",
|
||||||
|
"armorPropertiesTypes",
|
||||||
|
"conditionTypes",
|
||||||
|
"consumableTypes",
|
||||||
|
"cover",
|
||||||
|
"currencies",
|
||||||
|
"damageResistanceTypes",
|
||||||
|
"damageTypes",
|
||||||
|
"distanceUnits",
|
||||||
|
"equipmentTypes",
|
||||||
|
"healingTypes",
|
||||||
|
"itemActionTypes",
|
||||||
|
"languages",
|
||||||
|
"limitedUsePeriods",
|
||||||
|
"movementTypes",
|
||||||
|
"movementUnits",
|
||||||
|
"polymorphSettings",
|
||||||
|
"proficiencyLevels",
|
||||||
|
"senses",
|
||||||
|
"skills",
|
||||||
|
"starshipRolessm",
|
||||||
|
"starshipRolesmed",
|
||||||
|
"starshipRoleslg",
|
||||||
|
"starshipRoleshuge",
|
||||||
|
"starshipRolesgrg",
|
||||||
|
"starshipSkills",
|
||||||
|
"powerComponents",
|
||||||
|
"powerLevels",
|
||||||
|
"powerPreparationModes",
|
||||||
|
"powerScalingModes",
|
||||||
|
"powerSchools",
|
||||||
|
"targetTypes",
|
||||||
|
"timePeriods",
|
||||||
|
"toolProficiencies",
|
||||||
|
"weaponProficiencies",
|
||||||
|
"weaponProperties",
|
||||||
|
"weaponSizes",
|
||||||
|
"weaponTypes"
|
||||||
];
|
];
|
||||||
|
|
||||||
// Exclude some from sorting where the default order matters
|
// Exclude some from sorting where the default order matters
|
||||||
const noSort = [
|
const noSort = [
|
||||||
"abilities", "alignments", "currencies", "deploymentTypes", "distanceUnits", "movementUnits", "itemActionTypes", "proficiencyLevels",
|
"abilities",
|
||||||
"limitedUsePeriods", "powerComponents", "powerLevels", "powerPreparationModes", "weaponTypes"
|
"alignments",
|
||||||
|
"currencies",
|
||||||
|
"distanceUnits",
|
||||||
|
"movementUnits",
|
||||||
|
"itemActionTypes",
|
||||||
|
"proficiencyLevels",
|
||||||
|
"limitedUsePeriods",
|
||||||
|
"powerComponents",
|
||||||
|
"powerLevels",
|
||||||
|
"powerPreparationModes",
|
||||||
|
"weaponTypes"
|
||||||
];
|
];
|
||||||
|
|
||||||
// Localize and sort CONFIG objects
|
// Localize and sort CONFIG objects
|
||||||
for (let o of toLocalize) {
|
for (let o of toLocalize) {
|
||||||
const localized = Object.entries(CONFIG.SW5E[o]).map(e => {
|
const localized = Object.entries(CONFIG.SW5E[o]).map((e) => {
|
||||||
return [e[0], game.i18n.localize(e[1])];
|
return [e[0], game.i18n.localize(e[1])];
|
||||||
});
|
});
|
||||||
if (!noSort.includes(o)) localized.sort((a, b) => a[1].localeCompare(b[1]));
|
if (!noSort.includes(o)) localized.sort((a, b) => a[1].localeCompare(b[1]));
|
||||||
|
@ -201,7 +263,7 @@ Hooks.once("setup", function() {
|
||||||
// add DND5E translation for module compatability
|
// add DND5E translation for module compatability
|
||||||
game.i18n.translations.DND5E = game.i18n.translations.SW5E;
|
game.i18n.translations.DND5E = game.i18n.translations.SW5E;
|
||||||
// console.log(game.settings.get("sw5e", "colorTheme"));
|
// console.log(game.settings.get("sw5e", "colorTheme"));
|
||||||
let theme = game.settings.get("sw5e", "colorTheme") + '-theme';
|
let theme = game.settings.get("sw5e", "colorTheme") + "-theme";
|
||||||
document.body.classList.add(theme);
|
document.body.classList.add(theme);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -210,7 +272,6 @@ Hooks.once("setup", function() {
|
||||||
* Once the entire VTT framework is initialized, check to see if we should perform a data migration
|
* Once the entire VTT framework is initialized, check to see if we should perform a data migration
|
||||||
*/
|
*/
|
||||||
Hooks.once("ready", function () {
|
Hooks.once("ready", function () {
|
||||||
|
|
||||||
// Wait to register hotbar drop hook on ready so that modules could register earlier if they want to
|
// Wait to register hotbar drop hook on ready so that modules could register earlier if they want to
|
||||||
Hooks.on("hotbarDrop", (bar, data, slot) => macros.create5eMacro(data, slot));
|
Hooks.on("hotbarDrop", (bar, data, slot) => macros.create5eMacro(data, slot));
|
||||||
|
|
||||||
|
@ -220,8 +281,11 @@ Hooks.once("ready", function() {
|
||||||
const NEEDS_MIGRATION_VERSION = "1.3.5.R1-A6";
|
const NEEDS_MIGRATION_VERSION = "1.3.5.R1-A6";
|
||||||
// Check for R1 SW5E versions
|
// Check for R1 SW5E versions
|
||||||
const SW5E_NEEDS_MIGRATION_VERSION = "R1-A6";
|
const SW5E_NEEDS_MIGRATION_VERSION = "R1-A6";
|
||||||
const COMPATIBLE_MIGRATION_VERSION = 0.80;
|
const COMPATIBLE_MIGRATION_VERSION = 0.8;
|
||||||
const needsMigration = currentVersion && (isNewerVersion(SW5E_NEEDS_MIGRATION_VERSION, currentVersion) || isNewerVersion(NEEDS_MIGRATION_VERSION, currentVersion));
|
const needsMigration =
|
||||||
|
currentVersion &&
|
||||||
|
(isNewerVersion(SW5E_NEEDS_MIGRATION_VERSION, currentVersion) ||
|
||||||
|
isNewerVersion(NEEDS_MIGRATION_VERSION, currentVersion));
|
||||||
if (!needsMigration && needsMigration !== "") return;
|
if (!needsMigration && needsMigration !== "") return;
|
||||||
|
|
||||||
// Perform the migration
|
// Perform the migration
|
||||||
|
@ -242,13 +306,11 @@ Hooks.on("canvasInit", function() {
|
||||||
SquareGrid.prototype.measureDistances = measureDistances;
|
SquareGrid.prototype.measureDistances = measureDistances;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Other Hooks */
|
/* Other Hooks */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
Hooks.on("renderChatMessage", (app, html, data) => {
|
Hooks.on("renderChatMessage", (app, html, data) => {
|
||||||
|
|
||||||
// Display action buttons
|
// Display action buttons
|
||||||
chat.displayChatActionButtons(app, html, data);
|
chat.displayChatActionButtons(app, html, data);
|
||||||
|
|
||||||
|
@ -261,7 +323,7 @@ Hooks.on("renderChatMessage", (app, html, data) => {
|
||||||
Hooks.on("getChatLogEntryContext", chat.addChatMessageContextOptions);
|
Hooks.on("getChatLogEntryContext", chat.addChatMessageContextOptions);
|
||||||
Hooks.on("renderChatLog", (app, html, data) => Item5e.chatListeners(html));
|
Hooks.on("renderChatLog", (app, html, data) => Item5e.chatListeners(html));
|
||||||
Hooks.on("renderChatPopout", (app, html, data) => Item5e.chatListeners(html));
|
Hooks.on("renderChatPopout", (app, html, data) => Item5e.chatListeners(html));
|
||||||
Hooks.on('getActorDirectoryEntryContext', Actor5e.addDirectoryContextOptions);
|
Hooks.on("getActorDirectoryEntryContext", Actor5e.addDirectoryContextOptions);
|
||||||
Hooks.on("renderSceneDirectory", (app, html, data) => {
|
Hooks.on("renderSceneDirectory", (app, html, data) => {
|
||||||
//console.log(html.find("header.folder-header"));
|
//console.log(html.find("header.folder-header"));
|
||||||
setFolderBackground(html);
|
setFolderBackground(html);
|
||||||
|
@ -283,24 +345,14 @@ Hooks.on("ActorSheet5eCharacterNew", (app, html, data) => {
|
||||||
console.log("renderSwaltSheet");
|
console.log("renderSwaltSheet");
|
||||||
});
|
});
|
||||||
// FIXME: This helper is needed for the vehicle sheet. It should probably be refactored.
|
// FIXME: This helper is needed for the vehicle sheet. It should probably be refactored.
|
||||||
Handlebars.registerHelper('getProperty', function (data, property) {
|
Handlebars.registerHelper("getProperty", function (data, property) {
|
||||||
return getProperty(data, property);
|
return getProperty(data, property);
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('round', function(value) {
|
|
||||||
return Math.floor(value);
|
|
||||||
});
|
|
||||||
|
|
||||||
Handlebars.registerHelper('debug', function(value) {
|
|
||||||
console.log(value)
|
|
||||||
return value;
|
|
||||||
})
|
|
||||||
|
|
||||||
function setFolderBackground(html) {
|
function setFolderBackground(html) {
|
||||||
html.find("header.folder-header").each(function () {
|
html.find("header.folder-header").each(function () {
|
||||||
let bgColor = $(this).css("background-color");
|
let bgColor = $(this).css("background-color");
|
||||||
if(bgColor == undefined)
|
if (bgColor == undefined) bgColor = "rgb(255,255,255)";
|
||||||
bgColor = "rgb(255,255,255)";
|
$(this).closest("li").css("background-color", bgColor);
|
||||||
$(this).closest('li').css("background-color", bgColor);
|
});
|
||||||
})
|
|
||||||
}
|
}
|
36
system.json
36
system.json
|
@ -109,42 +109,6 @@
|
||||||
"label": "Species Traits",
|
"label": "Species Traits",
|
||||||
"path": "./packs/packs/speciestraits.db",
|
"path": "./packs/packs/speciestraits.db",
|
||||||
"entity": "Item"
|
"entity": "Item"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "starshiparmor",
|
|
||||||
"label": "Starship Armor",
|
|
||||||
"path": "./packs/packs/starshiparmor.db",
|
|
||||||
"entity": "Item"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "starshipequipment",
|
|
||||||
"label": "Starship Equipment",
|
|
||||||
"path": "./packs/packs/starshipequipment.db",
|
|
||||||
"entity": "Item"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "starshipfeatures",
|
|
||||||
"label": "Starship Features",
|
|
||||||
"path": "./packs/packs/starshipfeatures.db",
|
|
||||||
"entity": "Item"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "starshipmodifications",
|
|
||||||
"label": "Starship Modifications",
|
|
||||||
"path": "./packs/packs/starshipmodifications.db",
|
|
||||||
"entity": "Item"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "starships",
|
|
||||||
"label": "Starship Types",
|
|
||||||
"path": "./packs/packs/starships.db",
|
|
||||||
"entity": "Item"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "starshipweapons",
|
|
||||||
"label": "Starship Weapons",
|
|
||||||
"path": "./packs/packs/starshipweapons.db",
|
|
||||||
"entity": "Item"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tables",
|
"name": "tables",
|
||||||
|
|
176
template.json
176
template.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"Actor": {
|
"Actor": {
|
||||||
"types": ["character", "npc", "starship", "vehicle"],
|
"types": ["character", "npc", "vehicle"],
|
||||||
"templates": {
|
"templates": {
|
||||||
"common": {
|
"common": {
|
||||||
"abilities": {
|
"abilities": {
|
||||||
|
@ -37,8 +37,8 @@
|
||||||
"value": 10,
|
"value": 10,
|
||||||
"min": 0,
|
"min": 0,
|
||||||
"max": 10,
|
"max": 10,
|
||||||
"temp": null,
|
"temp": 0,
|
||||||
"tempmax": null
|
"tempmax": 0
|
||||||
},
|
},
|
||||||
"init": {
|
"init": {
|
||||||
"value": 0,
|
"value": 0,
|
||||||
|
@ -89,15 +89,6 @@
|
||||||
},
|
},
|
||||||
"creature": {
|
"creature": {
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"rank": {
|
|
||||||
"total": 0,
|
|
||||||
"coord": 0,
|
|
||||||
"gunner": 0,
|
|
||||||
"mechanic": 0,
|
|
||||||
"operator": 0,
|
|
||||||
"pilot": 0,
|
|
||||||
"technician": 0
|
|
||||||
},
|
|
||||||
"senses": {
|
"senses": {
|
||||||
"darkvision": 0,
|
"darkvision": 0,
|
||||||
"blindsight": 0,
|
"blindsight": 0,
|
||||||
|
@ -425,119 +416,31 @@
|
||||||
"starship": {
|
"starship": {
|
||||||
"templates": ["common"],
|
"templates": ["common"],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"cost": {
|
"cargcap": 0,
|
||||||
"baseBuild": 0,
|
"crewcap": 0,
|
||||||
"baseUpgrade": 0,
|
"cscap": 0,
|
||||||
"multEquip": 0,
|
|
||||||
"multModification": 0,
|
|
||||||
"multUpgrade": 0
|
|
||||||
},
|
|
||||||
"death": {
|
"death": {
|
||||||
"failure": 0,
|
"failure": 0,
|
||||||
"success": 0
|
"success": 0
|
||||||
},
|
},
|
||||||
"deployment": {
|
|
||||||
"coord": {
|
|
||||||
"uuid": null,
|
|
||||||
"name": null,
|
|
||||||
"rank": null,
|
|
||||||
"prof": null
|
|
||||||
},
|
|
||||||
"gunner": {
|
|
||||||
"uuid": null,
|
|
||||||
"name": null,
|
|
||||||
"rank": null,
|
|
||||||
"prof": null
|
|
||||||
},
|
|
||||||
"mechanic": {
|
|
||||||
"uuid": null,
|
|
||||||
"name": null,
|
|
||||||
"rank": null,
|
|
||||||
"prof": null
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"uuid": null,
|
|
||||||
"name": null,
|
|
||||||
"rank": null,
|
|
||||||
"prof": null
|
|
||||||
},
|
|
||||||
"pilot": {
|
|
||||||
"uuid": null,
|
|
||||||
"name": null,
|
|
||||||
"rank": null,
|
|
||||||
"prof": null
|
|
||||||
},
|
|
||||||
"technician": {
|
|
||||||
"uuid": null,
|
|
||||||
"name": null,
|
|
||||||
"rank": null,
|
|
||||||
"prof": null
|
|
||||||
},
|
|
||||||
"crew": [],
|
|
||||||
"passenger": []
|
|
||||||
},
|
|
||||||
"equip": {
|
|
||||||
"armor": {
|
|
||||||
"dr": 0,
|
"dr": 0,
|
||||||
"maxDex": 99,
|
"engpow": 1,
|
||||||
"stealthDisadv": false
|
"exhaustion": 0,
|
||||||
},
|
"hsm": 1,
|
||||||
"hyperdrive": {
|
|
||||||
"class": null
|
|
||||||
},
|
|
||||||
"powerCoupling": {
|
|
||||||
"centralCap": 0,
|
|
||||||
"systemCap": 0
|
|
||||||
},
|
|
||||||
"reactor": {
|
|
||||||
"fuelMult": 1,
|
|
||||||
"powerRecDie": "1"
|
|
||||||
},
|
|
||||||
"size": {
|
|
||||||
"cargoCap": 0,
|
|
||||||
"crewMinWorkforce": 0,
|
|
||||||
"foodCap": 0
|
|
||||||
},
|
|
||||||
"shields": {
|
|
||||||
"capMult": 1,
|
|
||||||
"regenRateMult": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systemDamage": 0,
|
|
||||||
"fuel": {
|
|
||||||
"cap": 0,
|
|
||||||
"cost": 0,
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"hull": {
|
"hull": {
|
||||||
"die": "",
|
"die": "",
|
||||||
"dice": 0,
|
"dice": 0,
|
||||||
"dicemax": 0,
|
|
||||||
"formula":"",
|
"formula":"",
|
||||||
"value": null,
|
"value": null,
|
||||||
"max": null
|
"max": null
|
||||||
},
|
},
|
||||||
"mods": {
|
"mods": {
|
||||||
"capUsed": 0,
|
"open": 10,
|
||||||
"capLimit": 10,
|
"max": 10
|
||||||
"hardpoints":{
|
|
||||||
"open": 0,
|
|
||||||
"max": 0
|
|
||||||
},
|
|
||||||
"installed": 0,
|
|
||||||
"suites": {
|
|
||||||
"open": 0,
|
|
||||||
"max": 0,
|
|
||||||
"cap": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"power": {
|
|
||||||
"die": "",
|
|
||||||
"routing":{
|
|
||||||
"engines": 1,
|
|
||||||
"shields": 1,
|
|
||||||
"weapons": 1
|
|
||||||
},
|
},
|
||||||
|
"pwrdice": {
|
||||||
|
"pwrdie": "",
|
||||||
|
"recovery": 1,
|
||||||
"central": {
|
"central": {
|
||||||
"value": 0,
|
"value": 0,
|
||||||
"max": 0
|
"max": 0
|
||||||
|
@ -566,24 +469,21 @@
|
||||||
"shld": {
|
"shld": {
|
||||||
"die": "",
|
"die": "",
|
||||||
"dice": 0,
|
"dice": 0,
|
||||||
"dicemax": 0,
|
|
||||||
"depleted": false,
|
|
||||||
"formula":"",
|
"formula":"",
|
||||||
"value": null,
|
"value": null,
|
||||||
"max": null
|
"max": null
|
||||||
},
|
},
|
||||||
"used": false,
|
"shieldpow": 1,
|
||||||
"workforce": {
|
"sscap": 0,
|
||||||
"max": 0,
|
"suites": {
|
||||||
"minBuild": 0,
|
"open": 0,
|
||||||
"minEquip": 0,
|
"max": 0
|
||||||
"minModification": 0,
|
},
|
||||||
"minUpgrade": 0
|
"weaponpow": 1
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"details": {
|
"details": {
|
||||||
"tier": 0,
|
"tier": 0,
|
||||||
"role": [],
|
"role": "",
|
||||||
"source": ""
|
"source": ""
|
||||||
},
|
},
|
||||||
"skills": {
|
"skills": {
|
||||||
|
@ -607,7 +507,7 @@
|
||||||
"value": 0,
|
"value": 0,
|
||||||
"ability": "cha"
|
"ability": "cha"
|
||||||
},
|
},
|
||||||
"inf": {
|
"int": {
|
||||||
"value": 0,
|
"value": 0,
|
||||||
"ability": "cha"
|
"ability": "cha"
|
||||||
},
|
},
|
||||||
|
@ -645,7 +545,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"traits": {
|
"traits": {
|
||||||
"size": null
|
"size": "med"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vehicle": {
|
"vehicle": {
|
||||||
|
@ -932,7 +832,7 @@
|
||||||
"capx": {
|
"capx": {
|
||||||
"value": null
|
"value": null
|
||||||
},
|
},
|
||||||
"dmgred": {
|
"hpperhd": {
|
||||||
"value": null
|
"value": null
|
||||||
},
|
},
|
||||||
"regrateco": {
|
"regrateco": {
|
||||||
|
@ -1115,34 +1015,12 @@
|
||||||
"size": "",
|
"size": "",
|
||||||
"tier": 0,
|
"tier": 0,
|
||||||
"hullDice": "d6",
|
"hullDice": "d6",
|
||||||
"hullDiceStart": 3,
|
"hullDiceStart": 1,
|
||||||
"hullDiceRolled":[6,4,4],
|
|
||||||
"hullDiceUsed": 0,
|
"hullDiceUsed": 0,
|
||||||
"shldDice": "d6",
|
"shldDice": "d6",
|
||||||
"shldDiceStart": 3,
|
"shldDiceStart": 1,
|
||||||
"shldDiceRolled":[6,4,4],
|
|
||||||
"shldDiceUsed": 0,
|
"shldDiceUsed": 0,
|
||||||
"pwrDice": "1",
|
"pwrDice": "1",
|
||||||
"buildBaseCost": 50000,
|
|
||||||
"buildMinWorkforce": 5,
|
|
||||||
"upgrdCostMult": 1,
|
|
||||||
"upgrdMinWorkforce": 1,
|
|
||||||
"baseSpaceSpeed": 300,
|
|
||||||
"baseTurnSpeed": 250,
|
|
||||||
"crewMinWorkforce": 1,
|
|
||||||
"modBaseCap": 20,
|
|
||||||
"modMaxSuitesBase": 0,
|
|
||||||
"modMaxSuitesMult": 1,
|
|
||||||
"modMaxSuiteCap": 1,
|
|
||||||
"modCostMult": 1,
|
|
||||||
"modMinWorkforce": 2,
|
|
||||||
"hardpointMult": 2,
|
|
||||||
"equipCostMult": 1,
|
|
||||||
"equipMinWorkforce": 1,
|
|
||||||
"cargoCap": 2,
|
|
||||||
"fuelCost": 50,
|
|
||||||
"fuelCap": 10,
|
|
||||||
"foodCap": 10,
|
|
||||||
"source": "SotG"
|
"source": "SotG"
|
||||||
},
|
},
|
||||||
"starshipfeature": {
|
"starshipfeature": {
|
||||||
|
|
|
@ -8,26 +8,6 @@
|
||||||
<li class="filter-item" data-filter="reaction">{{localize "SW5E.Reaction"}}</li>
|
<li class="filter-item" data-filter="reaction">{{localize "SW5E.Reaction"}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>Coordinator: {{data.attributes.deployment.coord.name}}</li>
|
|
||||||
<li> Rank: {{data.attributes.deployment.coord.rank}}</li>
|
|
||||||
<li> Prof: {{data.attributes.deployment.coord.prof}}</li>
|
|
||||||
<li>Gunner: {{data.attributes.deployment.gunner.name}}</li>
|
|
||||||
<li> Rank: {{data.attributes.deployment.gunner.rank}}</li>
|
|
||||||
<li> Prof: {{data.attributes.deployment.gunner.prof}}</li>
|
|
||||||
<li>Mechanic: {{data.attributes.deployment.mechanic.name}}</li>
|
|
||||||
<li> Rank: {{data.attributes.deployment.mechanic.rank}}</li>
|
|
||||||
<li> Prof: {{data.attributes.deployment.mechanic.prof}}</li>
|
|
||||||
<li>Operator: {{data.attributes.deployment.operator.name}}</li>
|
|
||||||
<li> Rank: {{data.attributes.deployment.operator.rank}}</li>
|
|
||||||
<li> Prof: {{data.attributes.deployment.operator.prof}}</li>
|
|
||||||
<li>Pilot: {{data.attributes.deployment.pilot.name}}</li>
|
|
||||||
<li> Rank: {{data.attributes.deployment.pilot.rank}}</li>
|
|
||||||
<li> Prof: {{data.attributes.deployment.pilot.prof}}</li>
|
|
||||||
<li>Technician: {{data.attributes.deployment.technician.name}}</li>
|
|
||||||
<li> Rank: {{data.attributes.deployment.technician.rank}}</li>
|
|
||||||
<li> Prof: {{data.attributes.deployment.technician.prof}}</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<ol class="group-list">
|
<ol class="group-list">
|
||||||
{{#each sections as |section sid|}}
|
{{#each sections as |section sid|}}
|
||||||
|
|
|
@ -14,8 +14,14 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="summary">
|
<div class="summary">
|
||||||
|
<!-- <input type="text" name="data.traits.size" value="{{data.traits.size}}"
|
||||||
|
placeholder="{{lookup config.actorSizes data.traits.size}}" style="text-transform: capitalize;" /> -->
|
||||||
<span class="summary-input" style="text-transform: capitalize;font-family: 'Russo One';display: inline; height: auto; font-size: 17px; font-weight: 400; letter-spacing: 0.5px; line-height: 24px; color: #4f4f4f;">{{lookup config.actorSizes data.traits.size}}</span>
|
<span class="summary-input" style="text-transform: capitalize;font-family: 'Russo One';display: inline; height: auto; font-size: 17px; font-weight: 400; letter-spacing: 0.5px; line-height: 24px; color: #4f4f4f;">{{lookup config.actorSizes data.traits.size}}</span>
|
||||||
<input type="text" name="data.details.source" value="{{data.details.source}}" placeholder="{{ localize 'SW5E.Source' }}" />
|
<!-- <input type="text" name="data.details.role" value="{{data.details.role}}"
|
||||||
|
placeholder="{{ localize 'SW5E.Role' }}" /> -->
|
||||||
|
<span class="summary-input" style="text-transform: capitalize;font-family: 'Russo One';display: inline; height: auto; font-size: 17px; font-weight: 400; letter-spacing: 0.5px; line-height: 24px; color: #4f4f4f;">{{lookup config.starshipRolessm data.details.role}}</span>
|
||||||
|
<input type="text" name="data.details.source" value="{{data.details.source}}"
|
||||||
|
placeholder="{{ localize 'SW5E.Source' }}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="attributes">
|
<div class="attributes">
|
||||||
{{!-- ARMOR CLASS --}}
|
{{!-- ARMOR CLASS --}}
|
||||||
|
@ -25,10 +31,9 @@
|
||||||
<input class="ac-display" name="data.attributes.ac.value" type="text"
|
<input class="ac-display" name="data.attributes.ac.value" type="text"
|
||||||
value="{{data.attributes.ac.value}}" data-dtype="Number" placeholder="10" />
|
value="{{data.attributes.ac.value}}" data-dtype="Number" placeholder="10" />
|
||||||
</div>
|
</div>
|
||||||
<footer class="attribute-footer hit-dice" style="grid-template-columns: 1fr 1fr 1fr; column-gap: 4px;">
|
<footer class="attribute-footer proficiency">
|
||||||
<button type="button" class="rest short-rest">{{ localize "SW5E.Recharge" }}</button>
|
{{ localize "SW5E.Proficiency" }}
|
||||||
<button type="button" class="rest long-rest">{{ localize "SW5E.Refitting" }}</button>
|
{{numberFormat data.attributes.prof decimals=0 sign=true}}
|
||||||
<button type="button" class="rest long-rest">{{ localize "SW5E.ShieldRegen" }}</button>
|
|
||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -37,15 +42,14 @@
|
||||||
<h1 class="attribute-name rollable">{{ localize "SW5E.HullPoints" }}</h1>
|
<h1 class="attribute-name rollable">{{ localize "SW5E.HullPoints" }}</h1>
|
||||||
<div class="attribute-value multiple">
|
<div class="attribute-value multiple">
|
||||||
<input name="data.attributes.hp.value" type="text" value="{{data.attributes.hp.value}}"
|
<input name="data.attributes.hp.value" type="text" value="{{data.attributes.hp.value}}"
|
||||||
data-dtype="Number" class="value-number" />
|
data-dtype="Number" placeholder="0" class="value-number" />
|
||||||
<span class="value-separator">/</span>
|
<span class="value-separator">/</span>
|
||||||
<input name="data.attributes.hp.max" type="text" value="{{data.attributes.hp.max}}"
|
<input name="data.attributes.hp.max" type="text" value="{{data.attributes.hp.max}}"
|
||||||
data-dtype="Number" class="value-number" />
|
data-dtype="Number" placeholder="0" class="value-number" />
|
||||||
</div>
|
</div>
|
||||||
<footer class="attribute-footer" style="line-height: 12px; height: 12px; text-align: center; font-family: 'Russo One';">
|
<footer class="attribute-footer hit-points">
|
||||||
<!-- <input name="data.attributes.hull.formula" class="hpformula" type="text"
|
<input name="data.attributes.hull.formula" class="hpformula" type="text"
|
||||||
placeholder="{{ localize 'SW5E.HullPointsFormula' }}" value="{{data.attributes.hull.formula}}" style="min-width: 150px;" /> -->
|
placeholder="{{ localize 'SW5E.HullPointsFormula' }}" value="{{data.attributes.hull.formula}}" style="min-width: 150px;" />
|
||||||
<strong>{{localize "SW5E.HullDice"}}:</strong> {{data.attributes.hull.dice}}{{data.attributes.hull.die}}
|
|
||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -53,19 +57,20 @@
|
||||||
<section class="attribute health" style="box-sizing: border-box; width: 150px;">
|
<section class="attribute health" style="box-sizing: border-box; width: 150px;">
|
||||||
<h1 class="attribute-name rollable">{{ localize "SW5E.ShieldPoints" }}</h1>
|
<h1 class="attribute-name rollable">{{ localize "SW5E.ShieldPoints" }}</h1>
|
||||||
<div class="attribute-value multiple">
|
<div class="attribute-value multiple">
|
||||||
<input name="data.attributes.hp.temp" type="text" value="{{round data.attributes.hp.temp}}"
|
<input name="data.attributes.hp.temp" type="text" value="{{data.attributes.hp.temp}}"
|
||||||
data-dtype="Number" placeholder="0" class="value-number" />
|
data-dtype="Number" placeholder="0" class="value-number" />
|
||||||
<span class="value-separator">/</span>
|
<span class="value-separator">/</span>
|
||||||
<input name="data.attributes.hp.tempmax" type="text" value="{{round data.attributes.hp.tempmax}}"
|
<input name="data.attributes.hp.tempmax" type="text" value="{{data.attributes.hp.tempmax}}"
|
||||||
data-dtype="Number" placeholder="0" class="value-number" />
|
data-dtype="Number" placeholder="0" class="value-number" />
|
||||||
</div>
|
</div>
|
||||||
<footer class="attribute-footer" style="line-height: 12px; height: 12px; text-align: center; font-family: 'Russo One';">
|
<footer class="attribute-footer hit-points">
|
||||||
<!-- <input name="data.attributes.shld.formula" class="hpformula" type="text"
|
<input name="data.attributes.shld.Formula" class="hpformula" type="text"
|
||||||
placeholder="{{ localize 'SW5E.ShieldPointsFormula' }}" value="{{data.attributes.shld.formula}}" style="min-width: 150px;" /> -->
|
placeholder="{{ localize 'SW5E.ShieldPointsFormula' }}" value="{{data.attributes.shld.formula}}" style="min-width: 150px;" />
|
||||||
<strong>{{localize "SW5E.ShieldDice"}}: </strong> {{data.attributes.shld.dice}}{{data.attributes.shld.die}}
|
|
||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{{!-- SPEED / MOVEMENT TYPES --}}
|
||||||
|
|
||||||
<section style="box-sizing: border-box; width: 150px;">
|
<section style="box-sizing: border-box; width: 150px;">
|
||||||
<h1>{{ localize "SW5E.Movement" }}
|
<h1>{{ localize "SW5E.Movement" }}
|
||||||
<a class="config-button" data-action="movement" title="{{localize 'SW5E.MovementConfig'}}"><i class="fas fa-cog"></i></a>
|
<a class="config-button" data-action="movement" title="{{localize 'SW5E.MovementConfig'}}"><i class="fas fa-cog"></i></a>
|
||||||
|
@ -140,79 +145,95 @@
|
||||||
<section class="panel resources">
|
<section class="panel resources">
|
||||||
<h1>Resources and Traits</h1>
|
<h1>Resources and Traits</h1>
|
||||||
<div class="traits">
|
<div class="traits">
|
||||||
<table style="border: none; background: none;">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<label>
|
<label>
|
||||||
{{localize "SW5E.VehicleCargoCapacity"}}: {{data.attributes.equip.size.cargoCap}} tons
|
{{localize "SW5E.Size"}}
|
||||||
|
<select class="actor-size" name="data.traits.size">
|
||||||
|
{{#select data.traits.size}}
|
||||||
|
<option value=""> </option>
|
||||||
|
{{#each config.actorSizes as |label size|}}
|
||||||
|
<option value="{{size}}">{{label}}</option>
|
||||||
|
{{/each}}
|
||||||
|
{{/select}}
|
||||||
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<label>
|
<label>
|
||||||
{{localize "SW5E.CrewCap"}}: {{data.attributes.equip.size.crewMinWorkforce}}
|
{{localize "SW5E.Role"}}
|
||||||
|
<select class="actor-size" name="data.details.role">
|
||||||
|
{{#select data.details.role}}
|
||||||
|
<option value=""> </option>
|
||||||
|
{{#if isTiny}}
|
||||||
|
{{#each config.starshipRolestiny as |label role|}}
|
||||||
|
<option value="{{role}}">{{label}}</option>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if isSmall}}
|
||||||
|
{{#each config.starshipRolessm as |label role|}}
|
||||||
|
<option value="{{role}}">{{label}}</option>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if isMedium}}
|
||||||
|
{{#each config.starshipRolesmed as |label role|}}
|
||||||
|
<option value="{{role}}">{{label}}</option>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if isLarge}}
|
||||||
|
{{#each config.starshipRoleslg as |label role|}}
|
||||||
|
<option value="{{role}}">{{label}}</option>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if isHuge}}
|
||||||
|
{{#each config.starshipRoleshuge as |label role|}}
|
||||||
|
<option value="{{role}}">{{label}}</option>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if isGargantuan}}
|
||||||
|
{{#each config.starshipRolesgrg as |label role|}}
|
||||||
|
<option value="{{role}}">{{label}}</option>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
{{/select}}
|
||||||
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
<br />
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<label>
|
<label>
|
||||||
{{localize "SW5E.FuelCostPerUnit"}}: {{data.attributes.fuel.cost}} cr/unit
|
{{localize "SW5E.HullDice"}}: <input class="hpformula" style="max-width:50px;" name="data.attributes.hd" value="{{data.attributes.hd}}" placeholder="{{data.attributes.hd}}" />
|
||||||
</label>
|
</label>
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<button type="button" class="rest long-rest burnfuel" style="width:40%;" title="Burn 1 Unit of Fuel">{{ localize "SW5E.BurnFuel" }}</button>
|
|
||||||
<button type="button" class="rest long-rest refuel" style="width:40%;" title="Refuel">{{ localize "SW5E.Refuel" }}</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<label>
|
<label>
|
||||||
{{localize "SW5E.FuelCapacity"}}
|
{{localize "SW5E.ShieldDice"}}: <input class="hpformula" style="max-width:50px;" name="data.attributes.sd" value="{{data.attributes.sd}}" placeholder="{{data.attributes.sd}}" />
|
||||||
</label>
|
</label>
|
||||||
{{#with data.attributes.fuel}}
|
<label>
|
||||||
<div class="fuel-wrapper" title="Fuel">
|
{{localize "SW5E.PowerDice"}}:
|
||||||
<div class="fuel {{#if fueled}}fueled{{/if}}">
|
<select class="actor-size" name="data.attributes.pd">
|
||||||
<span class="fuel-bar" style="width:{{pct}}%"></span>
|
{{#select data.attributes.pd}}
|
||||||
|
<option value=""> </option>
|
||||||
<i class="fuel-breakpoint fuel-20 arrow-up"></i>
|
{{#each config.powerDieTypes as |pd|}}
|
||||||
<i class="fuel-breakpoint fuel-20 arrow-down"></i>
|
<option value="{{pd}}">{{pd}}</option>
|
||||||
<i class="fuel-breakpoint fuel-40 arrow-up"></i>
|
{{/each}}
|
||||||
<i class="fuel-breakpoint fuel-40 arrow-down"></i>
|
{{/select}}
|
||||||
<i class="fuel-breakpoint fuel-60 arrow-up"></i>
|
</select>
|
||||||
<i class="fuel-breakpoint fuel-60 arrow-down"></i>
|
</label>
|
||||||
<i class="fuel-breakpoint fuel-80 arrow-up"></i>
|
<br />
|
||||||
<i class="fuel-breakpoint fuel-80 arrow-down"></i>
|
<label>
|
||||||
</div>
|
{{localize "SW5E.DmgRed"}}: <input class="hpformula" style="max-width:30px;" name="data.attributes.dr" value="{{data.attributes.dr}}" placeholder="0" />
|
||||||
<span class="fuel-label">{{value}} / {{cap}} units</span>
|
</label>
|
||||||
</div>
|
<label>
|
||||||
{{/with}}
|
{{localize "SW5E.VehicleCargoCapacity"}}: <input class="hpformula" style="max-width:60px;" name="data.attributes.cargcap" value="{{data.attributes.cargcap}}" placeholder="0" /> tons
|
||||||
<!-- <label>
|
</label>
|
||||||
|
<br />
|
||||||
|
<label>
|
||||||
|
{{localize "SW5E.CrewCap"}}: <input class="hpformula" style="max-width:60px;" name="data.attributes.crewcap" value="{{data.attributes.crewcap}}" placeholder="0" />
|
||||||
|
</label>
|
||||||
|
<br />
|
||||||
|
<label>
|
||||||
{{localize "SW5E.CentStorageCapacity"}}: <input class="hpformula" style="max-width:30px;" name="data.attributes.cscap" value="{{data.attributes.cscap}}" placeholder="0" />
|
{{localize "SW5E.CentStorageCapacity"}}: <input class="hpformula" style="max-width:30px;" name="data.attributes.cscap" value="{{data.attributes.cscap}}" placeholder="0" />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
{{localize "SW5E.SysStorageCapacity"}}: <input class="hpformula" style="max-width:30px;" name="data.attributes.sscap" value="{{data.attributes.sscap}}" placeholder="0" />
|
{{localize "SW5E.SysStorageCapacity"}}: <input class="hpformula" style="max-width:30px;" name="data.attributes.sscap" value="{{data.attributes.sscap}}" placeholder="0" />
|
||||||
</label> -->
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<h1>{{localize "SW5E.PowerRouting"}}</h1>
|
<h1>{{localize "SW5E.PowerRouting"}}</h1>
|
||||||
<div class="traits">
|
<div class="traits">
|
||||||
<table style="border:none;">
|
<label>
|
||||||
<tr>
|
|
||||||
<td align="center"><strong>{{localize "SW5E.EnginePl"}}</strong></td>
|
|
||||||
<td rowspan=3><input type="range" orient="vertical" id="engineslidervalue" class="vertslider" value={{data.attributes.power.routing.engines}} step="1" min="0" max="2" ></td>
|
|
||||||
<td align="center"><strong>{{localize "SW5E.EquipmentShieldProficiency"}}</strong></td>
|
|
||||||
<td rowspan=3><input type="range" orient="vertical" id="shieldslidervalue" class="vertslider" value={{data.attributes.power.routing.shields}} step="1" min="0" max="2" ></td>
|
|
||||||
<td align="center"><strong>{{localize "SW5E.ItemTypeWeaponPl"}}</strong></td>
|
|
||||||
<td rowspan=3><input type="range" orient="vertical" id="weaponslidervalue" class="vertslider" value={{data.attributes.power.routing.weapons}} step="1" min="0" max="2" ></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td rowspan=2 align="center"><strong><output for=value id="engineslideroutput">=</output></td>
|
|
||||||
<td rowspan=2 align="center"><strong><output for=value id="shieldslideroutput">=</output></td>
|
|
||||||
<td rowspan=2 align="center"><strong><output for=value id="weaponslideroutput">=</output></td>
|
|
||||||
</tr>
|
|
||||||
<tr></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- <label>
|
|
||||||
{{localize "SW5E.EnginePl"}}:
|
{{localize "SW5E.EnginePl"}}:
|
||||||
<select name="data.attributes.engpow">
|
<select name="data.attributes.engpow">
|
||||||
{{#select data.attributes.engpow}}
|
{{#select data.attributes.engpow}}
|
||||||
|
@ -241,24 +262,6 @@
|
||||||
<option value="0.5">↓</option>
|
<option value="0.5">↓</option>
|
||||||
{{/select}}
|
{{/select}}
|
||||||
</select>
|
</select>
|
||||||
</label>
|
|
||||||
<br /> -->
|
|
||||||
<table style="border: none; width: 400px;">
|
|
||||||
<tr><th colspan=3 align="left">{{localize "SW5E.PowerDieAlloc"}}</th><th colspan=3 align="right">
|
|
||||||
<label>
|
|
||||||
{{localize "SW5E.PowerDie"}}: {{data.attributes.power.die}}
|
|
||||||
</label>
|
|
||||||
</tr>
|
|
||||||
<tr><th style="border-top: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;">Central</th><th style="border-top: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;">Comms</th><th style="border-top: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;">Engines</th><th style="border-top: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;">Sensors</th><th style="border-top: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;">Shields</th><th style="border-top: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;">Weapons</th>
|
|
||||||
<tr>
|
|
||||||
<td style="border-bottom: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;"><input class="hpformula" style="max-width:25px;" name="data.attributes.power.central.value" value="{{data.attributes.power.central.value}}" placeholder="{{data.attributes.power.central.max}}" /> / {{data.attributes.power.central.max}}</td>
|
|
||||||
<td style="border-bottom: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;"><input class="hpformula" style="max-width:25px;" name="data.attributes.power.comms.value" value="{{data.attributes.power.comms.value}}" placeholder="{{data.attributes.power.comms.max}}" /> / {{data.attributes.power.comms.max}}</td>
|
|
||||||
<td style="border-bottom: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;"><input class="hpformula" style="max-width:25px;" name="data.attributes.power.engines.value" value="{{data.attributes.power.engines.value}}" placeholder="{{data.attributes.power.engines.max}}" /> / {{data.attributes.power.engines.max}}</td>
|
|
||||||
<td style="border-bottom: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;"><input class="hpformula" style="max-width:25px;" name="data.attributes.power.sensors.value" value="{{data.attributes.power.sensors.value}}" placeholder="{{data.attributes.power.sensors.max}}" /> / {{data.attributes.power.sensors.max}}</td>
|
|
||||||
<td style="border-bottom: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;"><input class="hpformula" style="max-width:25px;" name="data.attributes.power.shields.value" value="{{data.attributes.power.shields.value}}" placeholder="{{data.attributes.power.shields.max}}" /> / {{data.attributes.power.shields.max}}</td>
|
|
||||||
<td style="border-bottom: 2px solid #0d99cc; border-left: 2px solid #0d99cc; border-right: 2px solid #0d99cc;"><input class="hpformula" style="max-width:25px;" name="data.attributes.power.weapons.value" value="{{data.attributes.power.weapons.value}}" placeholder="{{data.attributes.power.weapons.max}}" /> / {{data.attributes.power.weapons.max}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<section class="counters" style="border: none; margin: 8px 0; display: grid; grid-template-columns: repeat(2, 1fr);">
|
<section class="counters" style="border: none; margin: 8px 0; display: grid; grid-template-columns: repeat(2, 1fr);">
|
||||||
|
@ -280,24 +283,21 @@
|
||||||
<div class="counter">
|
<div class="counter">
|
||||||
<h4>{{ localize "SW5E.SystemDrainage" }}</h4>
|
<h4>{{ localize "SW5E.SystemDrainage" }}</h4>
|
||||||
<div class="counter-value" style="text-align: left;">
|
<div class="counter-value" style="text-align: left;">
|
||||||
<input type="text" name="data.attributes.systemDamage" data-dtype="Number" placeholder="0"
|
<input type="text" name="data.attributes.exhaustion" data-dtype="Number" placeholder="0"
|
||||||
value="{{data.attributes.systemDamage}}" />
|
value="{{data.attributes.exhaustion}}" />
|
||||||
</div>
|
</div>
|
||||||
</div></section>
|
</div></section>
|
||||||
<h1>{{localize "SW5E.StarshipmodPl"}}</h1>
|
<h1>{{localize "SW5E.StarshipmodPl"}}</h1>
|
||||||
<div class="traits">
|
<div class="traits">
|
||||||
<label>
|
<label>
|
||||||
{{localize "SW5E.ModCap"}}: <input class="hpformula" style="max-width:30px;" name="data.attributes.mods.capUsed" value="{{data.attributes.mods.capUsed}}" placeholder="10" />/ {{data.attributes.mods.capLimit}}
|
{{localize "SW5E.ModCap"}}: <input class="hpformula" style="max-width:30px;" name="data.attributes.mods.open" value="{{data.attributes.mods.open}}" placeholder="10" />/<input class="hpformula" style="max-width:30px;" name="data.attributes.mods.max" value="{{data.attributes.mods.max}}" placeholder="10" />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
{{localize "SW5E.SuiteCap"}}: <input class="hpformula" style="max-width:30px;" name="data.attributes.suites.open" value="{{data.attributes.mods.suites.open}}" placeholder="0" />/ {{data.attributes.mods.suites.max}} - Occupancy: {{data.attributes.mods.suites.cap}}
|
{{localize "SW5E.SuiteCap"}}: <input class="hpformula" style="max-width:30px;" name="data.attributes.suites.open" value="{{data.attributes.suites.open}}" placeholder="0" />/<input class="hpformula" style="max-width:30px;" name="data.attributes.suites.max" value="{{data.attributes.suites.max}}" placeholder="0" />
|
||||||
</label>
|
</label>
|
||||||
<br />
|
<br />
|
||||||
<label>
|
<label>
|
||||||
{{localize "SW5E.HardpointsPerRound"}}: {{data.attributes.mods.hardpoints.max}}
|
{{localize "SW5E.HardpointSizeMod"}}: <input class="hpformula" style="max-width:30px;" name="data.attributes.hsm" value="{{data.attributes.hsm}}" placeholder="0" />
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
{{localize "SW5E.DmgRed"}}: {{data.attributes.equip.armor.dr}}
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
<div class="dialog-content">
|
|
||||||
{{#each content.i18n}}
|
|
||||||
<div>
|
|
||||||
<label class="checkbox" for="{{@key}}">
|
|
||||||
<input type="checkbox" id="{{@key}}" name="{{@key}}" {{checked (lookup ../content.options @key)}}>
|
|
||||||
{{this}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
<div class="dialog-buttons">
|
|
||||||
{{#each buttons as |button id|}}
|
|
||||||
<button class="dialog-button" data-button="{{id}}">
|
|
||||||
{{{button.icon}}}
|
|
||||||
{{{button.label}}}
|
|
||||||
</button>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
|
@ -24,10 +24,6 @@
|
||||||
<label>{{localize "SW5E.MovementRoll"}}</label>
|
<label>{{localize "SW5E.MovementRoll"}}</label>
|
||||||
<input name="data.attributes.movement.roll" type="number" step="0.1" value="{{movement.roll}}"/>
|
<input name="data.attributes.movement.roll" type="number" step="0.1" value="{{movement.roll}}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.MovementSpace"}}</label>
|
|
||||||
<input name="data.attributes.movement.space" type="number" step="0.1" value="{{movement.space}}"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{localize "SW5E.MovementSwim"}}</label>
|
<label>{{localize "SW5E.MovementSwim"}}</label>
|
||||||
<input name="data.attributes.movement.swim" type="number" step="0.1" value="{{movement.swim}}"/>
|
<input name="data.attributes.movement.swim" type="number" step="0.1" value="{{movement.swim}}"/>
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
<form id="recharge-rest-hd" class="dialog-content" onsubmit="event.preventDefault();">
|
|
||||||
<p>{{ localize "SW5E.RechargeRestHint" }}</p>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{ localize "SW5E.RechargeRestSelect" }}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<select name="hd">
|
|
||||||
{{#select denomination}}
|
|
||||||
{{#each availableHD as |num denom|}}
|
|
||||||
<option value="{{denom}}">{{denom}} ({{num}} {{ localize "SW5E.available" }})</option>
|
|
||||||
{{/each}}
|
|
||||||
{{/select}}
|
|
||||||
</select>
|
|
||||||
<button id="roll-hulld" {{#unless canRoll}}disabled{{/unless}}>
|
|
||||||
<i class="fas fa-dice-d20"></i> {{ localize "Roll" }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{{#unless canRoll}}
|
|
||||||
<p class="notes">{{ localize "SW5E.RechargeRestNoHullD" }}</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}}">
|
|
||||||
{{{button.icon}}}
|
|
||||||
{{{button.label}}}
|
|
||||||
</button>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
|
@ -1,20 +0,0 @@
|
||||||
<form id="refitting-rest" class="dialog-content" onsubmit="event.preventDefault();">
|
|
||||||
<p>Take a refitting rest? On a refitting rest you will recover hull points, your hull dice, and shields.</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>
|
|
|
@ -98,11 +98,11 @@
|
||||||
|
|
||||||
<div class="form-group" style="width: 60%;">
|
<div class="form-group" style="width: 60%;">
|
||||||
<strong style="color:#4b4a44; font-size: 11px">{{ localize "SW5E.CapacityMultiplier" }}</strong>
|
<strong style="color:#4b4a44; font-size: 11px">{{ localize "SW5E.CapacityMultiplier" }}</strong>
|
||||||
<input style="min-width: 5px; max-width: 35px; padding: none;" type="text" name="data.capx.value" value="{{data.capx.value}}" />
|
<input style="min-width: 5px; max-width: 35px; padding: 0;" type="text" name="data.capx.value" value="{{data.capx.value}}" />
|
||||||
<strong style="color:#4b4a44; font-size: 11px;">{{ localize "SW5E.DmgRed" }}</strong>
|
<strong style="color:#4b4a44; font-size: 11px;">{{ localize "SW5E.DmgRed" }}</strong>
|
||||||
<input style="min-width: 5px; max-width: 35px; padding: none;" type="text" name="data.attributes.dmgred.value" value="{{data.attributes.dmgred.value}}" />
|
<input style="min-width: 5px; max-width: 35px; padding: 0;" type="text" name="data.attributes.dr" value="{{data.attributes.dr}}" />
|
||||||
<strong style="color:#4b4a44; font-size: 11px;">{{ localize "SW5E.RegenerationRateCoefficient" }}</strong>
|
<strong style="color:#4b4a44; font-size: 11px;">{{ localize "SW5E.RegenerationRateCoefficient" }}</strong>
|
||||||
<input style="min-width: 5px; max-width: 35px; padding: none;" type="text" name="data.attributes.regrateco.value" value="{{data.regrateco.value}}" />
|
<input style="min-width: 5px; max-width: 35px; padding: 0;" type="text" name="data.regrateco.value" value="{{data.regrateco.value}}" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{!-- Starship Equipment Properties --}}
|
{{!-- Starship Equipment Properties --}}
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
<nav class="sheet-navigation tabs" data-group="primary">
|
<nav class="sheet-navigation tabs" data-group="primary">
|
||||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||||
<a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a>
|
<a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a>
|
||||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{{!-- Item Sheet Body --}}
|
{{!-- Item Sheet Body --}}
|
||||||
|
@ -118,155 +117,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{!-- Construction --}}
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.StockCost"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.buildBaseCost" value="{{data.buildBaseCost}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.MinConstWorkforce"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.buildMinWorkforce" value="{{data.buildMinWorkforce}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.UpgradeCostMult"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.upgrdCostMult" value="{{data.upgrdCostMult}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.MinUpgradeWorkforce"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.upgrdMinWorkforce" value="{{data.upgrdMinWorkforce}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.BaseSpaceSpeed"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.baseSpaceSpeed" value="{{data.baseSpaceSpeed}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.BaseTurnSpeed"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.baseTurnSpeed" value="{{data.baseTurnSpeed}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.MinCrewWorkforce"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.crewMinWorkforce" value="{{data.crewMinWorkforce}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{!-- Modifications --}}
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.BaseModCap"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.modBaseCap" value="{{data.modBaseCap}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.BaseMaxSuites"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.modMaxSuitesBase" value="{{data.modMaxSuitesBase}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.BaseMaxConMult"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.modMaxSuitesMult" value="{{data.modMaxSuitesMult}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.MaxSuiteCapacity"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.modMaxSuiteCap" value="{{data.modMaxSuiteCap}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.ModCostMult"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.modCostMult" value="{{data.modCostMult}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.MinModWorkforce"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.modMinWorkforce" value="{{data.modMinWorkforce}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.HardpointStrMult"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.hardpointMult" value="{{data.hardpointMult}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{!-- Equipment --}}
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.EquipCostMult"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.equipCostMult" value="{{data.equipCostMult}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.MinEquipWorkforce"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.equipMinWorkforce" value="{{data.equipMinWorkforce}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.CargoCap"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.cargoCap" value="{{data.cargoCap}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.FuelCost"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.fuelCost" value="{{data.fuelCost}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.FuelCap"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.fuelCap" value="{{data.fuelCap}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "SW5E.FoodCap"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" placeholder="0" name="data.foodCap" value="{{data.foodCap}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{!-- Effects Tab --}}
|
|
||||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
|
||||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue