diff --git a/.gitignore b/.gitignore index 4922a46..4d9ea4c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ debug.log node_modules foundryconfig.json -build \ No newline at end of file +build +scripts/loadEffects.js \ No newline at end of file diff --git a/effects/0FIA8JbwKG8vwpfC.js b/effects/0FIA8JbwKG8vwpfC.js new file mode 100644 index 0000000..61bcaa1 --- /dev/null +++ b/effects/0FIA8JbwKG8vwpfC.js @@ -0,0 +1 @@ +args.fields.pool += 2 * args.actor.system.advances.rank \ No newline at end of file diff --git a/effects/0LckJjaDaLyk3Obu.js b/effects/0LckJjaDaLyk3Obu.js new file mode 100644 index 0000000..f61749d --- /dev/null +++ b/effects/0LckJjaDaLyk3Obu.js @@ -0,0 +1,31 @@ +let condition = this.actor.hasCondition("bleeding"); + +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Bleeding"); +} + +condition = this.actor.hasCondition("exhausted"); + +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Exhausted"); +} + +condition = this.actor.hasCondition("poisoned"); + +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Poisoned"); +} + +condition = this.actor.hasCondition("prone"); + +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Prone"); +} \ No newline at end of file diff --git a/effects/0QqHPcw6j1FdmlvX.js b/effects/0QqHPcw6j1FdmlvX.js new file mode 100644 index 0000000..37ab06b --- /dev/null +++ b/effects/0QqHPcw6j1FdmlvX.js @@ -0,0 +1,8 @@ +if (this.actor.system.combat.wounds.value != 0) +{ + this.actor.applyHealing({wounds : this.effect.sourceTest.result.wounds}, {messageData : this.script.getChatData()}) +} +else if (this.actor.system.combat.shock.value != 0) +{ + this.actor.applyHealing({shock : this.effect.sourceTest.result.shock}, {messageData : this.script.getChatData()}) +} \ No newline at end of file diff --git a/effects/0R4H5ZmawVzuJ6Lh.js b/effects/0R4H5ZmawVzuJ6Lh.js new file mode 100644 index 0000000..dec93a3 --- /dev/null +++ b/effects/0R4H5ZmawVzuJ6Lh.js @@ -0,0 +1,3 @@ +let items = ["Compendium.wng-core.items.Item.JVz6FoVEc7p1DJUj", "Compendium.wng-core.items.Item.n0bj5UJjgglDzFcB"]; + +this.actor.createEmbeddedDocuments("Item", (await Promise.all(items.map(fromUuid))), {fromEffect: this.effect.id}) \ No newline at end of file diff --git a/effects/0cILdPJ3OxVq13jd.js b/effects/0cILdPJ3OxVq13jd.js new file mode 100644 index 0000000..8b7dd6d --- /dev/null +++ b/effects/0cILdPJ3OxVq13jd.js @@ -0,0 +1 @@ +args.fields.difficulty += (2) \ No newline at end of file diff --git a/effects/0uAhcEEn8mexMNwg.js b/effects/0uAhcEEn8mexMNwg.js new file mode 100644 index 0000000..c2ef646 --- /dev/null +++ b/effects/0uAhcEEn8mexMNwg.js @@ -0,0 +1,11 @@ +let target = Array.from(game.user.targets)[0]?.document?.name; + +if (target) +{ + this.script.notification(`Target set to ${target}`) + this.effect.update({name : this.effect.setSpecifier(target)}) +} +else +{ + this.script.notification("Must target a Token"); +} \ No newline at end of file diff --git a/effects/0us3LU3ENKAXsaoq.js b/effects/0us3LU3ENKAXsaoq.js new file mode 100644 index 0000000..14d1db3 --- /dev/null +++ b/effects/0us3LU3ENKAXsaoq.js @@ -0,0 +1 @@ +args.actor.addCondition("vulnerable"); \ No newline at end of file diff --git a/effects/0wdwMAPUHwmONtVM.js b/effects/0wdwMAPUHwmONtVM.js new file mode 100644 index 0000000..b4a8781 --- /dev/null +++ b/effects/0wdwMAPUHwmONtVM.js @@ -0,0 +1 @@ +return !this.actor.statuses.has("halfCover") && !this.actor.statuses.has("fullCover") \ No newline at end of file diff --git a/effects/0yKINsHhGL9Mql2j.js b/effects/0yKINsHhGL9Mql2j.js new file mode 100644 index 0000000..372decf --- /dev/null +++ b/effects/0yKINsHhGL9Mql2j.js @@ -0,0 +1,3 @@ +let report = await this.actor.applyDamage(0, {shock: 1}); + +this.script.message(`Received ${report.shock} Shock`); \ No newline at end of file diff --git a/effects/12LtSFtjEQ1I7EJ6.js b/effects/12LtSFtjEQ1I7EJ6.js new file mode 100644 index 0000000..cdc17c7 --- /dev/null +++ b/effects/12LtSFtjEQ1I7EJ6.js @@ -0,0 +1 @@ +return args.fields.range != "long" \ No newline at end of file diff --git a/effects/14MFhAYpU5TWG7GD.js b/effects/14MFhAYpU5TWG7GD.js new file mode 100644 index 0000000..5dcc4e5 --- /dev/null +++ b/effects/14MFhAYpU5TWG7GD.js @@ -0,0 +1 @@ +return !args.options.multi || !args.weapon || !args.weapon.isRanged; \ No newline at end of file diff --git a/effects/19ADzGXrV4XhQa3C.js b/effects/19ADzGXrV4XhQa3C.js new file mode 100644 index 0000000..d445cf1 --- /dev/null +++ b/effects/19ADzGXrV4XhQa3C.js @@ -0,0 +1,18 @@ +let shock = Math.ceil(CONFIG.Dice.randomUniform() * 3); +let mortal = 0 + +if (this.effect.sourceActor.type == "agent") +{ + shock += this.effect.sourceActor.corruptionLevel || 0 +} + +let test = await this.actor.setupAttributeTest("toughness", {fields : {difficulty : 5}}); + +if (!test.result.isSuccess) +{ + mortal = Math.ceil(CONFIG.Dice.randomUniform() * 3); +} + +let report = await this.actor.applyDamage(0, {mortal, shock}); + +this.script.message(` Received ${report.wounds} Wounds and ${report.shock} Shock`) \ No newline at end of file diff --git a/effects/1CIbPz2M7bjhkuKl.js b/effects/1CIbPz2M7bjhkuKl.js new file mode 100644 index 0000000..0a9b205 --- /dev/null +++ b/effects/1CIbPz2M7bjhkuKl.js @@ -0,0 +1 @@ +this.actor.setupGenericTest("fear", {fields : {difficulty : 1 + this.effect.sourceActor.system.advances.rank * 2}}) \ No newline at end of file diff --git a/effects/1TENfrHWdFXtz1TA.js b/effects/1TENfrHWdFXtz1TA.js new file mode 100644 index 0000000..63d732b --- /dev/null +++ b/effects/1TENfrHWdFXtz1TA.js @@ -0,0 +1 @@ +return !args.weapon || args.fields.distance < 12; \ No newline at end of file diff --git a/effects/1bbFeRFCapNGXysD.js b/effects/1bbFeRFCapNGXysD.js new file mode 100644 index 0000000..5ff1b69 --- /dev/null +++ b/effects/1bbFeRFCapNGXysD.js @@ -0,0 +1 @@ +await this.actor.addCondition("staggered"); \ No newline at end of file diff --git a/effects/1cVdks66HCH9RlIq.js b/effects/1cVdks66HCH9RlIq.js new file mode 100644 index 0000000..82afca6 --- /dev/null +++ b/effects/1cVdks66HCH9RlIq.js @@ -0,0 +1 @@ +return args.data.skill !== 'weaponSkill'; \ No newline at end of file diff --git a/effects/1uagy1u9G4bmT0FP.js b/effects/1uagy1u9G4bmT0FP.js new file mode 100644 index 0000000..bfe41d3 --- /dev/null +++ b/effects/1uagy1u9G4bmT0FP.js @@ -0,0 +1 @@ +return !args.actor.statuses.has("blinded") \ No newline at end of file diff --git a/effects/2SZWFMMApmNukV6O.js b/effects/2SZWFMMApmNukV6O.js new file mode 100644 index 0000000..f16b3cf --- /dev/null +++ b/effects/2SZWFMMApmNukV6O.js @@ -0,0 +1,14 @@ +if (this.actor.type == "agent") +{ + this.actor.update({"system.corruption.current" : this.actor.system.corruption.current + this.effect.sourceTest.result.corruption}) +} + +let table = await fromUuid("RollTable.5jY4Qiah2VaLmBVT"); + +let result = await table.roll() + +let uuid = `Compendium.${result.results[0].documentCollection}.${result.results[0].documentId}`; + +this.actor.addEffectItems(uuid, this.effect) + +this.script.notification(`Added ${this.effect.sourceTest.result.corruption} Corruption and ${result.results[0].text}`) \ No newline at end of file diff --git a/effects/2cM4UeiXanH2SPFl.js b/effects/2cM4UeiXanH2SPFl.js new file mode 100644 index 0000000..6e521ba --- /dev/null +++ b/effects/2cM4UeiXanH2SPFl.js @@ -0,0 +1 @@ +args.fields.pool += args.target.system.advances.tier * 2; \ No newline at end of file diff --git a/effects/2o1X1mbnFEaazgDO.js b/effects/2o1X1mbnFEaazgDO.js new file mode 100644 index 0000000..891ab8b --- /dev/null +++ b/effects/2o1X1mbnFEaazgDO.js @@ -0,0 +1,11 @@ +let roll = await new Roll("dp").roll(); +roll.toMessage(this.script.getChatData()); + +if (roll.total >= 5) +{ + await this.actor.applyHealing({wounds: 1, shock: 1}, {messageData : this.script.getChatData()}); + this.actor.removeCondition("dying"); + this.actor.removeCondition("exhausted"); + this.actor.removeCondition("prone"); + this.actor.removeCondition("dead"); +} \ No newline at end of file diff --git a/effects/2oWIZuaebBy335X7.js b/effects/2oWIZuaebBy335X7.js new file mode 100644 index 0000000..7edee73 --- /dev/null +++ b/effects/2oWIZuaebBy335X7.js @@ -0,0 +1 @@ +return args.weapon || args.power \ No newline at end of file diff --git a/effects/2too0w42AmXEGbW1.js b/effects/2too0w42AmXEGbW1.js new file mode 100644 index 0000000..52e2cbf --- /dev/null +++ b/effects/2too0w42AmXEGbW1.js @@ -0,0 +1 @@ +args.fields.difficulty -= 2 * (args.options.multi - 1); \ No newline at end of file diff --git a/effects/2xUXQUX3f1tUklhl.js b/effects/2xUXQUX3f1tUklhl.js new file mode 100644 index 0000000..2b2b01d --- /dev/null +++ b/effects/2xUXQUX3f1tUklhl.js @@ -0,0 +1 @@ +return !["willpower", "fellowship"].includes(args.attribute) || args.skill == "intimidation" \ No newline at end of file diff --git a/effects/3BoZezNKLz0UbcqM.js b/effects/3BoZezNKLz0UbcqM.js new file mode 100644 index 0000000..e2c0e3d --- /dev/null +++ b/effects/3BoZezNKLz0UbcqM.js @@ -0,0 +1 @@ +args.fields.pool += (args.actor.system.advances.rank) * 3 \ No newline at end of file diff --git a/effects/3JxOovChAEOPU068.js b/effects/3JxOovChAEOPU068.js new file mode 100644 index 0000000..2d1c427 --- /dev/null +++ b/effects/3JxOovChAEOPU068.js @@ -0,0 +1,4 @@ +let wounds = Math.ceil(CONFIG.Dice.randomUniform() * 3) +let shock = Math.ceil(CONFIG.Dice.randomUniform() * 6); + +await this.actor.applyHealing({wounds, shock}, {messageData : this.script.getChatData()}); \ No newline at end of file diff --git a/effects/3LchJbIa1VNRwneO.js b/effects/3LchJbIa1VNRwneO.js new file mode 100644 index 0000000..64bf4a3 --- /dev/null +++ b/effects/3LchJbIa1VNRwneO.js @@ -0,0 +1 @@ +return args.skill != "medicae" \ No newline at end of file diff --git a/effects/3gUH2eynhZbUjqHX.js b/effects/3gUH2eynhZbUjqHX.js new file mode 100644 index 0000000..34eb7f9 --- /dev/null +++ b/effects/3gUH2eynhZbUjqHX.js @@ -0,0 +1 @@ +return !(args.weapon.name === "Paired Hekatarri Blades" && args.options.multi); \ No newline at end of file diff --git a/effects/3hojzkE4Ojom9Kcz.js b/effects/3hojzkE4Ojom9Kcz.js new file mode 100644 index 0000000..7825b9b --- /dev/null +++ b/effects/3hojzkE4Ojom9Kcz.js @@ -0,0 +1,4 @@ +if (this.effect.sourceActor?.uuid == this.actor.uuid) +{ + this.effect.updateSource({"flags.round" : game.combat.round}); +} \ No newline at end of file diff --git a/effects/3jdAfVeD9ckNmouC.js b/effects/3jdAfVeD9ckNmouC.js new file mode 100644 index 0000000..4f245c4 --- /dev/null +++ b/effects/3jdAfVeD9ckNmouC.js @@ -0,0 +1,8 @@ +if (this.actor.statuses.has("halfCover")) +{ + args.fields.difficulty += 1; +} +else if (this.actor.statuses.has("fullCover")) +{ + args.fields.difficulty += 1; +} \ No newline at end of file diff --git a/effects/40CuO0G1aCKWkReH.js b/effects/40CuO0G1aCKWkReH.js new file mode 100644 index 0000000..c26975a --- /dev/null +++ b/effects/40CuO0G1aCKWkReH.js @@ -0,0 +1 @@ +return args.options.resolve || args.options.corruption \ No newline at end of file diff --git a/effects/4JYZtqxPGDojWdqY.js b/effects/4JYZtqxPGDojWdqY.js new file mode 100644 index 0000000..2335ec4 --- /dev/null +++ b/effects/4JYZtqxPGDojWdqY.js @@ -0,0 +1 @@ +return args.options.resolve \ No newline at end of file diff --git a/effects/4PKyEAY439MdUfCH.js b/effects/4PKyEAY439MdUfCH.js new file mode 100644 index 0000000..7784560 --- /dev/null +++ b/effects/4PKyEAY439MdUfCH.js @@ -0,0 +1,6 @@ +let weapons = ["Chainsword", + "Chainaxe", + "Power Sword", + "Power Fist", + "Unarmed Strike"] +return args.weapon && (weapons.includes(args.weapon.name) || args.weapon.keywords.includes("BOLT") || args.weapon.keywords.includes("ADEPTUS ASTARTES")) \ No newline at end of file diff --git a/effects/4dF20jKPuiuv0gcy.js b/effects/4dF20jKPuiuv0gcy.js new file mode 100644 index 0000000..6c8cb0a --- /dev/null +++ b/effects/4dF20jKPuiuv0gcy.js @@ -0,0 +1 @@ +return args.options.conviction || args.options.resolve || args.weapon; \ No newline at end of file diff --git a/effects/4uRdeuQHAiO3o2RQ.js b/effects/4uRdeuQHAiO3o2RQ.js new file mode 100644 index 0000000..fce6f8f --- /dev/null +++ b/effects/4uRdeuQHAiO3o2RQ.js @@ -0,0 +1 @@ +this.actor.applyDamage(0, {shock : 1}); \ No newline at end of file diff --git a/effects/4vVFOoDbyVouBJaI.js b/effects/4vVFOoDbyVouBJaI.js new file mode 100644 index 0000000..fe1d418 --- /dev/null +++ b/effects/4vVFOoDbyVouBJaI.js @@ -0,0 +1 @@ +this.actor.applyHealing({shock : this.actor.system.combat.shock.value}, {messageData : this.script.getChatData()}) \ No newline at end of file diff --git a/effects/53NTjNWaNTEisoIt.js b/effects/53NTjNWaNTEisoIt.js new file mode 100644 index 0000000..23719db --- /dev/null +++ b/effects/53NTjNWaNTEisoIt.js @@ -0,0 +1 @@ +args.fields.difficulty += (1) \ No newline at end of file diff --git a/effects/56lX5USxmNE0rJoo.js b/effects/56lX5USxmNE0rJoo.js new file mode 100644 index 0000000..aff20f6 --- /dev/null +++ b/effects/56lX5USxmNE0rJoo.js @@ -0,0 +1 @@ +return !args.target.system.mob?.value; \ No newline at end of file diff --git a/effects/5879j2uObuDQY4R6.js b/effects/5879j2uObuDQY4R6.js new file mode 100644 index 0000000..0024221 --- /dev/null +++ b/effects/5879j2uObuDQY4R6.js @@ -0,0 +1 @@ +args.fields.difficulty -= 2; \ No newline at end of file diff --git a/effects/5A12EywlpDuJF7Sy.js b/effects/5A12EywlpDuJF7Sy.js new file mode 100644 index 0000000..ed9ddb3 --- /dev/null +++ b/effects/5A12EywlpDuJF7Sy.js @@ -0,0 +1 @@ +return !args.options.corruption && !args.options.mutation \ No newline at end of file diff --git a/effects/5Avn6epl75S6VZHF.js b/effects/5Avn6epl75S6VZHF.js new file mode 100644 index 0000000..02d111c --- /dev/null +++ b/effects/5Avn6epl75S6VZHF.js @@ -0,0 +1 @@ +return args.skill != "psychicMastery" \ No newline at end of file diff --git a/effects/5PBjhK6b1paiQSL0.js b/effects/5PBjhK6b1paiQSL0.js new file mode 100644 index 0000000..d9edafc --- /dev/null +++ b/effects/5PBjhK6b1paiQSL0.js @@ -0,0 +1,4 @@ +let shock = (CONFIG.Dice.randomUniform() * 3) + 1 +let report = await this.actor.applyDamage(0, {shock: 1}); + +this.script.message(`Received ${report.shock} Shock`); \ No newline at end of file diff --git a/effects/5ZI2vWygrweuXVjh.js b/effects/5ZI2vWygrweuXVjh.js new file mode 100644 index 0000000..7f334c2 --- /dev/null +++ b/effects/5ZI2vWygrweuXVjh.js @@ -0,0 +1,8 @@ +if (args.target.statuses.has("fullCover")) +{ + args.fields.difficulty -= 2; +} +else if (args.target.statuses.has("halfCover")) +{ + args.fields.difficulty -= 1; +} \ No newline at end of file diff --git a/effects/5dcXsqAYfSc0XsGu.js b/effects/5dcXsqAYfSc0XsGu.js new file mode 100644 index 0000000..af8636f --- /dev/null +++ b/effects/5dcXsqAYfSc0XsGu.js @@ -0,0 +1 @@ +return !args.weapon || (args.weapon.isRanged && args.weapon.traitList.pistol) \ No newline at end of file diff --git a/effects/5uMGCreXCE2Lz2Fj.js b/effects/5uMGCreXCE2Lz2Fj.js new file mode 100644 index 0000000..a094799 --- /dev/null +++ b/effects/5uMGCreXCE2Lz2Fj.js @@ -0,0 +1,20 @@ +condition = this.actor.hasCondition("blinded"); +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Blinded"); +} + +let condition = this.actor.hasCondition("bleeding"); +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Bleeding"); +} + +condition = this.actor.hasCondition("poisoned"); +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Poisoned"); +} \ No newline at end of file diff --git a/effects/5zi5aYysSkGrQaRf.js b/effects/5zi5aYysSkGrQaRf.js new file mode 100644 index 0000000..0eb2241 --- /dev/null +++ b/effects/5zi5aYysSkGrQaRf.js @@ -0,0 +1 @@ +args.fields.damage += 2; \ No newline at end of file diff --git a/effects/62hMTw6MQiiYm0mg.js b/effects/62hMTw6MQiiYm0mg.js new file mode 100644 index 0000000..0ffb9e1 --- /dev/null +++ b/effects/62hMTw6MQiiYm0mg.js @@ -0,0 +1 @@ +return args.fileds.range == "short"; \ No newline at end of file diff --git a/effects/6AC775QniHCQ9WzK.js b/effects/6AC775QniHCQ9WzK.js new file mode 100644 index 0000000..0c1bae3 --- /dev/null +++ b/effects/6AC775QniHCQ9WzK.js @@ -0,0 +1 @@ +return !args.weapon.isMelee; \ No newline at end of file diff --git a/effects/6GtlOX0l9XA8MgWq.js b/effects/6GtlOX0l9XA8MgWq.js new file mode 100644 index 0000000..7a15452 --- /dev/null +++ b/effects/6GtlOX0l9XA8MgWq.js @@ -0,0 +1 @@ +return !args.target?.system.combat.fly; \ No newline at end of file diff --git a/effects/6MfHNLZcAULy0cPX.js b/effects/6MfHNLZcAULy0cPX.js new file mode 100644 index 0000000..b411225 --- /dev/null +++ b/effects/6MfHNLZcAULy0cPX.js @@ -0,0 +1,7 @@ +let attribute = await ItemDialog.create(ItemDialog.objectToArray(systemConfig().attributes, this.effect.img), 1, {title : this.effect.name}) + +if (attribute[0]) +{ + this.item.updateSource({name : this.item.name.replace("Attribute", attribute[0].name)}); + this.effect.updateSource({name : this.item.name, "flags.wrath-and-glory.attribute" : attribute[0].id}) +} \ No newline at end of file diff --git a/effects/6O49JUv1z2bf7CGH.js b/effects/6O49JUv1z2bf7CGH.js new file mode 100644 index 0000000..55cbe4a --- /dev/null +++ b/effects/6O49JUv1z2bf7CGH.js @@ -0,0 +1 @@ +return args.target?.effects.contents.some(e => e.statuses.has("halfCover") || e.statuses.has("fullCover")); \ No newline at end of file diff --git a/effects/6cGh0fZBduJtCAEy.js b/effects/6cGh0fZBduJtCAEy.js new file mode 100644 index 0000000..a090a77 --- /dev/null +++ b/effects/6cGh0fZBduJtCAEy.js @@ -0,0 +1 @@ +return args.skill != "ballisticSkill" \ No newline at end of file diff --git a/effects/6gDZdFsjx5Fpn7no.js b/effects/6gDZdFsjx5Fpn7no.js new file mode 100644 index 0000000..7103359 --- /dev/null +++ b/effects/6gDZdFsjx5Fpn7no.js @@ -0,0 +1 @@ +return !["intimidation", "leadership"].includes(args.skill) \ No newline at end of file diff --git a/effects/6jsNtkioqGJW2JJy.js b/effects/6jsNtkioqGJW2JJy.js new file mode 100644 index 0000000..bfd926d --- /dev/null +++ b/effects/6jsNtkioqGJW2JJy.js @@ -0,0 +1 @@ +return args.attribute != "intellect" \ No newline at end of file diff --git a/effects/6kzMfJv70NiCv84x.js b/effects/6kzMfJv70NiCv84x.js new file mode 100644 index 0000000..d546b85 --- /dev/null +++ b/effects/6kzMfJv70NiCv84x.js @@ -0,0 +1,2 @@ +let report = await this.actor.applyDamage(0, {mortal: 1}); +this.script.message(`Received ${report.wounds} Mortal Wound`); \ No newline at end of file diff --git a/effects/6q2PhkCvoJqmtwdS.js b/effects/6q2PhkCvoJqmtwdS.js new file mode 100644 index 0000000..06e252c --- /dev/null +++ b/effects/6q2PhkCvoJqmtwdS.js @@ -0,0 +1 @@ +return this.actor.statuses.has("halfCover") || this.actor.statuses.has("fullCover") \ No newline at end of file diff --git a/effects/6qyhFOQ3lgjrzusR.js b/effects/6qyhFOQ3lgjrzusR.js new file mode 100644 index 0000000..1acbb44 --- /dev/null +++ b/effects/6qyhFOQ3lgjrzusR.js @@ -0,0 +1 @@ +return args.target.statuses.has("fullCover") || args.target.statuses.has("halfCover"); \ No newline at end of file diff --git a/effects/6wNMuZ5al7DPtsWX.js b/effects/6wNMuZ5al7DPtsWX.js new file mode 100644 index 0000000..23cd569 --- /dev/null +++ b/effects/6wNMuZ5al7DPtsWX.js @@ -0,0 +1,10 @@ +let name = args.item._source.name; +if (name !== "Scything Talon" && name !== "Lash Whip") +{ + return; +} + +if (!args.item._source.system.traits.list.find(i => i.name == "inflict")) +{ + args.item.system.traits.list.push({name : "inflict", rating:"Poisoned(4)"}); +} \ No newline at end of file diff --git a/effects/6x9mD1GGnzqhjuOh.js b/effects/6x9mD1GGnzqhjuOh.js new file mode 100644 index 0000000..3ee6ee4 --- /dev/null +++ b/effects/6x9mD1GGnzqhjuOh.js @@ -0,0 +1 @@ +this.actor.setupGenericTest("corruption", {fields: {difficulty: 1}}) \ No newline at end of file diff --git a/effects/7biphiKt2ypBulRr.js b/effects/7biphiKt2ypBulRr.js new file mode 100644 index 0000000..fa9393c --- /dev/null +++ b/effects/7biphiKt2ypBulRr.js @@ -0,0 +1 @@ +return !args.weapon || !args.weapon.isMelee; \ No newline at end of file diff --git a/effects/7cDipKtebsB9pCEj.js b/effects/7cDipKtebsB9pCEj.js new file mode 100644 index 0000000..c6a3d23 --- /dev/null +++ b/effects/7cDipKtebsB9pCEj.js @@ -0,0 +1,2 @@ +await this.actor.addCondition("vulnerable", {[game.system.id] : {value : Math.abs(this.effect.sourceTest.result.defence)}}) +await this.actor.addCondition("hindered",{[game.system.id] : {value : Math.abs(this.effect.sourceTest.result.penalty)}}) \ No newline at end of file diff --git a/effects/7imBh0BPLrQnXG9I.js b/effects/7imBh0BPLrQnXG9I.js new file mode 100644 index 0000000..a5986ed --- /dev/null +++ b/effects/7imBh0BPLrQnXG9I.js @@ -0,0 +1 @@ +return args.weapon || args.power; \ No newline at end of file diff --git a/effects/7mnXZc4kMpkCGScW.js b/effects/7mnXZc4kMpkCGScW.js new file mode 100644 index 0000000..04a2dff --- /dev/null +++ b/effects/7mnXZc4kMpkCGScW.js @@ -0,0 +1 @@ +return args.skill == "scholar" \ No newline at end of file diff --git a/effects/7slYBl8Pv8czLe30.js b/effects/7slYBl8Pv8czLe30.js new file mode 100644 index 0000000..0bab6a7 --- /dev/null +++ b/effects/7slYBl8Pv8czLe30.js @@ -0,0 +1 @@ +return args.options.conviction || args.options.determination \ No newline at end of file diff --git a/effects/8cWc8m3TKBh9q35p.js b/effects/8cWc8m3TKBh9q35p.js new file mode 100644 index 0000000..caf356b --- /dev/null +++ b/effects/8cWc8m3TKBh9q35p.js @@ -0,0 +1,8 @@ +let effects = this.item.effects.contents.filter(i => i.id != this.effect.id); + +let choice = await ItemDialog.create(effects, 1, {title : this.effect.name, text: this.script.name}); + +if (choice[0]) +{ + choice[0].updateSource({"system.transferData.type" : "document"}) +} \ No newline at end of file diff --git a/effects/8gwRPxUjCTtMmJmZ.js b/effects/8gwRPxUjCTtMmJmZ.js new file mode 100644 index 0000000..e114d93 --- /dev/null +++ b/effects/8gwRPxUjCTtMmJmZ.js @@ -0,0 +1,2 @@ +this.actor.combat.resilience.invulnerable = true; +this.actor.combat.resilience.armour += 2; \ No newline at end of file diff --git a/effects/8wvVPtRfQtgAfAzs.js b/effects/8wvVPtRfQtgAfAzs.js new file mode 100644 index 0000000..cbd30f4 --- /dev/null +++ b/effects/8wvVPtRfQtgAfAzs.js @@ -0,0 +1,9 @@ +if (!this.actor.hasKeyword("PSYKER")) +{ + + +let keyword = await fromUuid("Compendium.wng-core.items.Item.JVz6FoVEc7p1DJUj"); + +this.actor.createEmbeddedDocuments("Item", [keyword], {fromEffect: this.effect.id}) + +} \ No newline at end of file diff --git a/effects/91SCyNkOTrw4U4zJ.js b/effects/91SCyNkOTrw4U4zJ.js new file mode 100644 index 0000000..65d3b08 --- /dev/null +++ b/effects/91SCyNkOTrw4U4zJ.js @@ -0,0 +1 @@ +return !["willpower", "fellowship"].includes(args.attribute) \ No newline at end of file diff --git a/effects/9DVUzshl2xnSKhJW.js b/effects/9DVUzshl2xnSKhJW.js new file mode 100644 index 0000000..94a9121 --- /dev/null +++ b/effects/9DVUzshl2xnSKhJW.js @@ -0,0 +1 @@ +return !args.target || !args.target.system.advances?.tier; \ No newline at end of file diff --git a/effects/9GC3eLhOC6YMEtdc.js b/effects/9GC3eLhOC6YMEtdc.js new file mode 100644 index 0000000..d070aba --- /dev/null +++ b/effects/9GC3eLhOC6YMEtdc.js @@ -0,0 +1 @@ +return !args.options.conviction && !args.options.determination \ No newline at end of file diff --git a/effects/9GRenPwwZaH576mF.js b/effects/9GRenPwwZaH576mF.js new file mode 100644 index 0000000..bb06e2a --- /dev/null +++ b/effects/9GRenPwwZaH576mF.js @@ -0,0 +1 @@ +args.fields.pool += (1); \ No newline at end of file diff --git a/effects/9ItWr4dV38zN8Qsl.js b/effects/9ItWr4dV38zN8Qsl.js new file mode 100644 index 0000000..075b63d --- /dev/null +++ b/effects/9ItWr4dV38zN8Qsl.js @@ -0,0 +1,4 @@ +if (this.effect.sourceTest.testData.shifted.wounds) +{ + await this.actor.applyHealing({wounds: this.effect.sourceTest.testData.shifted.wounds.dice.length, shock: 0}, {messageData : this.script.getChatData()}); +} \ No newline at end of file diff --git a/effects/9aHFUvD1bKbwBv65.js b/effects/9aHFUvD1bKbwBv65.js new file mode 100644 index 0000000..f91fa9d --- /dev/null +++ b/effects/9aHFUvD1bKbwBv65.js @@ -0,0 +1 @@ +this.effect.delete(); \ No newline at end of file diff --git a/effects/9iAdpvynLswAjkOT.js b/effects/9iAdpvynLswAjkOT.js new file mode 100644 index 0000000..72d8201 --- /dev/null +++ b/effects/9iAdpvynLswAjkOT.js @@ -0,0 +1 @@ +return args.skill != "investigation" \ No newline at end of file diff --git a/effects/9n7XTVnfdJQ6TZPc.js b/effects/9n7XTVnfdJQ6TZPc.js new file mode 100644 index 0000000..7057703 --- /dev/null +++ b/effects/9n7XTVnfdJQ6TZPc.js @@ -0,0 +1 @@ +return args.weapon?.isRanged && this.actor.itemTypes.weapon.some(i => i.traitList.parry) \ No newline at end of file diff --git a/effects/9zwIQfxDjFcGVE1y.js b/effects/9zwIQfxDjFcGVE1y.js new file mode 100644 index 0000000..9cb9386 --- /dev/null +++ b/effects/9zwIQfxDjFcGVE1y.js @@ -0,0 +1 @@ +this.fields.pool += 2; \ No newline at end of file diff --git a/effects/A0mJlGOlM3vZncax.js b/effects/A0mJlGOlM3vZncax.js new file mode 100644 index 0000000..451e7d5 --- /dev/null +++ b/effects/A0mJlGOlM3vZncax.js @@ -0,0 +1 @@ +return args.actor.itemTypes.armour.filter(i => i.system.isEquipped && i.hasKeyword("HEAVY")).length > 0 \ No newline at end of file diff --git a/effects/A5hyKXJMUPEtCre9.js b/effects/A5hyKXJMUPEtCre9.js new file mode 100644 index 0000000..d45bda4 --- /dev/null +++ b/effects/A5hyKXJMUPEtCre9.js @@ -0,0 +1,13 @@ +let myConviction = this.actor.system.combat.conviction.total; +let shaperConviction = this.effect.sourceActor.system.combat.conviction.total +if ( myConviction < shaperConviction ) +{ + this.actor.system.combat.conviction.total = shaperConviction; +} + +let myResolve = this.actor.system.combat.resolve.total; +let shaperResolve = this.effect.sourceActor.system.combat.resolve.total +if ( myResolve < shaperResolve ) +{ + this.actor.system.combat.resolve.total = shaperResolve; +} \ No newline at end of file diff --git a/effects/A766gvj4lxclZVuo.js b/effects/A766gvj4lxclZVuo.js new file mode 100644 index 0000000..8eabc28 --- /dev/null +++ b/effects/A766gvj4lxclZVuo.js @@ -0,0 +1 @@ +return !args.weapon \ No newline at end of file diff --git a/effects/A9erE3GTUTBg7FwY.js b/effects/A9erE3GTUTBg7FwY.js new file mode 100644 index 0000000..e4385e5 --- /dev/null +++ b/effects/A9erE3GTUTBg7FwY.js @@ -0,0 +1,5 @@ +let roll = Math.ceil(CONFIG.Dice.randomUniform() * 3); + +let shock = this.effect.sourceActor.system.advances.rank * 2 + roll; + +this.actor.applyHealing({shock}, {messageData : this.script.getChatData()}); \ No newline at end of file diff --git a/effects/ABgzV2bS45Tv2sVb.js b/effects/ABgzV2bS45Tv2sVb.js new file mode 100644 index 0000000..02d6a53 --- /dev/null +++ b/effects/ABgzV2bS45Tv2sVb.js @@ -0,0 +1,4 @@ +if (args.test?.result.isWrathCritical && args.test?.item?.system.isMelee) +{ + args.modifiers.mortal.push({label : this.effect.name, value : 4}) +} \ No newline at end of file diff --git a/effects/AGvYMSSTglLGo4EY.js b/effects/AGvYMSSTglLGo4EY.js new file mode 100644 index 0000000..eefd164 --- /dev/null +++ b/effects/AGvYMSSTglLGo4EY.js @@ -0,0 +1 @@ +return !args.weapon || args.weapon.traitList.inflict?.rating != "On Fire" \ No newline at end of file diff --git a/effects/APpTseZxCahlA77s.js b/effects/APpTseZxCahlA77s.js new file mode 100644 index 0000000..86aa512 --- /dev/null +++ b/effects/APpTseZxCahlA77s.js @@ -0,0 +1,3 @@ +this.actor.update({"system.corruption.current" : this.actor.system.corruption.current + 1}); + +this.script.notification("Added +1 Corruption"); \ No newline at end of file diff --git a/effects/AWtOoK7JHmHpVIea.js b/effects/AWtOoK7JHmHpVIea.js new file mode 100644 index 0000000..f9cf2f6 --- /dev/null +++ b/effects/AWtOoK7JHmHpVIea.js @@ -0,0 +1 @@ +args.fields.difficulty += (3) \ No newline at end of file diff --git a/effects/Aa5IigIPUKd1isA1.js b/effects/Aa5IigIPUKd1isA1.js new file mode 100644 index 0000000..6c37694 --- /dev/null +++ b/effects/Aa5IigIPUKd1isA1.js @@ -0,0 +1,4 @@ +if (args.weapon.hasKeyword("BLADE") || args.weapon.hasKeyword("CHAIN") || args.weapon.hasKeyword("FORCE") || args.weapon.hasKeyword("POWER FIELD")) +{ + args.addShiftOption("armourbane", "Armourbane", "A") +} \ No newline at end of file diff --git a/effects/Al97hu95Ill2dPBR.js b/effects/Al97hu95Ill2dPBR.js new file mode 100644 index 0000000..b9bf34a --- /dev/null +++ b/effects/Al97hu95Ill2dPBR.js @@ -0,0 +1 @@ +args.fields.ed.value += (args.actor.system.advances.rank * 2) \ No newline at end of file diff --git a/effects/Awdlr5BAmiJTXc3H.js b/effects/Awdlr5BAmiJTXc3H.js new file mode 100644 index 0000000..3c66bf1 --- /dev/null +++ b/effects/Awdlr5BAmiJTXc3H.js @@ -0,0 +1 @@ +args.fields.pool += args.actor.system.advances.rank \ No newline at end of file diff --git a/effects/AxqgtlCw60kOoesX.js b/effects/AxqgtlCw60kOoesX.js new file mode 100644 index 0000000..667e9a2 --- /dev/null +++ b/effects/AxqgtlCw60kOoesX.js @@ -0,0 +1 @@ +return args.skill == "ballisticSkill" \ No newline at end of file diff --git a/effects/B3sBL1gjTphredxy.js b/effects/B3sBL1gjTphredxy.js new file mode 100644 index 0000000..7abbf90 --- /dev/null +++ b/effects/B3sBL1gjTphredxy.js @@ -0,0 +1 @@ +return !["persuasion", "leadership"].includes(args.skill) || args.actor.system.resources.faith.current == 0 \ No newline at end of file diff --git a/effects/BD8adlqXBtxcgEeT.js b/effects/BD8adlqXBtxcgEeT.js new file mode 100644 index 0000000..3b07001 --- /dev/null +++ b/effects/BD8adlqXBtxcgEeT.js @@ -0,0 +1 @@ +return args.attribute != this.effect.getFlag(game.system.id, "attribute"); \ No newline at end of file diff --git a/effects/Be00piKt2tG7VOX2.js b/effects/Be00piKt2tG7VOX2.js new file mode 100644 index 0000000..a818b57 --- /dev/null +++ b/effects/Be00piKt2tG7VOX2.js @@ -0,0 +1,4 @@ +let wounds = Math.ceil(CONFIG.Dice.randomUniform() * 3); +let report = await this.actor.applyDamage(0, {mortal:wounds}); + +this.script.message(`${this.actor.name} took ${report.mortal} Wounds`); \ No newline at end of file diff --git a/effects/BlIq9WVygXecUJ99.js b/effects/BlIq9WVygXecUJ99.js new file mode 100644 index 0000000..b7d578f --- /dev/null +++ b/effects/BlIq9WVygXecUJ99.js @@ -0,0 +1 @@ +return args.actor.statuses.has("halfCover") || args.actor.statuses.has("fullCover"); \ No newline at end of file diff --git a/effects/Blhy4vTA7nd8eb1i.js b/effects/Blhy4vTA7nd8eb1i.js new file mode 100644 index 0000000..e47bff1 --- /dev/null +++ b/effects/Blhy4vTA7nd8eb1i.js @@ -0,0 +1 @@ +return args.skill != "investigation" && !args.options.influence \ No newline at end of file diff --git a/effects/BrT6piUnOYUmM2Ip.js b/effects/BrT6piUnOYUmM2Ip.js new file mode 100644 index 0000000..1e0717f --- /dev/null +++ b/effects/BrT6piUnOYUmM2Ip.js @@ -0,0 +1,6 @@ +let wounds = this.effect.getFlag(game.system.id, "wounds"); + +if (wounds) +{ + this.actor.applyHealing({wounds : wounds * 2}, {messageData : this.script.getChatData()}) +} \ No newline at end of file diff --git a/effects/Brhe83cWXvseTm2n.js b/effects/Brhe83cWXvseTm2n.js new file mode 100644 index 0000000..cb02807 --- /dev/null +++ b/effects/Brhe83cWXvseTm2n.js @@ -0,0 +1,3 @@ +let mortal = Math.ceil(CONFIG.Dice.randomUniform() * 6); +let report = await this.actor.applyDamage(0, {mortal}); +this.script.message(`Received ${report.wounds} Wounds and ${report.shock} Shock`) \ No newline at end of file diff --git a/effects/BsgiKeowhYzXeHln.js b/effects/BsgiKeowhYzXeHln.js new file mode 100644 index 0000000..922af41 --- /dev/null +++ b/effects/BsgiKeowhYzXeHln.js @@ -0,0 +1,11 @@ +if (this.actor.hasCondition("staggered")) +{ + +let resisted = await this.effect.resistEffect(); + +if (resisted) +{ + this.actor.removeCondition("staggered"); +} + +} \ No newline at end of file diff --git a/effects/C3f6PAWu1EMtjqkc.js b/effects/C3f6PAWu1EMtjqkc.js new file mode 100644 index 0000000..21c20d6 --- /dev/null +++ b/effects/C3f6PAWu1EMtjqkc.js @@ -0,0 +1,2 @@ +let keyword = this.effect.specifier; +return args.target?.hasKeyword(keyword); \ No newline at end of file diff --git a/effects/C7pMcQJxsZS6Y0pp.js b/effects/C7pMcQJxsZS6Y0pp.js new file mode 100644 index 0000000..89d23a9 --- /dev/null +++ b/effects/C7pMcQJxsZS6Y0pp.js @@ -0,0 +1,5 @@ +if (args.test.weapon?.isRanged) +{ + args.modifiers.resilience.push({value : 1, label : this.effect.label}) + args.resilience.invulnerable = true; +} \ No newline at end of file diff --git a/effects/C8i148sLeDQ3K1JA.js b/effects/C8i148sLeDQ3K1JA.js new file mode 100644 index 0000000..c7e593c --- /dev/null +++ b/effects/C8i148sLeDQ3K1JA.js @@ -0,0 +1 @@ +args.addShiftOption("hardy", "Hardy", "H") \ No newline at end of file diff --git a/effects/CAmyzmufywSe5ZUw.js b/effects/CAmyzmufywSe5ZUw.js new file mode 100644 index 0000000..afc83d3 --- /dev/null +++ b/effects/CAmyzmufywSe5ZUw.js @@ -0,0 +1 @@ +return !["willpower", "fellowship", "initiative", "intellect"].includes(args.attribute) \ No newline at end of file diff --git a/effects/CFkKklyHbcDiKwsL.js b/effects/CFkKklyHbcDiKwsL.js new file mode 100644 index 0000000..9d39f04 --- /dev/null +++ b/effects/CFkKklyHbcDiKwsL.js @@ -0,0 +1 @@ +await this.actor.applyHealing({wounds: 1, shock: 1}, {messageData : this.script.getChatData()}); \ No newline at end of file diff --git a/effects/CI1w8HHmUshU3euA.js b/effects/CI1w8HHmUshU3euA.js new file mode 100644 index 0000000..4e81776 --- /dev/null +++ b/effects/CI1w8HHmUshU3euA.js @@ -0,0 +1 @@ +args.fields.difficulty++; \ No newline at end of file diff --git a/effects/CKhtZefLmNzwrOl6.js b/effects/CKhtZefLmNzwrOl6.js new file mode 100644 index 0000000..b61577f --- /dev/null +++ b/effects/CKhtZefLmNzwrOl6.js @@ -0,0 +1,20 @@ +if (this.item.specifier == "Skill") + { + let skills = Object.keys(systemConfig().skills).map(i => { + return {id : i, name : systemConfig().skills[i], img : this.effect.img} + }).filter(i => this.actor.system.skills[i.id].rating >= 4); + + if (skills.length == 0) + { + this.script.notification(`No Skills match the requirement`, "error") + return false; + } + + let choice = await ItemDialog.create(skills, 1, {title : this.effect.name, text : "Select Skill"}) + + if (choice[0]) + { + this.item.updateSource({name : this.item.name.replace("Skill", choice[0].name)}) + this.effect.updateSource({name : `${this.item.name}`, "flags.wrath-and-glory.skill" : choice[0].id}); + } +} \ No newline at end of file diff --git a/effects/CP8NUqEi2SZYGmZ9.js b/effects/CP8NUqEi2SZYGmZ9.js new file mode 100644 index 0000000..81fd440 --- /dev/null +++ b/effects/CP8NUqEi2SZYGmZ9.js @@ -0,0 +1 @@ +return args.skill == "athletics" \ No newline at end of file diff --git a/effects/CTpN3Iuy4XpZLTmF.js b/effects/CTpN3Iuy4XpZLTmF.js new file mode 100644 index 0000000..6047c52 --- /dev/null +++ b/effects/CTpN3Iuy4XpZLTmF.js @@ -0,0 +1 @@ +return !args.weapon || args.weapon.isMelee || !args.weapon.hasKeyword("PRIMARIS") \ No newline at end of file diff --git a/effects/Cl0NORHSIPZGKMni.js b/effects/Cl0NORHSIPZGKMni.js new file mode 100644 index 0000000..d89cb31 --- /dev/null +++ b/effects/Cl0NORHSIPZGKMni.js @@ -0,0 +1 @@ +return args.skill == "cunning" \ No newline at end of file diff --git a/effects/CobcJwDZCfYI7RXM.js b/effects/CobcJwDZCfYI7RXM.js new file mode 100644 index 0000000..2f615b4 --- /dev/null +++ b/effects/CobcJwDZCfYI7RXM.js @@ -0,0 +1 @@ +args.fields.difficulty += 2 \ No newline at end of file diff --git a/effects/CxcKLAeyq0qsOEtv.js b/effects/CxcKLAeyq0qsOEtv.js new file mode 100644 index 0000000..94af0af --- /dev/null +++ b/effects/CxcKLAeyq0qsOEtv.js @@ -0,0 +1,5 @@ +let wound = 0 + this.effect.sourceTest.result.wound; +let shock = Math.ceil(CONFIG.Dice.randomUniform() * 3) + this.effect.sourceTest.result.shock;; + + +await this.actor.applyHealing({wounds: wound, shock: shock}, {messageData : this.script.getChatData()}); \ No newline at end of file diff --git a/effects/DDw6ZRQGqShyROa2.js b/effects/DDw6ZRQGqShyROa2.js new file mode 100644 index 0000000..42162a0 --- /dev/null +++ b/effects/DDw6ZRQGqShyROa2.js @@ -0,0 +1 @@ +return !["willpower", "toughness"].includes(args.attribute) \ No newline at end of file diff --git a/effects/DIQtlogkgKy7sNzG.js b/effects/DIQtlogkgKy7sNzG.js new file mode 100644 index 0000000..bd729a7 --- /dev/null +++ b/effects/DIQtlogkgKy7sNzG.js @@ -0,0 +1,5 @@ +if(args.options.fear || args.options.resolve) +{ + return false +} +return args.weapon || args.power || !["willpower", "intellect", "fellowship"].includes(args.attribute) \ No newline at end of file diff --git a/effects/DLKCiOfPGMDwgMPU.js b/effects/DLKCiOfPGMDwgMPU.js new file mode 100644 index 0000000..59201d3 --- /dev/null +++ b/effects/DLKCiOfPGMDwgMPU.js @@ -0,0 +1,5 @@ +for(let skill in args.system.skills) +{ + if (args.system.skills[skill].total < 3) + args.system.skills[skill].total = 3; +} \ No newline at end of file diff --git a/effects/DPin1O0dp6nU6gzn.js b/effects/DPin1O0dp6nU6gzn.js new file mode 100644 index 0000000..2874bce --- /dev/null +++ b/effects/DPin1O0dp6nU6gzn.js @@ -0,0 +1 @@ +return args.weapon.traitList.inflict?.rating == "On Fire"; \ No newline at end of file diff --git a/effects/DSDRdmg0jOGtNyjx.js b/effects/DSDRdmg0jOGtNyjx.js new file mode 100644 index 0000000..da14585 --- /dev/null +++ b/effects/DSDRdmg0jOGtNyjx.js @@ -0,0 +1 @@ +return args.skill != "stealth" \ No newline at end of file diff --git a/effects/DSHCECbXssRxCwtk.js b/effects/DSHCECbXssRxCwtk.js new file mode 100644 index 0000000..e9894fd --- /dev/null +++ b/effects/DSHCECbXssRxCwtk.js @@ -0,0 +1 @@ +return args.attribute != "fellowship" && args.skill != "survival" \ No newline at end of file diff --git a/effects/DUHfuHQK7gydcaBp.js b/effects/DUHfuHQK7gydcaBp.js new file mode 100644 index 0000000..9d8a3a3 --- /dev/null +++ b/effects/DUHfuHQK7gydcaBp.js @@ -0,0 +1 @@ +args.addShiftOption("wounds", "Wounds", "W"); \ No newline at end of file diff --git a/effects/DUI6OBk8lrBeHvqf.js b/effects/DUI6OBk8lrBeHvqf.js new file mode 100644 index 0000000..e31b0e5 --- /dev/null +++ b/effects/DUI6OBk8lrBeHvqf.js @@ -0,0 +1,7 @@ +let img = this.item.img; +let choice = await ItemDialog.create([{id : "WS", name : "Weapon Skill", img}, {id : "BS", name : "Ballistic Skill", img}]) + +if (choice[0]) +{ + this.item.updateSource({name : this.item.name + ` [${choice[0].id}]`}); +} \ No newline at end of file diff --git a/effects/DWXWAhr2s4muITW1.js b/effects/DWXWAhr2s4muITW1.js new file mode 100644 index 0000000..d95a369 --- /dev/null +++ b/effects/DWXWAhr2s4muITW1.js @@ -0,0 +1 @@ +return !args.actor.system.combat.stealth \ No newline at end of file diff --git a/effects/DkgzIdxwoXcGusDV.js b/effects/DkgzIdxwoXcGusDV.js new file mode 100644 index 0000000..8ddd01b --- /dev/null +++ b/effects/DkgzIdxwoXcGusDV.js @@ -0,0 +1,2 @@ +this.actor.spend("system.resources.faith.current") +this.script.notification("Spent 1 Faith"); \ No newline at end of file diff --git a/effects/DnkY4E4frqtmNw10.js b/effects/DnkY4E4frqtmNw10.js new file mode 100644 index 0000000..1e296c1 --- /dev/null +++ b/effects/DnkY4E4frqtmNw10.js @@ -0,0 +1,5 @@ +let item = (await fromUuid("Compendium.wng-core.items.Item.HSTXKR5NVmndHpOT")).toObject(); + +item.system.equipped = true; + +this.actor.createEmbeddedDocuments("Item", [item], {fromEffect : this.effect.id}) \ No newline at end of file diff --git a/effects/DtGXkwrXvo1HRFMe.js b/effects/DtGXkwrXvo1HRFMe.js new file mode 100644 index 0000000..90f5331 --- /dev/null +++ b/effects/DtGXkwrXvo1HRFMe.js @@ -0,0 +1 @@ +return args.actor.statuses.has("wounded") \ No newline at end of file diff --git a/effects/Dv2FHMh1PeyDnMow.js b/effects/Dv2FHMh1PeyDnMow.js new file mode 100644 index 0000000..4e04d37 --- /dev/null +++ b/effects/Dv2FHMh1PeyDnMow.js @@ -0,0 +1 @@ +return args.options.fear \ No newline at end of file diff --git a/effects/DzXwv7X6jCAnO9UU.js b/effects/DzXwv7X6jCAnO9UU.js new file mode 100644 index 0000000..d4fea81 --- /dev/null +++ b/effects/DzXwv7X6jCAnO9UU.js @@ -0,0 +1,4 @@ +if (args.combat.combatant?.token?.actor.uuid == this.effect.sourceActor.uuid) +{ + this.effect.delete(); +} \ No newline at end of file diff --git a/effects/EDZOzmX7jeJxvBqp.js b/effects/EDZOzmX7jeJxvBqp.js new file mode 100644 index 0000000..8c85fdb --- /dev/null +++ b/effects/EDZOzmX7jeJxvBqp.js @@ -0,0 +1 @@ +return args.skill != "deception" \ No newline at end of file diff --git a/effects/EPpf9etWAFHBjEfg.js b/effects/EPpf9etWAFHBjEfg.js new file mode 100644 index 0000000..9158499 --- /dev/null +++ b/effects/EPpf9etWAFHBjEfg.js @@ -0,0 +1 @@ +return !args.weapon || !args.weapon.isRanged || !args.target.system.advances?.tier; \ No newline at end of file diff --git a/effects/EjPuVimeJAQR1Wl6.js b/effects/EjPuVimeJAQR1Wl6.js new file mode 100644 index 0000000..98127db --- /dev/null +++ b/effects/EjPuVimeJAQR1Wl6.js @@ -0,0 +1,4 @@ +if (args.test?.item?.type == "psychicPower") +{ + args.mortalDetermination = true; +} \ No newline at end of file diff --git a/effects/EknZ9g3HNqdZzWM0.js b/effects/EknZ9g3HNqdZzWM0.js new file mode 100644 index 0000000..3ad92e9 --- /dev/null +++ b/effects/EknZ9g3HNqdZzWM0.js @@ -0,0 +1 @@ +args.fields.ed.value += (args.actor.system.advances.rank) \ No newline at end of file diff --git a/effects/EofUDIUTQaDBmDWf.js b/effects/EofUDIUTQaDBmDWf.js new file mode 100644 index 0000000..850b9e0 --- /dev/null +++ b/effects/EofUDIUTQaDBmDWf.js @@ -0,0 +1 @@ +return args.attribute == this.effect.getFlag(game.system.id, "attribute"); \ No newline at end of file diff --git a/effects/EpWGJjgVutx3kJXO.js b/effects/EpWGJjgVutx3kJXO.js new file mode 100644 index 0000000..64fdcd6 --- /dev/null +++ b/effects/EpWGJjgVutx3kJXO.js @@ -0,0 +1 @@ +args.resilience.invulnerable = false; \ No newline at end of file diff --git a/effects/Eq9PeNuPABX5Ist3.js b/effects/Eq9PeNuPABX5Ist3.js new file mode 100644 index 0000000..28766c2 --- /dev/null +++ b/effects/Eq9PeNuPABX5Ist3.js @@ -0,0 +1,5 @@ +let roll = Math.ceil(CONFIG.Dice.randomUniform() * 3); + +let shock = this.effect.sourceActor.system.advances.rank + roll; + +this.actor.applyHealing({shock}, {messageData : this.script.getChatData()}); \ No newline at end of file diff --git a/effects/ErneCiMv3usTHl3h.js b/effects/ErneCiMv3usTHl3h.js new file mode 100644 index 0000000..266d6ad --- /dev/null +++ b/effects/ErneCiMv3usTHl3h.js @@ -0,0 +1 @@ +args.fields.pool += 4; \ No newline at end of file diff --git a/effects/F0lqM12xDvYNF1KO.js b/effects/F0lqM12xDvYNF1KO.js new file mode 100644 index 0000000..ebd68fa --- /dev/null +++ b/effects/F0lqM12xDvYNF1KO.js @@ -0,0 +1,4 @@ +if (args.fields.level != "unbound") +{ + args.fields.level = "unbound"; +} \ No newline at end of file diff --git a/effects/F4QyV4jeDJ9UyQL1.js b/effects/F4QyV4jeDJ9UyQL1.js new file mode 100644 index 0000000..a863452 --- /dev/null +++ b/effects/F4QyV4jeDJ9UyQL1.js @@ -0,0 +1 @@ +return args.skill == this.effect.getFlag(game.system.id, "skill"); \ No newline at end of file diff --git a/effects/F9z9SZ9nikSYIqBj.js b/effects/F9z9SZ9nikSYIqBj.js new file mode 100644 index 0000000..d7a07db --- /dev/null +++ b/effects/F9z9SZ9nikSYIqBj.js @@ -0,0 +1,24 @@ +if (this.item.specifier == "Skill") + { + let skills = ["athletics", + "deception", + "intimidation", + "persuasion", + "tech"].map(i => { + return {id : i, name : systemConfig().skills[i], img : this.effect.img} + }).filter(i => this.actor.system.skills[i.id].rating >= 4); + + if (skills.length == 0) + { + this.script.notification(`No Skills match the requirement`, "error") + return false; + } + + let choice = await ItemDialog.create(skills, 1, {title : this.effect.name, text : "Select Skill"}) + + if (choice[0]) + { + this.item.updateSource({name : this.item.name.replace("Skill", choice[0].name)}) + this.effect.updateSource({name : `${this.item.name}`, "flags.wrath-and-glory.skill" : choice[0].id}); + } +} \ No newline at end of file diff --git a/effects/FEQWJ4DGNHm3gbDf.js b/effects/FEQWJ4DGNHm3gbDf.js new file mode 100644 index 0000000..f9da80e --- /dev/null +++ b/effects/FEQWJ4DGNHm3gbDf.js @@ -0,0 +1 @@ +return !args.options.fear && !args.options.corruption \ No newline at end of file diff --git a/effects/FWM1XKT6uSGVm8o1.js b/effects/FWM1XKT6uSGVm8o1.js new file mode 100644 index 0000000..c91d41f --- /dev/null +++ b/effects/FWM1XKT6uSGVm8o1.js @@ -0,0 +1 @@ +return !args.target.statuses.has("fullCover") && !args.target.statuses.has("halfCover"); \ No newline at end of file diff --git a/effects/FfuXSl90FhKLFAV5.js b/effects/FfuXSl90FhKLFAV5.js new file mode 100644 index 0000000..4f80d6b --- /dev/null +++ b/effects/FfuXSl90FhKLFAV5.js @@ -0,0 +1 @@ +args.fields.pool += args.actor.system.mob.value; \ No newline at end of file diff --git a/effects/FjfWaGeBfWlAWQFW.js b/effects/FjfWaGeBfWlAWQFW.js new file mode 100644 index 0000000..209c177 --- /dev/null +++ b/effects/FjfWaGeBfWlAWQFW.js @@ -0,0 +1,11 @@ +let effects = this.item.effects.contents.filter(i => i.id != this.effect.id); + +effects.forEach(e => { + e.update({disabled : true}) +}) + +let e = effects.find(i => i.id == "93ZacxDC5SSsOAfb"); + +e.update({disabled : false}); + +this.script.notification(`${e.name} (+3 Defence) Activated`) \ No newline at end of file diff --git a/effects/Fv2f4myS5meJjkzs.js b/effects/Fv2f4myS5meJjkzs.js new file mode 100644 index 0000000..40eb894 --- /dev/null +++ b/effects/Fv2f4myS5meJjkzs.js @@ -0,0 +1 @@ +return args.weapon.system.isMelee; \ No newline at end of file diff --git a/effects/G8XJl9hmqiEVxHvz.js b/effects/G8XJl9hmqiEVxHvz.js new file mode 100644 index 0000000..313607f --- /dev/null +++ b/effects/G8XJl9hmqiEVxHvz.js @@ -0,0 +1,5 @@ +if (args.item.type == "weapon" && args.item.hasKeyword(["POWER FIELD"])) +{ + // See https://github.com/foundryvtt/foundryvtt/issues/7987 + args.item.system.damage.ap.bonus -= this.actor.system.advances.rank / 2 +} \ No newline at end of file diff --git a/effects/G8kIIKqPTgQHCZ5D.js b/effects/G8kIIKqPTgQHCZ5D.js new file mode 100644 index 0000000..3aac636 --- /dev/null +++ b/effects/G8kIIKqPTgQHCZ5D.js @@ -0,0 +1 @@ +args.fields.difficulty += (this.effect.sourceTest.result.defence) \ No newline at end of file diff --git a/effects/GtMnwu9j9pCbSpiO.js b/effects/GtMnwu9j9pCbSpiO.js new file mode 100644 index 0000000..0eac8ea --- /dev/null +++ b/effects/GtMnwu9j9pCbSpiO.js @@ -0,0 +1 @@ +args.fields.pool += (args.actor.system.test.result.bonus) \ No newline at end of file diff --git a/effects/GxyOrCjQTV8uiXBd.js b/effects/GxyOrCjQTV8uiXBd.js new file mode 100644 index 0000000..9c1a0d4 --- /dev/null +++ b/effects/GxyOrCjQTV8uiXBd.js @@ -0,0 +1,20 @@ +let effects = this.effect.sourceItem.effects.contents.filter(i => i.id != this.effect.id); + +if (this.actor.name === "Aberrant") +{ + let choice = await ItemDialog.create(effects, 1, {title : this.effect.name, text: this.script.name}); + + if (choice[0]) + { + this.actor.applyEffect({effectUuids: choice[0].uuid}); + } +} +else +{ + let roll = await new Roll("1d3").roll(); + + roll.toMessage(this.script.getChatData()); + + let chosenEffect = effects[roll.total-1]; + this.actor.applyEffect({effectUuids: chosenEffect.uuid}); +} \ No newline at end of file diff --git a/effects/H25ksa52eRFkWQnT.js b/effects/H25ksa52eRFkWQnT.js new file mode 100644 index 0000000..cb0268b --- /dev/null +++ b/effects/H25ksa52eRFkWQnT.js @@ -0,0 +1 @@ +return args.skill != "insight" \ No newline at end of file diff --git a/effects/H9a2LNH7YSDMvZzh.js b/effects/H9a2LNH7YSDMvZzh.js new file mode 100644 index 0000000..2bde595 --- /dev/null +++ b/effects/H9a2LNH7YSDMvZzh.js @@ -0,0 +1,14 @@ +let skills = ["awareness", + "cunning", + "deception", + "insight", + "persuasion", + "psychicMastery", + "stealth"].map(i => { + return {id : i, name : systemConfig().skills[i], img : this.effect.img} + }); + let choice = await ItemDialog.create(skills, 1, {title : this.effect.name, text : "Select Skill"}) + if (choice[0]) + { + this.effect.updateSource({name : `${this.item.name} [${choice[0].name}]`, "flags.wrath-and-glory.skill" : choice[0].id}); + } \ No newline at end of file diff --git a/effects/HW2u4vWec1VNrBkX.js b/effects/HW2u4vWec1VNrBkX.js new file mode 100644 index 0000000..84633d3 --- /dev/null +++ b/effects/HW2u4vWec1VNrBkX.js @@ -0,0 +1 @@ +args.fields.pool += (args.actor.system.advances.rank) \ No newline at end of file diff --git a/effects/HnaVYE8Th4xFalOM.js b/effects/HnaVYE8Th4xFalOM.js new file mode 100644 index 0000000..af942cf --- /dev/null +++ b/effects/HnaVYE8Th4xFalOM.js @@ -0,0 +1 @@ +return args.skill != this.effect.getFlag(game.system.id, "skill"); \ No newline at end of file diff --git a/effects/HoK2qFwgUkl1RKW2.js b/effects/HoK2qFwgUkl1RKW2.js new file mode 100644 index 0000000..354e7e3 --- /dev/null +++ b/effects/HoK2qFwgUkl1RKW2.js @@ -0,0 +1 @@ +this.actor.system.combat.resilience.invulnerable = true; \ No newline at end of file diff --git a/effects/I1dYoHN21F2dbyol.js b/effects/I1dYoHN21F2dbyol.js new file mode 100644 index 0000000..d32f244 --- /dev/null +++ b/effects/I1dYoHN21F2dbyol.js @@ -0,0 +1 @@ +return args.options.corruption || args.options.mutation \ No newline at end of file diff --git a/effects/IFMyExNp5b9jIzqV.js b/effects/IFMyExNp5b9jIzqV.js new file mode 100644 index 0000000..70e69d2 --- /dev/null +++ b/effects/IFMyExNp5b9jIzqV.js @@ -0,0 +1,2 @@ +this.actor.system.combat.resilience.invulnerable = true; +this.actor.system.combat.resilience.total += 1; \ No newline at end of file diff --git a/effects/IP3rY6Lb4LX3AcxT.js b/effects/IP3rY6Lb4LX3AcxT.js new file mode 100644 index 0000000..fd65aa2 --- /dev/null +++ b/effects/IP3rY6Lb4LX3AcxT.js @@ -0,0 +1 @@ +return args.options.fear || args.options.corruption \ No newline at end of file diff --git a/effects/IQhq0N46YRPDHtbr.js b/effects/IQhq0N46YRPDHtbr.js new file mode 100644 index 0000000..deb4eb7 --- /dev/null +++ b/effects/IQhq0N46YRPDHtbr.js @@ -0,0 +1 @@ +return this.actor.statuses.has("wounded") \ No newline at end of file diff --git a/effects/ISH5OJH9DkzLC0ot.js b/effects/ISH5OJH9DkzLC0ot.js new file mode 100644 index 0000000..18d4eee --- /dev/null +++ b/effects/ISH5OJH9DkzLC0ot.js @@ -0,0 +1 @@ +args.fields.damage += 1; \ No newline at end of file diff --git a/effects/IdfHvESnId2ZRGZA.js b/effects/IdfHvESnId2ZRGZA.js new file mode 100644 index 0000000..4b619ae --- /dev/null +++ b/effects/IdfHvESnId2ZRGZA.js @@ -0,0 +1,4 @@ +if (this.actor.spend("system.resources.faith.current", 2)) +{ + this.script.message("Spent 2 Faith"); +} \ No newline at end of file diff --git a/effects/In83yMaxcTlgZ887.js b/effects/In83yMaxcTlgZ887.js new file mode 100644 index 0000000..9959c68 --- /dev/null +++ b/effects/In83yMaxcTlgZ887.js @@ -0,0 +1 @@ +return !["scholar", "deception", "cunning"].includes(args.skill) \ No newline at end of file diff --git a/effects/InAf6SJ93t3jEliQ.js b/effects/InAf6SJ93t3jEliQ.js new file mode 100644 index 0000000..1384c23 --- /dev/null +++ b/effects/InAf6SJ93t3jEliQ.js @@ -0,0 +1 @@ +return args.skill != "weaponSkill" \ No newline at end of file diff --git a/effects/IpYIXx6ESt9E0D3I.js b/effects/IpYIXx6ESt9E0D3I.js new file mode 100644 index 0000000..f050c75 --- /dev/null +++ b/effects/IpYIXx6ESt9E0D3I.js @@ -0,0 +1,9 @@ +if (args.result.isSuccess) +{ + let shock = 1 + this.actor.system.advances.rank * 2 + (this.effect.sourceTest.testData.shifted.hardy.dice.length || 0) + this.actor.applyHealing({shock}, {messageData : this.script.getChatData()}) +} +else +{ + this.actor.applyHealing({shock : 1}, {messageData : this.script.getChatData()}) +} \ No newline at end of file diff --git a/effects/Iq9NZ7FHOFDYqkGk.js b/effects/Iq9NZ7FHOFDYqkGk.js new file mode 100644 index 0000000..c5ee7a9 --- /dev/null +++ b/effects/Iq9NZ7FHOFDYqkGk.js @@ -0,0 +1,13 @@ +if (args.item.system.isEquipped && args.item.type == "weapon" && args.item.hasKeyword("BLADE")) +{ +if (args.item._source.system.traits.list.find(i => i.name == "parry")) +{ + args.item.flags.hasParry= true; +} +else +{ + args.item.system.traits.list.push({name : "parry"}) +} + + +} \ No newline at end of file diff --git a/effects/IrgVdpyPNCZXhnet.js b/effects/IrgVdpyPNCZXhnet.js new file mode 100644 index 0000000..132e661 --- /dev/null +++ b/effects/IrgVdpyPNCZXhnet.js @@ -0,0 +1 @@ +return args.skill != "awareness" \ No newline at end of file diff --git a/effects/IsseGpLLkviAFz2R.js b/effects/IsseGpLLkviAFz2R.js new file mode 100644 index 0000000..146059b --- /dev/null +++ b/effects/IsseGpLLkviAFz2R.js @@ -0,0 +1 @@ +return args.weapon.isRanged \ No newline at end of file diff --git a/effects/Iyygo7jRj0uw2TTH.js b/effects/Iyygo7jRj0uw2TTH.js new file mode 100644 index 0000000..3a3c8ef --- /dev/null +++ b/effects/Iyygo7jRj0uw2TTH.js @@ -0,0 +1,6 @@ +if (args.combat.combatant?.token?.actor?.uuid == this.effect.sourceActor.uuid) +{ + +this.actor.setupAttributeTest("willpower", {appendTitle : ` - ${this.effect.name}`}); + +} \ No newline at end of file diff --git a/effects/JFE35oMF5rNK2udD.js b/effects/JFE35oMF5rNK2udD.js new file mode 100644 index 0000000..4e8b42f --- /dev/null +++ b/effects/JFE35oMF5rNK2udD.js @@ -0,0 +1 @@ +args.fields.pool += (this.effect.sourceTest.result.WSBonus) \ No newline at end of file diff --git a/effects/JPmpOPoLol0cnAIa.js b/effects/JPmpOPoLol0cnAIa.js new file mode 100644 index 0000000..21ed82b --- /dev/null +++ b/effects/JPmpOPoLol0cnAIa.js @@ -0,0 +1 @@ +this.effect.updateSource({name : this.effect.setSpecifier(this.effect.getFlag(game.system.id, 'value') || 1)}) \ No newline at end of file diff --git a/effects/JQbgTsNyhe6rL4lJ.js b/effects/JQbgTsNyhe6rL4lJ.js new file mode 100644 index 0000000..5e38689 --- /dev/null +++ b/effects/JQbgTsNyhe6rL4lJ.js @@ -0,0 +1 @@ +args.fields.pool += 1; \ No newline at end of file diff --git a/effects/JuKaOYhSmcApqIc1.js b/effects/JuKaOYhSmcApqIc1.js new file mode 100644 index 0000000..4600ad0 --- /dev/null +++ b/effects/JuKaOYhSmcApqIc1.js @@ -0,0 +1 @@ +args.fields.pool += this.actor.system.advances.rank \ No newline at end of file diff --git a/effects/Jyf9Km4G1kNkcEyX.js b/effects/Jyf9Km4G1kNkcEyX.js new file mode 100644 index 0000000..a6f168f --- /dev/null +++ b/effects/Jyf9Km4G1kNkcEyX.js @@ -0,0 +1 @@ +return !this.effect.specifier || args.skill != "survival" \ No newline at end of file diff --git a/effects/K0MBJAgg4qU98fxN.js b/effects/K0MBJAgg4qU98fxN.js new file mode 100644 index 0000000..113a0fb --- /dev/null +++ b/effects/K0MBJAgg4qU98fxN.js @@ -0,0 +1,4 @@ +if (args.test?.item?.flags?.hasBrutal) +{ + args.modifiers.damage.push({label : this.effect.name, value : 1}) +} \ No newline at end of file diff --git a/effects/K8eBW9NiQHrTY7jP.js b/effects/K8eBW9NiQHrTY7jP.js new file mode 100644 index 0000000..f192886 --- /dev/null +++ b/effects/K8eBW9NiQHrTY7jP.js @@ -0,0 +1 @@ +args.fields.pool += (this.effect.sourceTest.result.stealth) \ No newline at end of file diff --git a/effects/KBhnj53oQ6huS93m.js b/effects/KBhnj53oQ6huS93m.js new file mode 100644 index 0000000..7f48b40 --- /dev/null +++ b/effects/KBhnj53oQ6huS93m.js @@ -0,0 +1,14 @@ +let fear = this.actor.hasCondition("fear"); +let terror = this.actor.hasCondition("terror"); + +if (fear) +{ + this.script.notification("Removed Fear"); +} +if (terror) +{ + this.script.notification("Removed Terror"); +} + +fear?.delete(); +terror?.delete(); \ No newline at end of file diff --git a/effects/KCw9i0XxO94qhZeK.js b/effects/KCw9i0XxO94qhZeK.js new file mode 100644 index 0000000..e78b8b3 --- /dev/null +++ b/effects/KCw9i0XxO94qhZeK.js @@ -0,0 +1 @@ +return !args.options.corruption \ No newline at end of file diff --git a/effects/KInyOtvNgm4LPm6C.js b/effects/KInyOtvNgm4LPm6C.js new file mode 100644 index 0000000..8c9a686 --- /dev/null +++ b/effects/KInyOtvNgm4LPm6C.js @@ -0,0 +1 @@ +args.fields.pool += (this.effect.sourceTest.result.defenceAndStealth) \ No newline at end of file diff --git a/effects/KpRXZG54C6JQrlHG.js b/effects/KpRXZG54C6JQrlHG.js new file mode 100644 index 0000000..18afa7f --- /dev/null +++ b/effects/KpRXZG54C6JQrlHG.js @@ -0,0 +1 @@ +args.fields.difficulty += 4 \ No newline at end of file diff --git a/effects/KppzdgCK5UQ16G7M.js b/effects/KppzdgCK5UQ16G7M.js new file mode 100644 index 0000000..fec8633 --- /dev/null +++ b/effects/KppzdgCK5UQ16G7M.js @@ -0,0 +1 @@ +return !(args.target && (args.target.statuses.has("halfCover") || args.target.statuses.has("fullCover"))); \ No newline at end of file diff --git a/effects/KsnmyJkm0xCTnxTJ.js b/effects/KsnmyJkm0xCTnxTJ.js new file mode 100644 index 0000000..2091ed2 --- /dev/null +++ b/effects/KsnmyJkm0xCTnxTJ.js @@ -0,0 +1 @@ +return !args.attribute === 'willpower' && !args.options.conviction && !args.options.resolve; \ No newline at end of file diff --git a/effects/KtJx2wvotTdvRb6Z.js b/effects/KtJx2wvotTdvRb6Z.js new file mode 100644 index 0000000..f5fa3e4 --- /dev/null +++ b/effects/KtJx2wvotTdvRb6Z.js @@ -0,0 +1,6 @@ +if (args.test.result.isWrathCritical) +{ + args.modifiers.mortal.push({label: this.effect.name, value: 1}); +} + +let report = await this.actor.applyDamage(0, {mortal: 1}); \ No newline at end of file diff --git a/effects/L3U59xnZcFAwHT7C.js b/effects/L3U59xnZcFAwHT7C.js new file mode 100644 index 0000000..b54df2a --- /dev/null +++ b/effects/L3U59xnZcFAwHT7C.js @@ -0,0 +1,2 @@ +game.wng.RuinGloryCounter.changeCounter(1, "ruin"); +this.script.notification("Gained 1 Ruin"); \ No newline at end of file diff --git a/effects/LDFsgiIy9pAyTAaf.js b/effects/LDFsgiIy9pAyTAaf.js new file mode 100644 index 0000000..3b69f17 --- /dev/null +++ b/effects/LDFsgiIy9pAyTAaf.js @@ -0,0 +1,5 @@ +await this.actor.update({"system.combat.wounds.value" : this.actor.system.combat.wounds.value + this.actor.system.advances.tier}) + +this.item.effects.getName("Flagellated").update({disabled: false}) + +this.script.message(`Self-inflicted ${this.actor.system.advances.tier} Wounds`) \ No newline at end of file diff --git a/effects/LDKGK25becwc1usF.js b/effects/LDKGK25becwc1usF.js new file mode 100644 index 0000000..ed7c9f4 --- /dev/null +++ b/effects/LDKGK25becwc1usF.js @@ -0,0 +1,2 @@ +this.effect.update({name : this.effect.baseName}) +this.script.notification(`Target Cleared`) \ No newline at end of file diff --git a/effects/LGstPMwG2Wa8dmG1.js b/effects/LGstPMwG2Wa8dmG1.js new file mode 100644 index 0000000..36a3f0f --- /dev/null +++ b/effects/LGstPMwG2Wa8dmG1.js @@ -0,0 +1,6 @@ +if (args.options.resolve) +{ + this.script.notification("Does not makes Resolve Tests"); + args.abort = true; +} +else return true; \ No newline at end of file diff --git a/effects/LJSFGNiAzZ9RtrR0.js b/effects/LJSFGNiAzZ9RtrR0.js new file mode 100644 index 0000000..f69a3fa --- /dev/null +++ b/effects/LJSFGNiAzZ9RtrR0.js @@ -0,0 +1,18 @@ +let items = ["Compendium.wng-core.items.Item.JVz6FoVEc7p1DJUj", "Compendium.wng-core.items.Item.n0bj5UJjgglDzFcB"]; + +let minor = await Promise.all(["Compendium.wng-core.items.uQKBlm3iW0GYDy83", +"Compendium.wng-core.items.33nu3kaw8zNOPx8h", +"Compendium.wng-core.items.ehGa28RU82KxCDrZ", +"Compendium.wng-core.items.qPAsmaoh7YuxMAbt", +"Compendium.wng-core.items.6uxTxs8yqfJygqsG", +"Compendium.wng-core.items.rWQj4yseWbT0ffSf"].map(fromUuid)) + +let choice = await ItemDialog.create(minor, 1, {title : this.effect.name, text : "Choose 1 Rune of Battle Power"}); + +if (choice[0]) +{ + items.push(choice[0].uuid); +} + + +this.actor.createEmbeddedDocuments("Item", (await Promise.all(items.map(fromUuid))), {fromEffect: this.effect.id}); \ No newline at end of file diff --git a/effects/LOjdxoTYMCnrJIx6.js b/effects/LOjdxoTYMCnrJIx6.js new file mode 100644 index 0000000..063b862 --- /dev/null +++ b/effects/LOjdxoTYMCnrJIx6.js @@ -0,0 +1,2 @@ +let keyword = this.effect.specifier; +return args.target.hasKeyword(keyword); \ No newline at end of file diff --git a/effects/LPBJwM3xygw6bnfK.js b/effects/LPBJwM3xygw6bnfK.js new file mode 100644 index 0000000..b10ffb8 --- /dev/null +++ b/effects/LPBJwM3xygw6bnfK.js @@ -0,0 +1 @@ +return args.weapon?.id == this.effect.getFlag(game.system.id, "trademarkId") \ No newline at end of file diff --git a/effects/LahIj0aZ5UkSRMOb.js b/effects/LahIj0aZ5UkSRMOb.js new file mode 100644 index 0000000..87defc1 --- /dev/null +++ b/effects/LahIj0aZ5UkSRMOb.js @@ -0,0 +1 @@ +return args.skill != "cunning" \ No newline at end of file diff --git a/effects/LlYG6NQFN2ccsIzG.js b/effects/LlYG6NQFN2ccsIzG.js new file mode 100644 index 0000000..6f07daa --- /dev/null +++ b/effects/LlYG6NQFN2ccsIzG.js @@ -0,0 +1 @@ +return args.weapon?.isMelee \ No newline at end of file diff --git a/effects/M0LuZmdoUoSn6wBd.js b/effects/M0LuZmdoUoSn6wBd.js new file mode 100644 index 0000000..4c33be3 --- /dev/null +++ b/effects/M0LuZmdoUoSn6wBd.js @@ -0,0 +1 @@ +return args.skill != "tech" \ No newline at end of file diff --git a/effects/M6sahvW09BCRJ3NN.js b/effects/M6sahvW09BCRJ3NN.js new file mode 100644 index 0000000..16da0a0 --- /dev/null +++ b/effects/M6sahvW09BCRJ3NN.js @@ -0,0 +1 @@ +args.fields.ed.value += args.target.system.advances.tier; \ No newline at end of file diff --git a/effects/MNuH94NRXq3wbhyN.js b/effects/MNuH94NRXq3wbhyN.js new file mode 100644 index 0000000..e762a3c --- /dev/null +++ b/effects/MNuH94NRXq3wbhyN.js @@ -0,0 +1 @@ +return arg.skill != "intimidation" \ No newline at end of file diff --git a/effects/MSNac1iGV1HHHgfO.js b/effects/MSNac1iGV1HHHgfO.js new file mode 100644 index 0000000..8de814c --- /dev/null +++ b/effects/MSNac1iGV1HHHgfO.js @@ -0,0 +1 @@ +this.effect.update({disabled: true}); \ No newline at end of file diff --git a/effects/MbVTjkQEMuZi5Net.js b/effects/MbVTjkQEMuZi5Net.js new file mode 100644 index 0000000..55390d0 --- /dev/null +++ b/effects/MbVTjkQEMuZi5Net.js @@ -0,0 +1 @@ +return !args.target || !args.target?.system.advances?.tier; \ No newline at end of file diff --git a/effects/Mg5O1YeSudh3qRE5.js b/effects/Mg5O1YeSudh3qRE5.js new file mode 100644 index 0000000..4637533 --- /dev/null +++ b/effects/Mg5O1YeSudh3qRE5.js @@ -0,0 +1 @@ +args.fields.pool += 1 \ No newline at end of file diff --git a/effects/MlSeK9tDcqy1XkpH.js b/effects/MlSeK9tDcqy1XkpH.js new file mode 100644 index 0000000..b2303b8 --- /dev/null +++ b/effects/MlSeK9tDcqy1XkpH.js @@ -0,0 +1 @@ +args.mortalDetermination = true; \ No newline at end of file diff --git a/effects/MuKDLBheaG9oW0wB.js b/effects/MuKDLBheaG9oW0wB.js new file mode 100644 index 0000000..0e9dacb --- /dev/null +++ b/effects/MuKDLBheaG9oW0wB.js @@ -0,0 +1 @@ +return args.weapon?.name !== "Scoped Needle Pistol" || args.fields.range != "long"; \ No newline at end of file diff --git a/effects/NGVjoMp9wB80n7xo.js b/effects/NGVjoMp9wB80n7xo.js new file mode 100644 index 0000000..df74f65 --- /dev/null +++ b/effects/NGVjoMp9wB80n7xo.js @@ -0,0 +1 @@ +return !args.weapon || args.weapon.isRanged || !this.actor.statuses.has("wounded") \ No newline at end of file diff --git a/effects/NN1chi0lu14wYMGw.js b/effects/NN1chi0lu14wYMGw.js new file mode 100644 index 0000000..82e5ff3 --- /dev/null +++ b/effects/NN1chi0lu14wYMGw.js @@ -0,0 +1 @@ +return !["leadership", "intimidation"].includes(args.skill) \ No newline at end of file diff --git a/effects/NOL4VOguuH3lLMHP.js b/effects/NOL4VOguuH3lLMHP.js new file mode 100644 index 0000000..cb80a1d --- /dev/null +++ b/effects/NOL4VOguuH3lLMHP.js @@ -0,0 +1 @@ +this.actor.removeCondition("hindered") \ No newline at end of file diff --git a/effects/NTdpO0kFNkWuX2Ry.js b/effects/NTdpO0kFNkWuX2Ry.js new file mode 100644 index 0000000..a2da4d9 --- /dev/null +++ b/effects/NTdpO0kFNkWuX2Ry.js @@ -0,0 +1 @@ +return args.power; \ No newline at end of file diff --git a/effects/NdU2gZsMZmJOY6CM.js b/effects/NdU2gZsMZmJOY6CM.js new file mode 100644 index 0000000..fa54478 --- /dev/null +++ b/effects/NdU2gZsMZmJOY6CM.js @@ -0,0 +1 @@ +args.fields.ed.value += (this.effect.sourceTest.result.edBonus) \ No newline at end of file diff --git a/effects/NhtHwF6MBxFiV6iE.js b/effects/NhtHwF6MBxFiV6iE.js new file mode 100644 index 0000000..147969a --- /dev/null +++ b/effects/NhtHwF6MBxFiV6iE.js @@ -0,0 +1 @@ +return args.skill !== "stealth"; \ No newline at end of file diff --git a/effects/NwmyFJv6SXZZc6sR.js b/effects/NwmyFJv6SXZZc6sR.js new file mode 100644 index 0000000..026dba2 --- /dev/null +++ b/effects/NwmyFJv6SXZZc6sR.js @@ -0,0 +1,8 @@ +let dying = this.actor.hasCondition("dying") +if (dying) +{ + await dying.delete(); + await this.actor.update({"system.combat.wounds.value": this.actor.system.combat.wounds.max - 1}) + await this.effect.update({"disabled" : true}); + this.script.notification("Prevented Dying"); +} \ No newline at end of file diff --git a/effects/Nwo2XQpXvaxqkd13.js b/effects/Nwo2XQpXvaxqkd13.js new file mode 100644 index 0000000..30bf458 --- /dev/null +++ b/effects/Nwo2XQpXvaxqkd13.js @@ -0,0 +1 @@ +return != args.options.resolve; \ No newline at end of file diff --git a/effects/OZOFbqSXc32c1dcp.js b/effects/OZOFbqSXc32c1dcp.js new file mode 100644 index 0000000..2566dd5 --- /dev/null +++ b/effects/OZOFbqSXc32c1dcp.js @@ -0,0 +1 @@ +return args.weapon?.isRanged \ No newline at end of file diff --git a/effects/Ox1wW1KM6ou0u6Sd.js b/effects/Ox1wW1KM6ou0u6Sd.js new file mode 100644 index 0000000..3376454 --- /dev/null +++ b/effects/Ox1wW1KM6ou0u6Sd.js @@ -0,0 +1,2 @@ +this.actor.addCondition("prone"); +this.actor.addCondition("restrained"); \ No newline at end of file diff --git a/effects/P5SH2LHnW76styQF.js b/effects/P5SH2LHnW76styQF.js new file mode 100644 index 0000000..42de360 --- /dev/null +++ b/effects/P5SH2LHnW76styQF.js @@ -0,0 +1 @@ +return args.skill == "deception" \ No newline at end of file diff --git a/effects/PBy7IGCnIhzN1eOJ.js b/effects/PBy7IGCnIhzN1eOJ.js new file mode 100644 index 0000000..a844559 --- /dev/null +++ b/effects/PBy7IGCnIhzN1eOJ.js @@ -0,0 +1 @@ +args.fields.difficulty += this.effect.sourceActor.system.attributes.willpower.total \ No newline at end of file diff --git a/effects/PHel0Bush3ZEHpcE.js b/effects/PHel0Bush3ZEHpcE.js new file mode 100644 index 0000000..0fbd8c6 --- /dev/null +++ b/effects/PHel0Bush3ZEHpcE.js @@ -0,0 +1 @@ +return !this.effect.specifier || !args.weapon || args.weapon.isRanged \ No newline at end of file diff --git a/effects/PQVukt8ftbDCmvrA.js b/effects/PQVukt8ftbDCmvrA.js new file mode 100644 index 0000000..83ccf64 --- /dev/null +++ b/effects/PQVukt8ftbDCmvrA.js @@ -0,0 +1 @@ +return args.data.weapon; \ No newline at end of file diff --git a/effects/PYT5fqC4zkcLrnWX.js b/effects/PYT5fqC4zkcLrnWX.js new file mode 100644 index 0000000..05f2ce5 --- /dev/null +++ b/effects/PYT5fqC4zkcLrnWX.js @@ -0,0 +1 @@ +return !args.weapon.hasKeyword("SHURIKEN"); \ No newline at end of file diff --git a/effects/PrhIVyqSEt53cOvR.js b/effects/PrhIVyqSEt53cOvR.js new file mode 100644 index 0000000..bff3d73 --- /dev/null +++ b/effects/PrhIVyqSEt53cOvR.js @@ -0,0 +1 @@ +return args.type == "corruption" && this.actor.corruption.current == 0 \ No newline at end of file diff --git a/effects/PyQkC1VM4692Fw0y.js b/effects/PyQkC1VM4692Fw0y.js new file mode 100644 index 0000000..4d46ed7 --- /dev/null +++ b/effects/PyQkC1VM4692Fw0y.js @@ -0,0 +1 @@ +this.actor.applyHealing({shock : this.actor.system.advances.rank}, {messageData : this.script.getChatData()}) \ No newline at end of file diff --git a/effects/QFuGceUxNi14pKXW.js b/effects/QFuGceUxNi14pKXW.js new file mode 100644 index 0000000..1298fc6 --- /dev/null +++ b/effects/QFuGceUxNi14pKXW.js @@ -0,0 +1 @@ +return args.fields.range === "short"; \ No newline at end of file diff --git a/effects/QMumWKn7liGyHCby.js b/effects/QMumWKn7liGyHCby.js new file mode 100644 index 0000000..a18be59 --- /dev/null +++ b/effects/QMumWKn7liGyHCby.js @@ -0,0 +1,11 @@ +let effects = this.item.effects.contents.filter(i => i.id != this.effect.id); + +effects.forEach(e => { + e.update({disabled : true}) +}) + +let e = effects.find(i => i.id == "02qHBz2ng62Xg1Zx"); + +e.update({disabled : false}); + +this.script.notification(`${e.name} (+2 Defence) Activated`) \ No newline at end of file diff --git a/effects/QQk2JRCB7NvfVbuv.js b/effects/QQk2JRCB7NvfVbuv.js new file mode 100644 index 0000000..4b2ab03 --- /dev/null +++ b/effects/QQk2JRCB7NvfVbuv.js @@ -0,0 +1 @@ +return !args.weapon || args.weapon?.system.isRanged || !this.effect.specifier \ No newline at end of file diff --git a/effects/Qz75uz3MiSoTyz46.js b/effects/Qz75uz3MiSoTyz46.js new file mode 100644 index 0000000..9f0fafd --- /dev/null +++ b/effects/Qz75uz3MiSoTyz46.js @@ -0,0 +1 @@ +this.actor.system.combat.resilience.invulnerable = false; \ No newline at end of file diff --git a/effects/R1eu0gVwClSnKCEQ.js b/effects/R1eu0gVwClSnKCEQ.js new file mode 100644 index 0000000..2c12e39 --- /dev/null +++ b/effects/R1eu0gVwClSnKCEQ.js @@ -0,0 +1 @@ +return ['weaponSkill', 'ballisticSkill', 'psychicMastery'].includes(args.skill) \ No newline at end of file diff --git a/effects/R4couNzoEwZ1KiUh.js b/effects/R4couNzoEwZ1KiUh.js new file mode 100644 index 0000000..a02a949 --- /dev/null +++ b/effects/R4couNzoEwZ1KiUh.js @@ -0,0 +1 @@ +args.fields.pool += this.effect.sourceTest.result.diceBonus || 6; \ No newline at end of file diff --git a/effects/R74wexFtorXmNAOb.js b/effects/R74wexFtorXmNAOb.js new file mode 100644 index 0000000..e4c6368 --- /dev/null +++ b/effects/R74wexFtorXmNAOb.js @@ -0,0 +1 @@ +return !args.options.determination \ No newline at end of file diff --git a/effects/RP5rqKe63nxmH7tb.js b/effects/RP5rqKe63nxmH7tb.js new file mode 100644 index 0000000..94725d3 --- /dev/null +++ b/effects/RP5rqKe63nxmH7tb.js @@ -0,0 +1 @@ +args.fields.pool += (this.effect.sourceActor.system.advances.rank) \ No newline at end of file diff --git a/effects/RVndp78fwAn8MGAw.js b/effects/RVndp78fwAn8MGAw.js new file mode 100644 index 0000000..d6e084b --- /dev/null +++ b/effects/RVndp78fwAn8MGAw.js @@ -0,0 +1 @@ +return args.options.resolve; \ No newline at end of file diff --git a/effects/RWfem3ObJwrC578W.js b/effects/RWfem3ObJwrC578W.js new file mode 100644 index 0000000..a96a9f6 --- /dev/null +++ b/effects/RWfem3ObJwrC578W.js @@ -0,0 +1 @@ +return args.weapon; \ No newline at end of file diff --git a/effects/RfRy3gtFt6iVhaYx.js b/effects/RfRy3gtFt6iVhaYx.js new file mode 100644 index 0000000..1ac6e9b --- /dev/null +++ b/effects/RfRy3gtFt6iVhaYx.js @@ -0,0 +1 @@ +return !args.weapon || !args.weapon.hasKeyword("TRANSONIC") \ No newline at end of file diff --git a/effects/Se8stX2b4hTXBIkk.js b/effects/Se8stX2b4hTXBIkk.js new file mode 100644 index 0000000..6e49e6c --- /dev/null +++ b/effects/Se8stX2b4hTXBIkk.js @@ -0,0 +1 @@ +return !args.options.resist?.includes("psychicPower"); \ No newline at end of file diff --git a/effects/T5V9ecdZwk63IdLu.js b/effects/T5V9ecdZwk63IdLu.js new file mode 100644 index 0000000..2c93a57 --- /dev/null +++ b/effects/T5V9ecdZwk63IdLu.js @@ -0,0 +1,7 @@ +// Bug: https://github.com/foundryvtt/foundryvtt/issues/7987 +// Solution currently is to divide by 2 +if (args.item.system.isRanged && args.item.system.isEquipped) +{ + args.item.system.salvo += this.actor.system.advances.rank / 2; + +} \ No newline at end of file diff --git a/effects/TDDOJIV2EYJrSKg2.js b/effects/TDDOJIV2EYJrSKg2.js new file mode 100644 index 0000000..ee57975 --- /dev/null +++ b/effects/TDDOJIV2EYJrSKg2.js @@ -0,0 +1,4 @@ +let shock = -1 * this.effect.sourceTest.result.shock; +let wounds = -1 * this.effect.sourceTest.result.wounds; +args.modifiers.shock.push({label : this.effect.name, value : shock}) +args.modifiers.wounds.push({label : this.effect.name, value : wounds}) \ No newline at end of file diff --git a/effects/TGUcXMJSG170xgvh.js b/effects/TGUcXMJSG170xgvh.js new file mode 100644 index 0000000..b28676b --- /dev/null +++ b/effects/TGUcXMJSG170xgvh.js @@ -0,0 +1,7 @@ +let bleeding = this.actor.hasCondition("bleeding"); + +if (bleeding) +{ + await bleeding.delete(); + this.script.notification("Removed Bleeding"); +} \ No newline at end of file diff --git a/effects/TJDM9SqHATGH3QQF.js b/effects/TJDM9SqHATGH3QQF.js new file mode 100644 index 0000000..b28596f --- /dev/null +++ b/effects/TJDM9SqHATGH3QQF.js @@ -0,0 +1,10 @@ +if (args.item.system.isEquipped && args.item.type == "weapon") + { + let sourceBlast = args.item._source.system.traits.list.find(i => i.name == "blast") + let blast = args.item.system.traits.list.find(i => i.name == "blast"); + if (sourceBlast && !blast.bombadier) + { + blast.rating = parseInt(blast.rating) + (this.actor.system.advances.rank * 2); + blast.bombadier = true; + } + } \ No newline at end of file diff --git a/effects/TUdLSiK62ZcpBJuX.js b/effects/TUdLSiK62ZcpBJuX.js new file mode 100644 index 0000000..b0f5313 --- /dev/null +++ b/effects/TUdLSiK62ZcpBJuX.js @@ -0,0 +1 @@ +args.options.resist?.includes("psychicPower") \ No newline at end of file diff --git a/effects/Tbav4WV9QmHYGgCG.js b/effects/Tbav4WV9QmHYGgCG.js new file mode 100644 index 0000000..0cf9c9a --- /dev/null +++ b/effects/Tbav4WV9QmHYGgCG.js @@ -0,0 +1,4 @@ +if (args.fields.level != "bound" || args.fields.level != "bound") +{ + args.fields.level = "bound"; +} \ No newline at end of file diff --git a/effects/TjWyU3g5jwGR91zC.js b/effects/TjWyU3g5jwGR91zC.js new file mode 100644 index 0000000..b3b178a --- /dev/null +++ b/effects/TjWyU3g5jwGR91zC.js @@ -0,0 +1 @@ +return args.skill == "stealth" \ No newline at end of file diff --git a/effects/TvI0MSH8mIn1aYGy.js b/effects/TvI0MSH8mIn1aYGy.js new file mode 100644 index 0000000..f4a2e29 --- /dev/null +++ b/effects/TvI0MSH8mIn1aYGy.js @@ -0,0 +1 @@ +return !args.fields.distance || args.fields.distance <= 12; \ No newline at end of file diff --git a/effects/U40htgNxfnoqrQ6u.js b/effects/U40htgNxfnoqrQ6u.js new file mode 100644 index 0000000..4d134b1 --- /dev/null +++ b/effects/U40htgNxfnoqrQ6u.js @@ -0,0 +1 @@ +return args.skill != "persuasion" \ No newline at end of file diff --git a/effects/UKqVvr36UwMajuDY.js b/effects/UKqVvr36UwMajuDY.js new file mode 100644 index 0000000..b1019b9 --- /dev/null +++ b/effects/UKqVvr36UwMajuDY.js @@ -0,0 +1 @@ +return args.skill == "survival" \ No newline at end of file diff --git a/effects/UL9ufbd1T1N6bckp.js b/effects/UL9ufbd1T1N6bckp.js new file mode 100644 index 0000000..4469521 --- /dev/null +++ b/effects/UL9ufbd1T1N6bckp.js @@ -0,0 +1,2 @@ +let tens = Math.floor(args.actor.system.mob.value/10)+1; +args.fields.difficulty -= tens; \ No newline at end of file diff --git a/effects/UMEI7y4VmIn85Yab.js b/effects/UMEI7y4VmIn85Yab.js new file mode 100644 index 0000000..5df4a8a --- /dev/null +++ b/effects/UMEI7y4VmIn85Yab.js @@ -0,0 +1 @@ +return args.fields.range == "long"; \ No newline at end of file diff --git a/effects/UUBcD07BreoSeGA0.js b/effects/UUBcD07BreoSeGA0.js new file mode 100644 index 0000000..399fcf0 --- /dev/null +++ b/effects/UUBcD07BreoSeGA0.js @@ -0,0 +1 @@ +this.actor.setupGenericTest("fear", {fields : {difficulty : 2 + this.effect.sourceActor.system.advances.rank * 2}}) \ No newline at end of file diff --git a/effects/UUv4HOaIDHY9P7xP.js b/effects/UUv4HOaIDHY9P7xP.js new file mode 100644 index 0000000..a543ae6 --- /dev/null +++ b/effects/UUv4HOaIDHY9P7xP.js @@ -0,0 +1 @@ +return !["strength", "agility"].includes(args.attribute) \ No newline at end of file diff --git a/effects/Uag1H6EegkuA9dNR.js b/effects/Uag1H6EegkuA9dNR.js new file mode 100644 index 0000000..e7b7519 --- /dev/null +++ b/effects/Uag1H6EegkuA9dNR.js @@ -0,0 +1 @@ +args.fields.ed.value += (2) \ No newline at end of file diff --git a/effects/UmHQnmKvPVS19IYN.js b/effects/UmHQnmKvPVS19IYN.js new file mode 100644 index 0000000..75e500c --- /dev/null +++ b/effects/UmHQnmKvPVS19IYN.js @@ -0,0 +1 @@ +args.fields.pool += 6; \ No newline at end of file diff --git a/effects/UnNdpT7GynKzEzis.js b/effects/UnNdpT7GynKzEzis.js new file mode 100644 index 0000000..851b334 --- /dev/null +++ b/effects/UnNdpT7GynKzEzis.js @@ -0,0 +1,6 @@ +// https://github.com/foundryvtt/foundryvtt/issues/7987 +if (args.item.type == "weapon" && args.item.system.isEquipped && args.item.traitList.sniper) +{ + let sniper = args.item.system.traits.list.find(i => i.name == "sniper"); + sniper.rating = Number(sniper.rating) + this.actor.system.advances?.rank/2 || 0 +} \ No newline at end of file diff --git a/effects/V4y7lOlGw0Gyl75Q.js b/effects/V4y7lOlGw0Gyl75Q.js new file mode 100644 index 0000000..711595d --- /dev/null +++ b/effects/V4y7lOlGw0Gyl75Q.js @@ -0,0 +1 @@ +return args.skill == "weaponSkill" \ No newline at end of file diff --git a/effects/VCF9VmjA1Js7z6qj.js b/effects/VCF9VmjA1Js7z6qj.js new file mode 100644 index 0000000..edc91bc --- /dev/null +++ b/effects/VCF9VmjA1Js7z6qj.js @@ -0,0 +1 @@ +args.fields.difficulty -= 4 \ No newline at end of file diff --git a/effects/VD1UxKrbZM0n8Msc.js b/effects/VD1UxKrbZM0n8Msc.js new file mode 100644 index 0000000..9ac7a63 --- /dev/null +++ b/effects/VD1UxKrbZM0n8Msc.js @@ -0,0 +1 @@ +return args.type == "corruption" \ No newline at end of file diff --git a/effects/VFd8eBVXny9ZsSCN.js b/effects/VFd8eBVXny9ZsSCN.js new file mode 100644 index 0000000..267fce6 --- /dev/null +++ b/effects/VFd8eBVXny9ZsSCN.js @@ -0,0 +1 @@ +args.fields.pool += 2 \ No newline at end of file diff --git a/effects/VVzWEPiCwTeq6dZw.js b/effects/VVzWEPiCwTeq6dZw.js new file mode 100644 index 0000000..ca1070b --- /dev/null +++ b/effects/VVzWEPiCwTeq6dZw.js @@ -0,0 +1 @@ +args.fields.pool += this.effect.sourceActor.system.attributes.willpower.total \ No newline at end of file diff --git a/effects/VX7h2ugVigZV2QTX.js b/effects/VX7h2ugVigZV2QTX.js new file mode 100644 index 0000000..865036b --- /dev/null +++ b/effects/VX7h2ugVigZV2QTX.js @@ -0,0 +1,9 @@ +let myLeadership = this.actor.system.skills.leadership.total; +let sourceLeadership = this.effect.sourceActor.skills.leadership.total; + +if ( myLeadership < sourceLeadership ) +{ + this.actor.system.skills.leadership.total = sourceLeadership; +} + +this.script.notification(`Changed ${this.actor.name} Leadership to 12`); \ No newline at end of file diff --git a/effects/VZzAvkn1TvQwXahb.js b/effects/VZzAvkn1TvQwXahb.js new file mode 100644 index 0000000..6f3287f --- /dev/null +++ b/effects/VZzAvkn1TvQwXahb.js @@ -0,0 +1 @@ +return args.skill != "survival" \ No newline at end of file diff --git a/effects/VfTxROOPZoZJ3aik.js b/effects/VfTxROOPZoZJ3aik.js new file mode 100644 index 0000000..e20b144 --- /dev/null +++ b/effects/VfTxROOPZoZJ3aik.js @@ -0,0 +1,14 @@ +let attributes = ItemDialog.objectToArray(game.wng.config.attributes, this.effect.img); + +let lowest = Math.min(...attributes.map(i => this.actor.system.attributes[i.id].total)) + +let choices = attributes.filter(i => this.actor.system.attributes[i.id].total == lowest); + +let attribute = await ItemDialog.create(choices, 1, {title : this.effect.name, text : "Choose Attribute Bonus"}) + +if (attribute[0]) +{ + let changes = this.effect.changes.concat([]); + changes[0].key = `system.attributes.${attribute[0].id}.bonus` + this.effect.updateSource({changes}); +} \ No newline at end of file diff --git a/effects/W0fSPg1JfVFwd2jX.js b/effects/W0fSPg1JfVFwd2jX.js new file mode 100644 index 0000000..a483c36 --- /dev/null +++ b/effects/W0fSPg1JfVFwd2jX.js @@ -0,0 +1 @@ +return args.weapon.isMelee \ No newline at end of file diff --git a/effects/WwvEgSjw3K6dNf8u.js b/effects/WwvEgSjw3K6dNf8u.js new file mode 100644 index 0000000..0b3be32 --- /dev/null +++ b/effects/WwvEgSjw3K6dNf8u.js @@ -0,0 +1 @@ +return game.messages.contents(args.options.message)?.item?.type == "psychicPower" \ No newline at end of file diff --git a/effects/X5F5gjW4i2zoHgyG.js b/effects/X5F5gjW4i2zoHgyG.js new file mode 100644 index 0000000..864e847 --- /dev/null +++ b/effects/X5F5gjW4i2zoHgyG.js @@ -0,0 +1,6 @@ +if (args.options.resolve) +{ + args.abort = true; + this.script.notification("Does not need to make Resolve Tests"); +} +else return true \ No newline at end of file diff --git a/effects/XBViGGApHecicjqc.js b/effects/XBViGGApHecicjqc.js new file mode 100644 index 0000000..1ff4286 --- /dev/null +++ b/effects/XBViGGApHecicjqc.js @@ -0,0 +1 @@ +return ["persuasion", "awareness"].includes(this.skill) \ No newline at end of file diff --git a/effects/XF8GhO2pNoRH0Vlm.js b/effects/XF8GhO2pNoRH0Vlm.js new file mode 100644 index 0000000..981e594 --- /dev/null +++ b/effects/XF8GhO2pNoRH0Vlm.js @@ -0,0 +1 @@ +return args.attribute != "fellowship" \ No newline at end of file diff --git a/effects/Xce2Tubcb1lqXA5Q.js b/effects/Xce2Tubcb1lqXA5Q.js new file mode 100644 index 0000000..f522777 --- /dev/null +++ b/effects/Xce2Tubcb1lqXA5Q.js @@ -0,0 +1 @@ +return !["toughness", "willpower"].includes(args.attribute); \ No newline at end of file diff --git a/effects/Y3IDZNg7GwTNiCri.js b/effects/Y3IDZNg7GwTNiCri.js new file mode 100644 index 0000000..b3e78bf --- /dev/null +++ b/effects/Y3IDZNg7GwTNiCri.js @@ -0,0 +1,10 @@ +if (args.item.system.isEquipped && args.item.type == "weapon") + { + let sourceHeavy = args.item._source.system.traits.list.find(i => i.name == "heavy") + let heavy = args.item.system.traits.list.find(i => i.name == "heavy"); + if (sourceHeavy && !heavy.tough) + { + heavy.rating = Math.floor(parseInt(heavy.rating) / 2); + heavy.tough = true; + } + } \ No newline at end of file diff --git a/effects/Y8kmllqudMGUeRjU.js b/effects/Y8kmllqudMGUeRjU.js new file mode 100644 index 0000000..abdfb01 --- /dev/null +++ b/effects/Y8kmllqudMGUeRjU.js @@ -0,0 +1 @@ +return !["persuasion", "awareness"].includes(this.skill) \ No newline at end of file diff --git a/effects/YGRqoOFiDWUAM80D.js b/effects/YGRqoOFiDWUAM80D.js new file mode 100644 index 0000000..dc0650f --- /dev/null +++ b/effects/YGRqoOFiDWUAM80D.js @@ -0,0 +1 @@ +return !args.actor.hasKeyword("T’AU") || !args.weapon || !args.weapon.isRanged; \ No newline at end of file diff --git a/effects/YGlUiKsaxuw8X3NR.js b/effects/YGlUiKsaxuw8X3NR.js new file mode 100644 index 0000000..bbbaf46 --- /dev/null +++ b/effects/YGlUiKsaxuw8X3NR.js @@ -0,0 +1,23 @@ +if (!this.item.specifier) + { + let skills = ["awareness", + "insight", + "investigation", + "scholar"].map(i => { + return {id : i, name : systemConfig().skills[i], img : this.effect.img} + }).filter(i => this.actor.system.skills[i.id].rating >= 3); + + if (skills.length == 0) + { + this.script.notification(`No Skills match the requirement`, "error") + return false; + } + + let choice = await ItemDialog.create(skills, 1, {title : this.effect.name, text : "Select Skill"}) + + if (choice[0]) + { + this.item.updateSource({"system.test" : {type : "skill", specification : choice[0].id, dn: 3}}) + this.effect.updateSource({name : this.effect.setSpecifier(choice[0].name), "flags.wrath-and-glory.skill" : choice[0].id}); + } +} \ No newline at end of file diff --git a/effects/YIqAWv3eiLNZ5Pr0.js b/effects/YIqAWv3eiLNZ5Pr0.js new file mode 100644 index 0000000..0bd19cf --- /dev/null +++ b/effects/YIqAWv3eiLNZ5Pr0.js @@ -0,0 +1 @@ +return !(args.actor?.statuses.has("all-out-attack")); \ No newline at end of file diff --git a/effects/YPWlXm6u2WButPG4.js b/effects/YPWlXm6u2WButPG4.js new file mode 100644 index 0000000..ca441b5 --- /dev/null +++ b/effects/YPWlXm6u2WButPG4.js @@ -0,0 +1 @@ +return !args.options.multi || !args.weapon || !args.weapon.name === "Enmitic Disintegrator Pistol"; \ No newline at end of file diff --git a/effects/YnGzWisVy8f5LZst.js b/effects/YnGzWisVy8f5LZst.js new file mode 100644 index 0000000..d70e5e0 --- /dev/null +++ b/effects/YnGzWisVy8f5LZst.js @@ -0,0 +1 @@ +this.effect.update({disabled:true}); \ No newline at end of file diff --git a/effects/YnndQ9I3Zi56twmp.js b/effects/YnndQ9I3Zi56twmp.js new file mode 100644 index 0000000..2302523 --- /dev/null +++ b/effects/YnndQ9I3Zi56twmp.js @@ -0,0 +1 @@ +return !args.options.resolve \ No newline at end of file diff --git a/effects/Yr0sfyumYCoNvzxZ.js b/effects/Yr0sfyumYCoNvzxZ.js new file mode 100644 index 0000000..66df2c3 --- /dev/null +++ b/effects/Yr0sfyumYCoNvzxZ.js @@ -0,0 +1,2 @@ +// https://github.com/foundryvtt/foundryvtt/issues/7987 +this.item.system.damage.base++; \ No newline at end of file diff --git a/effects/YscjjvV6TPuV9o2J.js b/effects/YscjjvV6TPuV9o2J.js new file mode 100644 index 0000000..b82d3b2 --- /dev/null +++ b/effects/YscjjvV6TPuV9o2J.js @@ -0,0 +1,8 @@ +let attribute = await ItemDialog.create(ItemDialog.objectToArray(game.wng.config.attributes, this.effect.img), 1, {title : this.effect.name, text : "Choose Attribute Bonus"}) + +if (attribute[0]) +{ + let changes = this.effect.changes.concat([]); + changes[0].key = `system.attributes.${attribute[0].id}.bonus` + this.effect.updateSource({changes}); +} \ No newline at end of file diff --git a/effects/YvhKDakdl9g5OLv7.js b/effects/YvhKDakdl9g5OLv7.js new file mode 100644 index 0000000..052f289 --- /dev/null +++ b/effects/YvhKDakdl9g5OLv7.js @@ -0,0 +1,2 @@ +args.fields.damageDice.values.threes += 1; +args.fields.damageDice.values.fives += 1; \ No newline at end of file diff --git a/effects/ZYHZu79DqOc6La3G.js b/effects/ZYHZu79DqOc6La3G.js new file mode 100644 index 0000000..95e810f --- /dev/null +++ b/effects/ZYHZu79DqOc6La3G.js @@ -0,0 +1 @@ +return args.fields.range !== "short"; \ No newline at end of file diff --git a/effects/ZbLoaXOcJ1yMQo5h.js b/effects/ZbLoaXOcJ1yMQo5h.js new file mode 100644 index 0000000..f5dfa81 --- /dev/null +++ b/effects/ZbLoaXOcJ1yMQo5h.js @@ -0,0 +1 @@ +return !["fellowship", "willpower"].includes(args.attribute) \ No newline at end of file diff --git a/effects/ZcwvQDFMK104NTm5.js b/effects/ZcwvQDFMK104NTm5.js new file mode 100644 index 0000000..5e5684e --- /dev/null +++ b/effects/ZcwvQDFMK104NTm5.js @@ -0,0 +1 @@ +return !args.options.fear \ No newline at end of file diff --git a/effects/ZpL6lIJAuwgECrSe.js b/effects/ZpL6lIJAuwgECrSe.js new file mode 100644 index 0000000..4d2dc06 --- /dev/null +++ b/effects/ZpL6lIJAuwgECrSe.js @@ -0,0 +1 @@ +args.fields.difficulty += 1; \ No newline at end of file diff --git a/effects/ZyZcLrPbd5oVwHA5.js b/effects/ZyZcLrPbd5oVwHA5.js new file mode 100644 index 0000000..83a0729 --- /dev/null +++ b/effects/ZyZcLrPbd5oVwHA5.js @@ -0,0 +1 @@ +return args.target.statuses.has("wounded"); \ No newline at end of file diff --git a/effects/aNDK8GD9IdkGnfDp.js b/effects/aNDK8GD9IdkGnfDp.js new file mode 100644 index 0000000..c02e24f --- /dev/null +++ b/effects/aNDK8GD9IdkGnfDp.js @@ -0,0 +1,2 @@ +await this.actor.removeCondition("hindered"); +await this.actor.removeCondition("staggered"); \ No newline at end of file diff --git a/effects/aWRkB6WqmN7V7qfd.js b/effects/aWRkB6WqmN7V7qfd.js new file mode 100644 index 0000000..1241790 --- /dev/null +++ b/effects/aWRkB6WqmN7V7qfd.js @@ -0,0 +1 @@ +return args.weapon.hasKeyword("PRIMARIS") \ No newline at end of file diff --git a/effects/b6K8xiDfpDlxb96b.js b/effects/b6K8xiDfpDlxb96b.js new file mode 100644 index 0000000..a468568 --- /dev/null +++ b/effects/b6K8xiDfpDlxb96b.js @@ -0,0 +1,3 @@ +let shock = this.effect.sourceActor.system.advances.rank + this.effect.sourceTest.result.success; + +this.actor.applyHealing({shock}, {messageData : this.script.getChatData()}) \ No newline at end of file diff --git a/effects/bEBclpmgLB2SmDVx.js b/effects/bEBclpmgLB2SmDVx.js new file mode 100644 index 0000000..2c3e80e --- /dev/null +++ b/effects/bEBclpmgLB2SmDVx.js @@ -0,0 +1,3 @@ +let report = await this.actor.applyDamage(0, {shock : this.effect.sourceTest.result.damage.other.shock}); + +this.script.message(`Received ${report.shock} Shock`) \ No newline at end of file diff --git a/effects/bO7RVQxWDsfuUohf.js b/effects/bO7RVQxWDsfuUohf.js new file mode 100644 index 0000000..2ead2d3 --- /dev/null +++ b/effects/bO7RVQxWDsfuUohf.js @@ -0,0 +1 @@ +args.fields.pool++; \ No newline at end of file diff --git a/effects/bbOJCIdPMEDS495T.js b/effects/bbOJCIdPMEDS495T.js new file mode 100644 index 0000000..51536fc --- /dev/null +++ b/effects/bbOJCIdPMEDS495T.js @@ -0,0 +1 @@ +args.fields.pool += (args.actor.system.advances.rank) * 2 \ No newline at end of file diff --git a/effects/bcKrdTt3aB5s82eZ.js b/effects/bcKrdTt3aB5s82eZ.js new file mode 100644 index 0000000..2a92563 --- /dev/null +++ b/effects/bcKrdTt3aB5s82eZ.js @@ -0,0 +1 @@ +return true \ No newline at end of file diff --git a/effects/bcdbbWzqxibNRaRP.js b/effects/bcdbbWzqxibNRaRP.js new file mode 100644 index 0000000..606127f --- /dev/null +++ b/effects/bcdbbWzqxibNRaRP.js @@ -0,0 +1 @@ +args.fields.pool += 2; \ No newline at end of file diff --git a/effects/biqrW5UvHRnioFQ0.js b/effects/biqrW5UvHRnioFQ0.js new file mode 100644 index 0000000..23ddf0f --- /dev/null +++ b/effects/biqrW5UvHRnioFQ0.js @@ -0,0 +1 @@ +args.fields.difficulty += (6) \ No newline at end of file diff --git a/effects/boHWspNKuKRlaOOL.js b/effects/boHWspNKuKRlaOOL.js new file mode 100644 index 0000000..ee1c983 --- /dev/null +++ b/effects/boHWspNKuKRlaOOL.js @@ -0,0 +1 @@ +args.fields.difficulty += this.actor.system.advances.rank; \ No newline at end of file diff --git a/effects/bx9Bt3I9YwXxtjEN.js b/effects/bx9Bt3I9YwXxtjEN.js new file mode 100644 index 0000000..c9993f0 --- /dev/null +++ b/effects/bx9Bt3I9YwXxtjEN.js @@ -0,0 +1 @@ +return !(args.options.conviction || args.options.resolve || args.weapon); \ No newline at end of file diff --git a/effects/cIGjQHAk6i6ApYAT.js b/effects/cIGjQHAk6i6ApYAT.js new file mode 100644 index 0000000..9b1efd4 --- /dev/null +++ b/effects/cIGjQHAk6i6ApYAT.js @@ -0,0 +1 @@ +args.fields.difficulty += 3; \ No newline at end of file diff --git a/effects/cUIaXaZ59uSkucyk.js b/effects/cUIaXaZ59uSkucyk.js new file mode 100644 index 0000000..cdd180a --- /dev/null +++ b/effects/cUIaXaZ59uSkucyk.js @@ -0,0 +1 @@ +this.actor.system.combat.defence.bonus -= parseInt(this.effect.specifier) \ No newline at end of file diff --git a/effects/cbpC5gCRrKi2riOK.js b/effects/cbpC5gCRrKi2riOK.js new file mode 100644 index 0000000..626fb2c --- /dev/null +++ b/effects/cbpC5gCRrKi2riOK.js @@ -0,0 +1,6 @@ +let tens = Math.floor(this.actor.system.mob.value/10)+1; + +let roll = await new Roll(`${tens}d3`).roll(); + +let report = await this.actor.applyDamage(0, {shock: roll.total}); +this.script.message(`Received ${report.shock} Shock and ${report.wounds} Wounds`); \ No newline at end of file diff --git a/effects/ciIOQivfHgjLInjy.js b/effects/ciIOQivfHgjLInjy.js new file mode 100644 index 0000000..6ae9884 --- /dev/null +++ b/effects/ciIOQivfHgjLInjy.js @@ -0,0 +1 @@ +return !(args.data.skill === 'intimidation' || args.data.skill === 'leadership' || args.data.skill === 'weaponSkill') \ No newline at end of file diff --git a/effects/cidgP9Os1NMnvJe7.js b/effects/cidgP9Os1NMnvJe7.js new file mode 100644 index 0000000..3312cbd --- /dev/null +++ b/effects/cidgP9Os1NMnvJe7.js @@ -0,0 +1 @@ +return !args.weapon || args.weapon.isMelee \ No newline at end of file diff --git a/effects/clYKgwwkDUQ0FSix.js b/effects/clYKgwwkDUQ0FSix.js new file mode 100644 index 0000000..563db29 --- /dev/null +++ b/effects/clYKgwwkDUQ0FSix.js @@ -0,0 +1 @@ +return !args.weapon || !args.weapon.traitList.melta \ No newline at end of file diff --git a/effects/cxyuhg9WV7F6Lo7P.js b/effects/cxyuhg9WV7F6Lo7P.js new file mode 100644 index 0000000..b54fc75 --- /dev/null +++ b/effects/cxyuhg9WV7F6Lo7P.js @@ -0,0 +1 @@ +args.fields.pool += args.target.system.advances.tier; \ No newline at end of file diff --git a/effects/d5tnbLn5zQfV1vF4.js b/effects/d5tnbLn5zQfV1vF4.js new file mode 100644 index 0000000..6364007 --- /dev/null +++ b/effects/d5tnbLn5zQfV1vF4.js @@ -0,0 +1,8 @@ +let weapons = this.actor.itemTypes.weapon; + +let choice = await ItemDialog.create(weapons, 1, {title : this.effect.name}); + +if (choice[0]) +{ + this.effect.update({name : this.effect.baseName + ` [${choice[0].name}]`, "flags.wrath-and-glory.trademarkId" : choice[0].id}); +} \ No newline at end of file diff --git a/effects/dKN6CmYlcO09U6Y7.js b/effects/dKN6CmYlcO09U6Y7.js new file mode 100644 index 0000000..59a2110 --- /dev/null +++ b/effects/dKN6CmYlcO09U6Y7.js @@ -0,0 +1,19 @@ +let item = { + name : "Conjured Flame", + img : this.effect.img, + type : "weapon", + system : { + damage : { + base : 8, + ed : { + base: this.effect.sourceTest.result.damage.ed.value + } + }, + traits : [{name : "inflict", rating : "On Fire"}], + equipped : true + } +} + +this.actor.createEmbeddedDocuments("Item", [item], {fromEffect: this.effect.id}) + +this.script.notification("Weapon Created"); \ No newline at end of file diff --git a/effects/dQyH83wQOByoYt2s.js b/effects/dQyH83wQOByoYt2s.js new file mode 100644 index 0000000..8e5158c --- /dev/null +++ b/effects/dQyH83wQOByoYt2s.js @@ -0,0 +1 @@ +return args.skill != "intimidation" \ No newline at end of file diff --git a/effects/dkcoIue3tbSIzlgV.js b/effects/dkcoIue3tbSIzlgV.js new file mode 100644 index 0000000..ddf4640 --- /dev/null +++ b/effects/dkcoIue3tbSIzlgV.js @@ -0,0 +1 @@ +return args.type == "fear" || args.type == "terror" \ No newline at end of file diff --git a/effects/dpBhmA2SdRssy2fV.js b/effects/dpBhmA2SdRssy2fV.js new file mode 100644 index 0000000..c759738 --- /dev/null +++ b/effects/dpBhmA2SdRssy2fV.js @@ -0,0 +1 @@ +return !args.actor.hasKeyword("GENESTEALER CULT"); \ No newline at end of file diff --git a/effects/dq3xsmx7ETI0RgVp.js b/effects/dq3xsmx7ETI0RgVp.js new file mode 100644 index 0000000..7662c73 --- /dev/null +++ b/effects/dq3xsmx7ETI0RgVp.js @@ -0,0 +1 @@ +return !["medicae", "survival"].includes(args.skill) \ No newline at end of file diff --git a/effects/e7Wh5Ni04a11zdxU.js b/effects/e7Wh5Ni04a11zdxU.js new file mode 100644 index 0000000..7a61e46 --- /dev/null +++ b/effects/e7Wh5Ni04a11zdxU.js @@ -0,0 +1 @@ +args.actor.addCondition("frenzied"); \ No newline at end of file diff --git a/effects/e8GHDZcASdS27Moy.js b/effects/e8GHDZcASdS27Moy.js new file mode 100644 index 0000000..2b9b496 --- /dev/null +++ b/effects/e8GHDZcASdS27Moy.js @@ -0,0 +1 @@ +return args.skill == "awareness" \ No newline at end of file diff --git a/effects/eG9sXowL54uhuTNy.js b/effects/eG9sXowL54uhuTNy.js new file mode 100644 index 0000000..ce2dd78 --- /dev/null +++ b/effects/eG9sXowL54uhuTNy.js @@ -0,0 +1,2 @@ +let report = await this.actor.applyDamage(0, {shock: 1}); +this.script.message(`Received ${report.shock} Shock`); \ No newline at end of file diff --git a/effects/eJHmdYEuhaW2NF1W.js b/effects/eJHmdYEuhaW2NF1W.js new file mode 100644 index 0000000..f3c0b09 --- /dev/null +++ b/effects/eJHmdYEuhaW2NF1W.js @@ -0,0 +1,4 @@ +if (args.wounds || args.mortal) +{ + this.item.effects.getName("Inured to Suffering").update({disabled: false}); +} \ No newline at end of file diff --git a/effects/emLoEakrPK482SEL.js b/effects/emLoEakrPK482SEL.js new file mode 100644 index 0000000..b304698 --- /dev/null +++ b/effects/emLoEakrPK482SEL.js @@ -0,0 +1 @@ +return args.options.determination || args.skill == "tech" \ No newline at end of file diff --git a/effects/esqNRqLBFcUG672G.js b/effects/esqNRqLBFcUG672G.js new file mode 100644 index 0000000..2879270 --- /dev/null +++ b/effects/esqNRqLBFcUG672G.js @@ -0,0 +1 @@ +this.actor.setupGenericTest("corruption", {fields: {difficulty: 5}}) \ No newline at end of file diff --git a/effects/f0I4agsEbXn6mHqf.js b/effects/f0I4agsEbXn6mHqf.js new file mode 100644 index 0000000..067c43e --- /dev/null +++ b/effects/f0I4agsEbXn6mHqf.js @@ -0,0 +1 @@ +return !args.weapon || !args.weapon.isRanged; \ No newline at end of file diff --git a/effects/f8HCFia4qPsSeOCY.js b/effects/f8HCFia4qPsSeOCY.js new file mode 100644 index 0000000..196b2f6 --- /dev/null +++ b/effects/f8HCFia4qPsSeOCY.js @@ -0,0 +1,24 @@ +if (this.item.specifier == "Skill") + { + let skills = ["awareness", + "cunning", + "deception", + "insight", + "persuasion", + "psychicMastery", + "stealth"].map(i => { + return {id : i, name : systemConfig().skills[i], img : this.effect.img} + }); + let choice = await ItemDialog.create(skills, 1, {title : this.effect.name, text : "Select Skill"}) + if (choice[0]) + { + if (this.actor.system.skills[choice[0].id].rating < 1) + { + this.script.notification(`${choice[0].name} Rating not high enough.`, "error") + return false; + } + + this.item.updateSource({name : this.item.name.replace("Skill", choice[0].name), "system.test" : {type : "skill", specification : choice[0].id}}) + this.effect.updateSource({name : `${this.item.name}`}); + } +} \ No newline at end of file diff --git a/effects/fUpCX9Lnd15Z0jUB.js b/effects/fUpCX9Lnd15Z0jUB.js new file mode 100644 index 0000000..1d6018c --- /dev/null +++ b/effects/fUpCX9Lnd15Z0jUB.js @@ -0,0 +1 @@ +args.fields.difficulty += 2; \ No newline at end of file diff --git a/effects/fXsyrJNCwm9DOsx9.js b/effects/fXsyrJNCwm9DOsx9.js new file mode 100644 index 0000000..bd6ad69 --- /dev/null +++ b/effects/fXsyrJNCwm9DOsx9.js @@ -0,0 +1 @@ +return !args.weapon || args.weapon?.isMelee \ No newline at end of file diff --git a/effects/fZzIVhGLtV3RA31t.js b/effects/fZzIVhGLtV3RA31t.js new file mode 100644 index 0000000..531fde2 --- /dev/null +++ b/effects/fZzIVhGLtV3RA31t.js @@ -0,0 +1 @@ +return !["deception", "cunning", "insight", "intimidation", "investigation", "leadership", "persuasion", "pilot", "tech"].includes(args.skill) \ No newline at end of file diff --git a/effects/faj4Tr2pW6zS5CRt.js b/effects/faj4Tr2pW6zS5CRt.js new file mode 100644 index 0000000..4c138f8 --- /dev/null +++ b/effects/faj4Tr2pW6zS5CRt.js @@ -0,0 +1 @@ +return !args.weapon || !args.weapon.isMelee || !args.target.system.advances?.tier; \ No newline at end of file diff --git a/effects/fdS0NlIwU6x2rhda.js b/effects/fdS0NlIwU6x2rhda.js new file mode 100644 index 0000000..f6a7949 --- /dev/null +++ b/effects/fdS0NlIwU6x2rhda.js @@ -0,0 +1 @@ +return !args.weapon; \ No newline at end of file diff --git a/effects/feoIt14PYzLJm1vT.js b/effects/feoIt14PYzLJm1vT.js new file mode 100644 index 0000000..24f96ed --- /dev/null +++ b/effects/feoIt14PYzLJm1vT.js @@ -0,0 +1,3 @@ +this.actor.update({"system.corruption.current" : this.actor.system.corruption.current + 2}); + +this.script.notification("Added +2 Corruption"); \ No newline at end of file diff --git a/effects/g4vy06YF8IUFWbl4.js b/effects/g4vy06YF8IUFWbl4.js new file mode 100644 index 0000000..394ad61 --- /dev/null +++ b/effects/g4vy06YF8IUFWbl4.js @@ -0,0 +1,7 @@ +if (this.effect.sourceActor?.uuid == this.actor.uuid) +{ + if (this.effect.flags.round + 1 < game.combat.round) + { + this.effect.delete(); + } +} \ No newline at end of file diff --git a/effects/g6U46b1iRvU2GxZu.js b/effects/g6U46b1iRvU2GxZu.js new file mode 100644 index 0000000..20d187e --- /dev/null +++ b/effects/g6U46b1iRvU2GxZu.js @@ -0,0 +1 @@ +return args.weapon?.system.isMelee; \ No newline at end of file diff --git a/effects/gE6rYsCMIGx9uqej.js b/effects/gE6rYsCMIGx9uqej.js new file mode 100644 index 0000000..358dc66 --- /dev/null +++ b/effects/gE6rYsCMIGx9uqej.js @@ -0,0 +1 @@ +return !args.weapon || !args.options.multi || !args.weapon || !args.weapon.isMelee; \ No newline at end of file diff --git a/effects/gVMLKdINaJJR5wcF.js b/effects/gVMLKdINaJJR5wcF.js new file mode 100644 index 0000000..d9991e1 --- /dev/null +++ b/effects/gVMLKdINaJJR5wcF.js @@ -0,0 +1 @@ +return !args.weapon || (game.combat && game.combat.round >= 2) \ No newline at end of file diff --git a/effects/gdX6k8VbLy89KTLH.js b/effects/gdX6k8VbLy89KTLH.js new file mode 100644 index 0000000..60b6193 --- /dev/null +++ b/effects/gdX6k8VbLy89KTLH.js @@ -0,0 +1 @@ +args.fields.difficulty += (args.actor.system.advances.rank * -2) \ No newline at end of file diff --git a/effects/gfVWHScavZx37MZG.js b/effects/gfVWHScavZx37MZG.js new file mode 100644 index 0000000..df3a0a3 --- /dev/null +++ b/effects/gfVWHScavZx37MZG.js @@ -0,0 +1 @@ +return !["cunning", "awareness", "insight", "survival"].includes(args.skill); \ No newline at end of file diff --git a/effects/groMMm7RpgZ9xZoZ.js b/effects/groMMm7RpgZ9xZoZ.js new file mode 100644 index 0000000..6045fc0 --- /dev/null +++ b/effects/groMMm7RpgZ9xZoZ.js @@ -0,0 +1 @@ +return !args.actor.statuses.has("wounded") \ No newline at end of file diff --git a/effects/gy3QCEOr9LvlCAmi.js b/effects/gy3QCEOr9LvlCAmi.js new file mode 100644 index 0000000..4bed2b2 --- /dev/null +++ b/effects/gy3QCEOr9LvlCAmi.js @@ -0,0 +1 @@ +return !args.power; \ No newline at end of file diff --git a/effects/gyxdbmv7FzoaojFc.js b/effects/gyxdbmv7FzoaojFc.js new file mode 100644 index 0000000..04b87de --- /dev/null +++ b/effects/gyxdbmv7FzoaojFc.js @@ -0,0 +1,2 @@ +await this.actor.addCondition("hindered", {[game.system.id]:{value : 2}}); +await this.actor.addCondition("staggered"); \ No newline at end of file diff --git a/effects/gztDZA9KRVN9CQrj.js b/effects/gztDZA9KRVN9CQrj.js new file mode 100644 index 0000000..7f30ec7 --- /dev/null +++ b/effects/gztDZA9KRVN9CQrj.js @@ -0,0 +1,7 @@ +if (this.effect.sourceTest.result.brutal && args.item.system.isEquipped) +{ + if (!args.item._source.system.traits.list.find(i => i.name == "brutal")) + { + args.item.system.traits.list.push({name : "brutal"}); + } +} \ No newline at end of file diff --git a/effects/hCdc6hojBo66Z36H.js b/effects/hCdc6hojBo66Z36H.js new file mode 100644 index 0000000..6369f27 --- /dev/null +++ b/effects/hCdc6hojBo66Z36H.js @@ -0,0 +1,31 @@ +let condition = this.actor.hasCondition("blinded"); + +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Blinded"); +} + +condition = this.actor.hasCondition("fear"); + +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Fear"); +} + +condition = this.actor.hasCondition("poisoned"); + +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Poisoned"); +} + +condition = this.actor.hasCondition("terror"); + +if (condition) +{ + await condition.delete(); + this.script.notification("Removed Terror"); +} \ No newline at end of file diff --git a/effects/hP5AxqFBRSCq2mX4.js b/effects/hP5AxqFBRSCq2mX4.js new file mode 100644 index 0000000..51481ab --- /dev/null +++ b/effects/hP5AxqFBRSCq2mX4.js @@ -0,0 +1,7 @@ +let staggered = this.actor.hasCondition("staggered"); + +if (staggered) +{ + await staggered.delete(); + this.script.notification("Removed Staggered"); +} \ No newline at end of file diff --git a/effects/hbQOSfNRBLjzxruD.js b/effects/hbQOSfNRBLjzxruD.js new file mode 100644 index 0000000..1038f11 --- /dev/null +++ b/effects/hbQOSfNRBLjzxruD.js @@ -0,0 +1 @@ +return args.data.skill !== 'tech'; \ No newline at end of file diff --git a/effects/hd7u8cpFQrwFfL9W.js b/effects/hd7u8cpFQrwFfL9W.js new file mode 100644 index 0000000..ff22abf --- /dev/null +++ b/effects/hd7u8cpFQrwFfL9W.js @@ -0,0 +1,7 @@ +if (!args.item.isRanged) + return; + +if (!args.item._source.system.traits.list.find(i => i.name == "inflict")) +{ + args.item.system.traits.list.push({name : "inflict", rating:"Vulnerable"}); +} \ No newline at end of file diff --git a/effects/i3I9MGTg7KK7Oozg.js b/effects/i3I9MGTg7KK7Oozg.js new file mode 100644 index 0000000..b80c391 --- /dev/null +++ b/effects/i3I9MGTg7KK7Oozg.js @@ -0,0 +1,2 @@ +let tens = Math.floor(args.actor.system.mob.value/10)+1; +this.script.message(`Add ${tens} icons to result`); \ No newline at end of file diff --git a/effects/i5yUEQiCIo2NKTwR.js b/effects/i5yUEQiCIo2NKTwR.js new file mode 100644 index 0000000..7d5eff3 --- /dev/null +++ b/effects/i5yUEQiCIo2NKTwR.js @@ -0,0 +1,4 @@ +let shock = Math.ceil(CONFIG.Dice.randomUniform() * 3); + +let report = await this.actor.applyDamage(0, {shock}); +this.script.message(`Received ${report.shock} Shock`); \ No newline at end of file diff --git a/effects/iD1bb8lFykvNCIk4.js b/effects/iD1bb8lFykvNCIk4.js new file mode 100644 index 0000000..315d50e --- /dev/null +++ b/effects/iD1bb8lFykvNCIk4.js @@ -0,0 +1,26 @@ +let items = ["Compendium.wng-core.items.Item.JVz6FoVEc7p1DJUj", "Compendium.wng-core.items.Item.n0bj5UJjgglDzFcB"]; + +let minor = await Promise.all(["Compendium.wng-core.items.KlfSjb1rKKdlYFhc", +"Compendium.wng-core.items.jbIQTvBy7CwO2h5z", +"Compendium.wng-core.items.9AnDpmrRMN5HuerR", +"Compendium.wng-core.items.hR56lZzY4JEW2JPC", +"Compendium.wng-core.items.oIHSFm0K5bAMjIvi", +"Compendium.wng-core.items.osDMPEt7SXTRS7bS", +"Compendium.wng-core.items.3FHZYIJhjgW9IIWB", +"Compendium.wng-core.items.49mSzarl5rOSp2Cb", +"Compendium.wng-core.items.I3705lKAu41owyp3", +"Compendium.wng-core.items.sNtFVD9vHeZmrRO4", +"Compendium.wng-core.items.cb8NYMnLIkt8dp4j", +"Compendium.wng-core.items.pITmVVUIbdcsigzp", +"Compendium.wng-core.items.XaPBgatbPT7tqArI", +"Compendium.wng-core.items.rLmWz3gH7TBmf5IH"].map(fromUuid)) + +let choice = await ItemDialog.create(minor, 1, {title : this.effect.name, text : "Choose 1 Minor Psychic Power"}); + +if (choice[0]) +{ + items.push(choice[0].uuid); +} + + +this.actor.createEmbeddedDocuments("Item", (await Promise.all(items.map(fromUuid))), {fromEffect: this.effect.id}) \ No newline at end of file diff --git a/effects/iU6ad2a8CqtrxBJN.js b/effects/iU6ad2a8CqtrxBJN.js new file mode 100644 index 0000000..c99b4e8 --- /dev/null +++ b/effects/iU6ad2a8CqtrxBJN.js @@ -0,0 +1 @@ +args.fields.difficulty -= 1; \ No newline at end of file diff --git a/effects/iUUp4ouubYFUgtuB.js b/effects/iUUp4ouubYFUgtuB.js new file mode 100644 index 0000000..4533c4c --- /dev/null +++ b/effects/iUUp4ouubYFUgtuB.js @@ -0,0 +1,4 @@ +if (args.combat.combatant?.token?.actor.uuid == this.effect.sourceActor.uuid) +{ + this.effect.delete(); +} \ No newline at end of file diff --git a/effects/iYe5V7unJwy6gUOF.js b/effects/iYe5V7unJwy6gUOF.js new file mode 100644 index 0000000..c7a991f --- /dev/null +++ b/effects/iYe5V7unJwy6gUOF.js @@ -0,0 +1 @@ +return args.attribute != "willpower" \ No newline at end of file diff --git a/effects/idV6ioTFaIuald60.js b/effects/idV6ioTFaIuald60.js new file mode 100644 index 0000000..75e2462 --- /dev/null +++ b/effects/idV6ioTFaIuald60.js @@ -0,0 +1,11 @@ +let roll = await new Roll("dp").roll(); +roll.toMessage(this.script.getChatData()); + +if (roll.total == 6) +{ + await this.actor.applyHealing({wounds: 1, shock: 1}, {messageData : this.script.getChatData()}); + this.actor.removeCondition("dying"); + this.actor.removeCondition("exhausted"); + this.actor.removeCondition("prone"); + this.actor.removeCondition("dead"); +} \ No newline at end of file diff --git a/effects/ij6jW9sSSvHbJwBw.js b/effects/ij6jW9sSSvHbJwBw.js new file mode 100644 index 0000000..be7fde5 --- /dev/null +++ b/effects/ij6jW9sSSvHbJwBw.js @@ -0,0 +1 @@ +args.fields.ignoreShock = true; \ No newline at end of file diff --git a/effects/ijii6HywL12e3Xig.js b/effects/ijii6HywL12e3Xig.js new file mode 100644 index 0000000..f5dc238 --- /dev/null +++ b/effects/ijii6HywL12e3Xig.js @@ -0,0 +1,18 @@ +if (!this.item.name.includes("(")) +{ + let name = this.item.name + + let choice = await ItemDialog.create(ItemDialog.objectToArray({ + taste: "Taste", + sight: "Sight", + smell: "Smell", + hearing: "Hearing", + touch: "Touch" + }, this.item.img), 1, { title: this.item.name, text: "Choose Sense" }); + + if (choice[0]) { + name += ` (${choice[0].name})` + } + this.item.updateSource({ name }) + this.effect.updateSource({ name }) +} \ No newline at end of file diff --git a/effects/iuF3ZVp1EogwdWXe.js b/effects/iuF3ZVp1EogwdWXe.js new file mode 100644 index 0000000..777c8ef --- /dev/null +++ b/effects/iuF3ZVp1EogwdWXe.js @@ -0,0 +1,9 @@ +let effects = this.item.effects.contents.filter(i => i.id != this.effect.id); + +let choice = await ItemDialog.create(effects, 1, {title : this.effect.name, text: this.script.name}); + +if (choice[0]) +{ + this.item.updateSource({name : this.item.name.replace("Trait", choice[0].name)}); + choice[0].updateSource({name : `Uncanny [${choice[0].name}]`, "system.transferData.type" : "document"}) +} \ No newline at end of file diff --git a/effects/jUuLH5m3pmWbcJNv.js b/effects/jUuLH5m3pmWbcJNv.js new file mode 100644 index 0000000..ff3dd04 --- /dev/null +++ b/effects/jUuLH5m3pmWbcJNv.js @@ -0,0 +1,2 @@ +await this.actor.removeCondition("vulnerable") +await this.actor.removeCondition("hindered") \ No newline at end of file diff --git a/effects/jmbP7QbNLBiGWdqb.js b/effects/jmbP7QbNLBiGWdqb.js new file mode 100644 index 0000000..6cd8310 --- /dev/null +++ b/effects/jmbP7QbNLBiGWdqb.js @@ -0,0 +1 @@ +return args.target?.hasKeyword(["CHAOS", "HERETIC"]); \ No newline at end of file diff --git a/effects/jsAmDWF0lCJDY5ds.js b/effects/jsAmDWF0lCJDY5ds.js new file mode 100644 index 0000000..58c9c9d --- /dev/null +++ b/effects/jsAmDWF0lCJDY5ds.js @@ -0,0 +1 @@ +return args.skill != "stealth"; \ No newline at end of file diff --git a/effects/jsuGIGHvoi6kIwlg.js b/effects/jsuGIGHvoi6kIwlg.js new file mode 100644 index 0000000..0fda8a4 --- /dev/null +++ b/effects/jsuGIGHvoi6kIwlg.js @@ -0,0 +1 @@ +return args.skill == "medicae" \ No newline at end of file diff --git a/effects/k5rHY4zyz4eHaK9p.js b/effects/k5rHY4zyz4eHaK9p.js new file mode 100644 index 0000000..a0079e9 --- /dev/null +++ b/effects/k5rHY4zyz4eHaK9p.js @@ -0,0 +1 @@ +return args.attribute != "weaponSkill" \ No newline at end of file diff --git a/effects/k8GTyix8MCn3Aav5.js b/effects/k8GTyix8MCn3Aav5.js new file mode 100644 index 0000000..6728e8f --- /dev/null +++ b/effects/k8GTyix8MCn3Aav5.js @@ -0,0 +1,9 @@ +if (this.item.name.includes("Keyword")) +{ + let choice = await ValueDialog.create({text: "Enter Loremaster Keyword", title : this.effect.name}); + if (choice) + { + this.item.updateSource({name : this.item.name.replace("Keyword", choice.toUpperCase())}); + this.effect.updateSource({name : this.item.name}); + } +} \ No newline at end of file diff --git a/effects/kVDQ1CJOLAipZJDF.js b/effects/kVDQ1CJOLAipZJDF.js new file mode 100644 index 0000000..dd85212 --- /dev/null +++ b/effects/kVDQ1CJOLAipZJDF.js @@ -0,0 +1,2 @@ +args.fields.ed.value += (args.actor.system.advances.rank) +args.fields.ap.value -= (args.actor.system.advances.rank) \ No newline at end of file diff --git a/effects/kWmUhKdfROjmN14D.js b/effects/kWmUhKdfROjmN14D.js new file mode 100644 index 0000000..0250b5e --- /dev/null +++ b/effects/kWmUhKdfROjmN14D.js @@ -0,0 +1 @@ +this.actor.removeCondition("frenzied"); \ No newline at end of file diff --git a/effects/kYVIfxAaw5yVlv7q.js b/effects/kYVIfxAaw5yVlv7q.js new file mode 100644 index 0000000..19499ba --- /dev/null +++ b/effects/kYVIfxAaw5yVlv7q.js @@ -0,0 +1,7 @@ +// Bug: https://github.com/foundryvtt/foundryvtt/issues/7987 +// Solution currently is to divide by 2 +if (args.item.system.isRanged && args.item.system.isEquipped && args.item.hasKeyword("PROJECTILE")) +{ + args.item.system.salvo += this.actor.system.advances.rank / 2; + +} \ No newline at end of file diff --git a/effects/kaeQpP8uopwR3lEu.js b/effects/kaeQpP8uopwR3lEu.js new file mode 100644 index 0000000..de34d8f --- /dev/null +++ b/effects/kaeQpP8uopwR3lEu.js @@ -0,0 +1 @@ +args.fields.pool += (2) \ No newline at end of file diff --git a/effects/l0B9EGEGwi8qppAQ.js b/effects/l0B9EGEGwi8qppAQ.js new file mode 100644 index 0000000..01edaa2 --- /dev/null +++ b/effects/l0B9EGEGwi8qppAQ.js @@ -0,0 +1,2 @@ +this.actor.update({"system.resources.wrath" : this.actor.system.resources.wrath + 1}); +ui.notifications.info(`${this.effect.sourceItem.name}: Added 1 Wrath`) \ No newline at end of file diff --git a/effects/l5eevl1fQWYhumG8.js b/effects/l5eevl1fQWYhumG8.js new file mode 100644 index 0000000..598849f --- /dev/null +++ b/effects/l5eevl1fQWYhumG8.js @@ -0,0 +1,7 @@ +if (!args.item.isRanged) + return; + +if (!args.item._source.system.traits.list.find(i => i.name == "pistol")) +{ + args.item.system.traits.list.push({name : "pistol"}); +} \ No newline at end of file diff --git a/effects/l67Xry71u1CV0Ub2.js b/effects/l67Xry71u1CV0Ub2.js new file mode 100644 index 0000000..53859fc --- /dev/null +++ b/effects/l67Xry71u1CV0Ub2.js @@ -0,0 +1 @@ +return args.skill == "intimidation" \ No newline at end of file diff --git a/effects/lD2tFqbh0wFkZSrK.js b/effects/lD2tFqbh0wFkZSrK.js new file mode 100644 index 0000000..56f514b --- /dev/null +++ b/effects/lD2tFqbh0wFkZSrK.js @@ -0,0 +1 @@ +args.fields.pool += (this.effect.sourceTest.result.bonus) \ No newline at end of file diff --git a/effects/lER1x79iaiqO1fOW.js b/effects/lER1x79iaiqO1fOW.js new file mode 100644 index 0000000..05c5d55 --- /dev/null +++ b/effects/lER1x79iaiqO1fOW.js @@ -0,0 +1,15 @@ +if (args.item.system.isEquipped && args.item.type == "weapon" && args.item.hasKeyword("BOLT")) +{ +let sourceRF = args.item._source.system.traits.list.find(i => i.name == "rapidFire") +let rf = args.item.system.traits.list.find(i => i.name == "rapidFire"); +if (sourceRF && !rf.bd) +{ + let rf = args.item.system.traits.list.find(i => i.name == "rapidFire"); + rf.rating = parseInt(args.item.traitList.rapidFire.rating) + this.actor.system.advances.rank; + rf.bd = true; +} +else if (!sourceRF && !rf) +{ + args.item.system.traits.list.push({name : "rapidFire", rating: 2}) +} +} \ No newline at end of file diff --git a/effects/lFSnysWskES0QfCg.js b/effects/lFSnysWskES0QfCg.js new file mode 100644 index 0000000..d266c22 --- /dev/null +++ b/effects/lFSnysWskES0QfCg.js @@ -0,0 +1 @@ +return args.options.resist?.includes("psychicPower") \ No newline at end of file diff --git a/effects/lIeTkWRe7wmkZN6W.js b/effects/lIeTkWRe7wmkZN6W.js new file mode 100644 index 0000000..e348170 --- /dev/null +++ b/effects/lIeTkWRe7wmkZN6W.js @@ -0,0 +1 @@ +return !args.weapons; \ No newline at end of file diff --git a/effects/lJvf6AVjmuHoddQV.js b/effects/lJvf6AVjmuHoddQV.js new file mode 100644 index 0000000..d4cb21c --- /dev/null +++ b/effects/lJvf6AVjmuHoddQV.js @@ -0,0 +1 @@ +return args.fields.distance > 12; \ No newline at end of file diff --git a/effects/lXnUDQrXmieYYUfb.js b/effects/lXnUDQrXmieYYUfb.js new file mode 100644 index 0000000..87cb46e --- /dev/null +++ b/effects/lXnUDQrXmieYYUfb.js @@ -0,0 +1 @@ +return args.targets[0].name == this.effect.specifier \ No newline at end of file diff --git a/effects/lYrTbmezOxjtpUHd.js b/effects/lYrTbmezOxjtpUHd.js new file mode 100644 index 0000000..3f0cd1e --- /dev/null +++ b/effects/lYrTbmezOxjtpUHd.js @@ -0,0 +1 @@ +args.fields.ed += args.target.system.advances.tier; \ No newline at end of file diff --git a/effects/lbIWEbtpdbsgjxSQ.js b/effects/lbIWEbtpdbsgjxSQ.js new file mode 100644 index 0000000..ee36667 --- /dev/null +++ b/effects/lbIWEbtpdbsgjxSQ.js @@ -0,0 +1,4 @@ +if (args.test.result.test) +{ + args.test.result.test.dn += this.actor.system.advances.rank; +} \ No newline at end of file diff --git a/effects/lgVM7wAPaBYEwwIP.js b/effects/lgVM7wAPaBYEwwIP.js new file mode 100644 index 0000000..5acac40 --- /dev/null +++ b/effects/lgVM7wAPaBYEwwIP.js @@ -0,0 +1 @@ +return true; \ No newline at end of file diff --git a/effects/lpYvIQske0dpQMW5.js b/effects/lpYvIQske0dpQMW5.js new file mode 100644 index 0000000..17fb837 --- /dev/null +++ b/effects/lpYvIQske0dpQMW5.js @@ -0,0 +1 @@ +return args.target.system.mob?.value > 1; \ No newline at end of file diff --git a/effects/m9TL25I0TQQiXXou.js b/effects/m9TL25I0TQQiXXou.js new file mode 100644 index 0000000..b500060 --- /dev/null +++ b/effects/m9TL25I0TQQiXXou.js @@ -0,0 +1 @@ +return args.weapon.isRanged && args.fields.distance > 18; \ No newline at end of file diff --git a/effects/mCmXZR7irAzGdOVF.js b/effects/mCmXZR7irAzGdOVF.js new file mode 100644 index 0000000..31b1867 --- /dev/null +++ b/effects/mCmXZR7irAzGdOVF.js @@ -0,0 +1 @@ +return args.target.statuses.has("halfCover") || args.target.statuses.has("fullCover"); \ No newline at end of file diff --git a/effects/mJymsW6BtEvjJYCB.js b/effects/mJymsW6BtEvjJYCB.js new file mode 100644 index 0000000..39b542d --- /dev/null +++ b/effects/mJymsW6BtEvjJYCB.js @@ -0,0 +1,5 @@ +if (args.item.type == "weapon" && args.item.hasKeyword(["BOLT", "MELTA", "FIRE"])) +{ + // See https://github.com/foundryvtt/foundryvtt/issues/7987 + args.item.system.damage.bonus += this.actor.system.advances.rank / 2 +} \ No newline at end of file diff --git a/effects/miuWXyBThpx2nwgi.js b/effects/miuWXyBThpx2nwgi.js new file mode 100644 index 0000000..1430963 --- /dev/null +++ b/effects/miuWXyBThpx2nwgi.js @@ -0,0 +1 @@ +args.fields.pool += (args.actor.system.advances.rank * 2) \ No newline at end of file diff --git a/effects/nRzPe8J9HBSo8Ouv.js b/effects/nRzPe8J9HBSo8Ouv.js new file mode 100644 index 0000000..a48ce6d --- /dev/null +++ b/effects/nRzPe8J9HBSo8Ouv.js @@ -0,0 +1 @@ +return args.data.skill !== "athletics"; \ No newline at end of file diff --git a/effects/nqlrIfrjMNqm9rPu.js b/effects/nqlrIfrjMNqm9rPu.js new file mode 100644 index 0000000..98b252d --- /dev/null +++ b/effects/nqlrIfrjMNqm9rPu.js @@ -0,0 +1,4 @@ +if (args.wounds || args.mortal) +{ + this.item.effects.getName("Flensing Fury").update({disabled: false}); +} \ No newline at end of file diff --git a/effects/o53o6uRxwzd2zD7o.js b/effects/o53o6uRxwzd2zD7o.js new file mode 100644 index 0000000..645ad8f --- /dev/null +++ b/effects/o53o6uRxwzd2zD7o.js @@ -0,0 +1 @@ +args.fields.ed.value += args.actor.system.combat.stealth \ No newline at end of file diff --git a/effects/oOJIFfnYm8FKSsrR.js b/effects/oOJIFfnYm8FKSsrR.js new file mode 100644 index 0000000..7802c98 --- /dev/null +++ b/effects/oOJIFfnYm8FKSsrR.js @@ -0,0 +1 @@ +args.fields.difficulty -= args.actor.system.advances.rank * 2 \ No newline at end of file diff --git a/effects/ogLfE7uslg3NOAw1.js b/effects/ogLfE7uslg3NOAw1.js new file mode 100644 index 0000000..0322531 --- /dev/null +++ b/effects/ogLfE7uslg3NOAw1.js @@ -0,0 +1 @@ +args.fields.difficulty += 1 \ No newline at end of file diff --git a/effects/ooSIRNctWCRYawhZ.js b/effects/ooSIRNctWCRYawhZ.js new file mode 100644 index 0000000..58b178f --- /dev/null +++ b/effects/ooSIRNctWCRYawhZ.js @@ -0,0 +1,3 @@ +RuinGloryCounter.changeCounter(1, "glory"); + +this.script.notification("1 Glory Added"); \ No newline at end of file diff --git a/effects/oolhxEgOc3mFsBwJ.js b/effects/oolhxEgOc3mFsBwJ.js new file mode 100644 index 0000000..491b7c0 --- /dev/null +++ b/effects/oolhxEgOc3mFsBwJ.js @@ -0,0 +1 @@ +return !args.weapon?.isMelee; \ No newline at end of file diff --git a/effects/osnCvMgQ8yz26om9.js b/effects/osnCvMgQ8yz26om9.js new file mode 100644 index 0000000..4f75a4a --- /dev/null +++ b/effects/osnCvMgQ8yz26om9.js @@ -0,0 +1 @@ +args.fields.damage.bonus += (args.actor.system.advances.rank) \ No newline at end of file diff --git a/effects/oueyTSpTbi8zzrcq.js b/effects/oueyTSpTbi8zzrcq.js new file mode 100644 index 0000000..aade302 --- /dev/null +++ b/effects/oueyTSpTbi8zzrcq.js @@ -0,0 +1,6 @@ +let item = args.test?.item; + +if (item && item.hasKeyword(["MELTA", "FIRE"])) +{ + args.abort = this.effect.name; +} \ No newline at end of file diff --git a/effects/p9y4Td4ZLYcVHC3X.js b/effects/p9y4Td4ZLYcVHC3X.js new file mode 100644 index 0000000..e7e17d7 --- /dev/null +++ b/effects/p9y4Td4ZLYcVHC3X.js @@ -0,0 +1,5 @@ +if (args.isSuccess && args.targetTokens.map(i => i.actor).filter(i => i).some(a => a.system.mob?.value)) +{ + args.result.success += this.actor.system.advances.rank + args.result.text[this.effect.id] = {label : this.effect.name, description : `Added ${this.actor.system.advances.rank} Icons`} +} \ No newline at end of file diff --git a/effects/pDRZ5DMEdCw9W4C7.js b/effects/pDRZ5DMEdCw9W4C7.js new file mode 100644 index 0000000..d7b5573 --- /dev/null +++ b/effects/pDRZ5DMEdCw9W4C7.js @@ -0,0 +1 @@ +return args.skill != "pilot" \ No newline at end of file diff --git a/effects/pFJ8cQaMJRs4KnCP.js b/effects/pFJ8cQaMJRs4KnCP.js new file mode 100644 index 0000000..513ee4c --- /dev/null +++ b/effects/pFJ8cQaMJRs4KnCP.js @@ -0,0 +1,2 @@ +args.fields.damage += 1; +args.fields.pool += 4; \ No newline at end of file diff --git a/effects/pOhKN1s9y1RTy89J.js b/effects/pOhKN1s9y1RTy89J.js new file mode 100644 index 0000000..c6be62d --- /dev/null +++ b/effects/pOhKN1s9y1RTy89J.js @@ -0,0 +1 @@ +args.fields.ed.value += 4; \ No newline at end of file diff --git a/effects/pRxvTPAoTozwcbjj.js b/effects/pRxvTPAoTozwcbjj.js new file mode 100644 index 0000000..6ce8b49 --- /dev/null +++ b/effects/pRxvTPAoTozwcbjj.js @@ -0,0 +1 @@ +return args.options.multi > 1; \ No newline at end of file diff --git a/effects/pX5gXopi2ldlzKgb.js b/effects/pX5gXopi2ldlzKgb.js new file mode 100644 index 0000000..8c6ef31 --- /dev/null +++ b/effects/pX5gXopi2ldlzKgb.js @@ -0,0 +1 @@ +this.actor.removeCondition("restrained") \ No newline at end of file diff --git a/effects/pb0Z1LMiekkRrTJQ.js b/effects/pb0Z1LMiekkRrTJQ.js new file mode 100644 index 0000000..ca5c1d8 --- /dev/null +++ b/effects/pb0Z1LMiekkRrTJQ.js @@ -0,0 +1 @@ +return args.skill == "psychicMastery" \ No newline at end of file diff --git a/effects/q1lg7gMIadtty31V.js b/effects/q1lg7gMIadtty31V.js new file mode 100644 index 0000000..8190213 --- /dev/null +++ b/effects/q1lg7gMIadtty31V.js @@ -0,0 +1 @@ +return args.skill == "stealth"; \ No newline at end of file diff --git a/effects/q9qCgYfhjGfABnJG.js b/effects/q9qCgYfhjGfABnJG.js new file mode 100644 index 0000000..98126d6 --- /dev/null +++ b/effects/q9qCgYfhjGfABnJG.js @@ -0,0 +1,9 @@ +if (this.item.name.includes("Any")) +{ + let choice = await ValueDialog.create({text: "Enter Hatred Keyword", title : this.effect.name}); + if (choice) + { + this.item.updateSource({name : this.item.name.replace("Any", choice.toUpperCase())}); + this.effect.updateSource({name : this.item.name}); + } +} \ No newline at end of file diff --git a/effects/qNIDztrDVsmYDvTr.js b/effects/qNIDztrDVsmYDvTr.js new file mode 100644 index 0000000..0b6b67d --- /dev/null +++ b/effects/qNIDztrDVsmYDvTr.js @@ -0,0 +1 @@ +return args.weapon?.id != this.effect.getFlag(game.system.id, "trademarkId") \ No newline at end of file diff --git a/effects/qPoO0dnxeLUbrMAa.js b/effects/qPoO0dnxeLUbrMAa.js new file mode 100644 index 0000000..9077a5e --- /dev/null +++ b/effects/qPoO0dnxeLUbrMAa.js @@ -0,0 +1 @@ +args.fields.pool += (this.effect.sourceActor.system.advances.rank * 2) \ No newline at end of file diff --git a/effects/qjs3Ub0DWJ4Zo0Rq.js b/effects/qjs3Ub0DWJ4Zo0Rq.js new file mode 100644 index 0000000..af1bbcf --- /dev/null +++ b/effects/qjs3Ub0DWJ4Zo0Rq.js @@ -0,0 +1 @@ +return args.weapon.isRanged; \ No newline at end of file diff --git a/effects/qpF5cnBJLq2DGbVJ.js b/effects/qpF5cnBJLq2DGbVJ.js new file mode 100644 index 0000000..43ef8ec --- /dev/null +++ b/effects/qpF5cnBJLq2DGbVJ.js @@ -0,0 +1 @@ +return args.weapon || args.power || ["fellowship", "intellect"].includes(args.skill) \ No newline at end of file diff --git a/effects/qrko05ksnkafSuLH.js b/effects/qrko05ksnkafSuLH.js new file mode 100644 index 0000000..8c1a9e4 --- /dev/null +++ b/effects/qrko05ksnkafSuLH.js @@ -0,0 +1,3 @@ +await this.actor.addCondition("hindered", {[game.system.id] : {value : 2}}) + +await this.actor.addCondition("staggered") \ No newline at end of file diff --git a/effects/qwZ6OTEbAXNZJ9yX.js b/effects/qwZ6OTEbAXNZJ9yX.js new file mode 100644 index 0000000..45650fa --- /dev/null +++ b/effects/qwZ6OTEbAXNZJ9yX.js @@ -0,0 +1 @@ +return args.skill != "scholar" \ No newline at end of file diff --git a/effects/r2yuM4c339EFvQVt.js b/effects/r2yuM4c339EFvQVt.js new file mode 100644 index 0000000..c7d1547 --- /dev/null +++ b/effects/r2yuM4c339EFvQVt.js @@ -0,0 +1,11 @@ +let effects = this.item.effects.contents.filter(i => i.id != this.effect.id); + +effects.forEach(e => { + e.update({disabled : true}) +}) + +let e = effects.find(i => i.id == "MGiuyToigSluPOE5"); + +e.update({disabled : false}); + +this.script.notification(`${e.name} (+1 Defence) Activated`) \ No newline at end of file diff --git a/effects/r6Akxtto05ADYOrs.js b/effects/r6Akxtto05ADYOrs.js new file mode 100644 index 0000000..79f3492 --- /dev/null +++ b/effects/r6Akxtto05ADYOrs.js @@ -0,0 +1 @@ +return args.attribute == "fellowship" || args.skill == "intimidation" \ No newline at end of file diff --git a/effects/rEyARRVbIC24gond.js b/effects/rEyARRVbIC24gond.js new file mode 100644 index 0000000..205bb8d --- /dev/null +++ b/effects/rEyARRVbIC24gond.js @@ -0,0 +1 @@ +return args.fields.range == "long" \ No newline at end of file diff --git a/effects/rFS1jHvsucUNBwez.js b/effects/rFS1jHvsucUNBwez.js new file mode 100644 index 0000000..c2ce40f --- /dev/null +++ b/effects/rFS1jHvsucUNBwez.js @@ -0,0 +1,2 @@ +args.fields.pool += 2; +args.fields.ed.value += 2; \ No newline at end of file diff --git a/effects/rSgP5FedH8BCMgER.js b/effects/rSgP5FedH8BCMgER.js new file mode 100644 index 0000000..169f9b3 --- /dev/null +++ b/effects/rSgP5FedH8BCMgER.js @@ -0,0 +1 @@ +args.fields.pool -= (this.effect.sourceTest.result.WSPenalty) \ No newline at end of file diff --git a/effects/rm96unQpe10RFKad.js b/effects/rm96unQpe10RFKad.js new file mode 100644 index 0000000..893629b --- /dev/null +++ b/effects/rm96unQpe10RFKad.js @@ -0,0 +1 @@ +return args.options.multi \ No newline at end of file diff --git a/effects/rrQQLpoZ74X6V866.js b/effects/rrQQLpoZ74X6V866.js new file mode 100644 index 0000000..b717cbb --- /dev/null +++ b/effects/rrQQLpoZ74X6V866.js @@ -0,0 +1,2 @@ +// https://github.com/foundryvtt/foundryvtt/issues/7987 +this.item.system.salvo += 0.5; \ No newline at end of file diff --git a/effects/ry6TRb3XOnZMSNtZ.js b/effects/ry6TRb3XOnZMSNtZ.js new file mode 100644 index 0000000..36a0b4c --- /dev/null +++ b/effects/ry6TRb3XOnZMSNtZ.js @@ -0,0 +1 @@ +args.fields.ed.value += (this.effect.sourceTest.result.edPenalty) \ No newline at end of file diff --git a/effects/s3VJqcRi2aAoPBxo.js b/effects/s3VJqcRi2aAoPBxo.js new file mode 100644 index 0000000..19197be --- /dev/null +++ b/effects/s3VJqcRi2aAoPBxo.js @@ -0,0 +1 @@ +return !args.options.determination && args.skill != "tech" \ No newline at end of file diff --git a/effects/s8gRnNiND2DAlbQm.js b/effects/s8gRnNiND2DAlbQm.js new file mode 100644 index 0000000..e7bf7dd --- /dev/null +++ b/effects/s8gRnNiND2DAlbQm.js @@ -0,0 +1 @@ +return !args.weapon.system.isMelee; \ No newline at end of file diff --git a/effects/sB7sGAK1sB3FCZPU.js b/effects/sB7sGAK1sB3FCZPU.js new file mode 100644 index 0000000..54a11bc --- /dev/null +++ b/effects/sB7sGAK1sB3FCZPU.js @@ -0,0 +1 @@ +args.fields.pool += 3; \ No newline at end of file diff --git a/effects/sKu1EkyvtKjzP2ND.js b/effects/sKu1EkyvtKjzP2ND.js new file mode 100644 index 0000000..3203b9b --- /dev/null +++ b/effects/sKu1EkyvtKjzP2ND.js @@ -0,0 +1 @@ +return !args.weapon || args.weapon.isRanged; \ No newline at end of file diff --git a/effects/sPGM5PQudnBtx7f3.js b/effects/sPGM5PQudnBtx7f3.js new file mode 100644 index 0000000..00927e7 --- /dev/null +++ b/effects/sPGM5PQudnBtx7f3.js @@ -0,0 +1 @@ +return !args.options.resolve; \ No newline at end of file diff --git a/effects/sfx6toL0h2qZqOL1.js b/effects/sfx6toL0h2qZqOL1.js new file mode 100644 index 0000000..24a56fe --- /dev/null +++ b/effects/sfx6toL0h2qZqOL1.js @@ -0,0 +1 @@ +return args.options.resist?.includes("psychicPower"); \ No newline at end of file diff --git a/effects/st9bGbW1bDs3gVtg.js b/effects/st9bGbW1bDs3gVtg.js new file mode 100644 index 0000000..6189625 --- /dev/null +++ b/effects/st9bGbW1bDs3gVtg.js @@ -0,0 +1 @@ +args.fields.ap.value += (-args.actor.system.advances.rank) \ No newline at end of file diff --git a/effects/tNdYTVOLLNwsv6rp.js b/effects/tNdYTVOLLNwsv6rp.js new file mode 100644 index 0000000..ea70c4a --- /dev/null +++ b/effects/tNdYTVOLLNwsv6rp.js @@ -0,0 +1,13 @@ +if (args.item.system.isEquipped) +{ +if (args.item._source.system.traits.list.find(i => i.name == "brutal")) +{ + args.item.flags.hasBrutal = true; +} +else +{ + args.item.system.traits.list.push({name : "brutal"}) +} + + +} \ No newline at end of file diff --git a/effects/tOnMd9D7MHn4eXFp.js b/effects/tOnMd9D7MHn4eXFp.js new file mode 100644 index 0000000..28b7859 --- /dev/null +++ b/effects/tOnMd9D7MHn4eXFp.js @@ -0,0 +1 @@ +return args.skill == "persuasion" \ No newline at end of file diff --git a/effects/tPwJOmN2seFJyCUV.js b/effects/tPwJOmN2seFJyCUV.js new file mode 100644 index 0000000..718d168 --- /dev/null +++ b/effects/tPwJOmN2seFJyCUV.js @@ -0,0 +1 @@ +args.fields.pool += (this.effect.sourceActor.system.advances.rank) * 2 \ No newline at end of file diff --git a/effects/tSkQICmV6Rb46lcO.js b/effects/tSkQICmV6Rb46lcO.js new file mode 100644 index 0000000..ad62ee1 --- /dev/null +++ b/effects/tSkQICmV6Rb46lcO.js @@ -0,0 +1 @@ +return !args.options.multi \ No newline at end of file diff --git a/effects/ti7VF62RlG5IZcEy.js b/effects/ti7VF62RlG5IZcEy.js new file mode 100644 index 0000000..79e11f5 --- /dev/null +++ b/effects/ti7VF62RlG5IZcEy.js @@ -0,0 +1 @@ +return args.skill != "intimidation" || args.actor.itemTypes.armour.filter(i => i.system.isEquipped && i.hasKeyword("HEAVY")).length == 0 \ No newline at end of file diff --git a/effects/tyFTdIGFK74yF3hK.js b/effects/tyFTdIGFK74yF3hK.js new file mode 100644 index 0000000..fde6bbc --- /dev/null +++ b/effects/tyFTdIGFK74yF3hK.js @@ -0,0 +1 @@ +args.fields.difficulty--; \ No newline at end of file diff --git a/effects/tzhERsZsFHmeLp10.js b/effects/tzhERsZsFHmeLp10.js new file mode 100644 index 0000000..5d4e7a0 --- /dev/null +++ b/effects/tzhERsZsFHmeLp10.js @@ -0,0 +1,8 @@ +let roll = await new Roll("dp").roll(); + +if (roll.dice[0].results[0].value >= 1) +{ + this.actor.addCondition("frenzied"); +} + +roll.toMessage(this.script.getChatData()); \ No newline at end of file diff --git a/effects/uCSQMb5MzJvpBynd.js b/effects/uCSQMb5MzJvpBynd.js new file mode 100644 index 0000000..41bdf63 --- /dev/null +++ b/effects/uCSQMb5MzJvpBynd.js @@ -0,0 +1,3 @@ +let bonus = args.target.statuses.has("fullCover")?2:1; + +args.fields.difficulty += bonus; \ No newline at end of file diff --git a/effects/uDMSAlABh4jqg69V.js b/effects/uDMSAlABh4jqg69V.js new file mode 100644 index 0000000..52f75e4 --- /dev/null +++ b/effects/uDMSAlABh4jqg69V.js @@ -0,0 +1 @@ +args.fields.pool += (1) \ No newline at end of file diff --git a/effects/uXgr5K68WSR7dGS8.js b/effects/uXgr5K68WSR7dGS8.js new file mode 100644 index 0000000..cec3862 --- /dev/null +++ b/effects/uXgr5K68WSR7dGS8.js @@ -0,0 +1 @@ +return args.attribute == "weaponSkill" \ No newline at end of file diff --git a/effects/unSFRiuQRqN7cTcM.js b/effects/unSFRiuQRqN7cTcM.js new file mode 100644 index 0000000..bea3b88 --- /dev/null +++ b/effects/unSFRiuQRqN7cTcM.js @@ -0,0 +1,4 @@ +if (args.test.testData.shifted.armourbane) +{ + args.modifiers.ap.push({label : this.effect.label, value : -1 * args.test.testData.shifted.armourbane.dice.length}) +} \ No newline at end of file diff --git a/effects/uqsOhE2qxCz7oOJl.js b/effects/uqsOhE2qxCz7oOJl.js new file mode 100644 index 0000000..4dd8ca2 --- /dev/null +++ b/effects/uqsOhE2qxCz7oOJl.js @@ -0,0 +1 @@ +return !args.weapon || args.weapon.isRanged \ No newline at end of file diff --git a/effects/vCwJgU7eJnlq1WzL.js b/effects/vCwJgU7eJnlq1WzL.js new file mode 100644 index 0000000..c9257bc --- /dev/null +++ b/effects/vCwJgU7eJnlq1WzL.js @@ -0,0 +1,6 @@ +let roll = await new Roll("10dp").roll(); +let icons = roll.dice[0].results.reduce((total, die) => total + die.value, 0); + +await this.actor.applyHealing({wounds: icons, shock: 0}, {messageData : this.script.getChatData()}); + +roll.toMessage(this.script.getChatData()); \ No newline at end of file diff --git a/effects/vHPsLvYNqhcF7R6Y.js b/effects/vHPsLvYNqhcF7R6Y.js new file mode 100644 index 0000000..3ada236 --- /dev/null +++ b/effects/vHPsLvYNqhcF7R6Y.js @@ -0,0 +1 @@ +return arg.skill == "intimidation" \ No newline at end of file diff --git a/effects/vJy1GbGNsq9DKLBh.js b/effects/vJy1GbGNsq9DKLBh.js new file mode 100644 index 0000000..fa9f22c --- /dev/null +++ b/effects/vJy1GbGNsq9DKLBh.js @@ -0,0 +1 @@ +return !args.weapon || args.weapon.isRanged || !this.actor.items.filter(i => i.system.isEquipped).some(i => i.flags.hasParry) \ No newline at end of file diff --git a/effects/vOM3FbAlKu8B5v7Q.js b/effects/vOM3FbAlKu8B5v7Q.js new file mode 100644 index 0000000..68f88b7 --- /dev/null +++ b/effects/vOM3FbAlKu8B5v7Q.js @@ -0,0 +1 @@ +this.actor.setupAttributeTest("willpower", {appendTitle : ` - ${this.effect.sourceItem.name}`}); \ No newline at end of file diff --git a/effects/vT9eJ7E8wianEjYe.js b/effects/vT9eJ7E8wianEjYe.js new file mode 100644 index 0000000..7ebd2f9 --- /dev/null +++ b/effects/vT9eJ7E8wianEjYe.js @@ -0,0 +1 @@ +return !args.weapon?.isMelee \ No newline at end of file diff --git a/effects/vjjs6BT6Net1NMIa.js b/effects/vjjs6BT6Net1NMIa.js new file mode 100644 index 0000000..384259f --- /dev/null +++ b/effects/vjjs6BT6Net1NMIa.js @@ -0,0 +1 @@ +return args.skill != "persuasion" && !args.options.influence \ No newline at end of file diff --git a/effects/vvNiRZSXslLMxCIW.js b/effects/vvNiRZSXslLMxCIW.js new file mode 100644 index 0000000..d1301af --- /dev/null +++ b/effects/vvNiRZSXslLMxCIW.js @@ -0,0 +1 @@ +return !args.options.resolve && !args.options.corruption \ No newline at end of file diff --git a/effects/vvQ5OklTChNlEex0.js b/effects/vvQ5OklTChNlEex0.js new file mode 100644 index 0000000..bacf708 --- /dev/null +++ b/effects/vvQ5OklTChNlEex0.js @@ -0,0 +1 @@ +return args.weapon && args.weapon.isRanged; \ No newline at end of file diff --git a/effects/w4Tk1CJll8XO8B1M.js b/effects/w4Tk1CJll8XO8B1M.js new file mode 100644 index 0000000..ce6eb8d --- /dev/null +++ b/effects/w4Tk1CJll8XO8B1M.js @@ -0,0 +1 @@ +return args.skill == "investigation" \ No newline at end of file diff --git a/effects/w7qqGlzwGOzoRtvi.js b/effects/w7qqGlzwGOzoRtvi.js new file mode 100644 index 0000000..7a90d19 --- /dev/null +++ b/effects/w7qqGlzwGOzoRtvi.js @@ -0,0 +1 @@ +return args.options.corruption \ No newline at end of file diff --git a/effects/wE70SJJI6hZibemp.js b/effects/wE70SJJI6hZibemp.js new file mode 100644 index 0000000..a9edd1e --- /dev/null +++ b/effects/wE70SJJI6hZibemp.js @@ -0,0 +1 @@ +args.fields.pool += (2 * args.actor.system.advances.rank) \ No newline at end of file diff --git a/effects/wK3Lgd7N4ZRApgKH.js b/effects/wK3Lgd7N4ZRApgKH.js new file mode 100644 index 0000000..675d825 --- /dev/null +++ b/effects/wK3Lgd7N4ZRApgKH.js @@ -0,0 +1,5 @@ +if (args.options.fear) +{ + args.abort = true; + this.script.notification("Automatically succeed Fear Tests") +} \ No newline at end of file diff --git a/effects/wKRySrfwHmXmNv0L.js b/effects/wKRySrfwHmXmNv0L.js new file mode 100644 index 0000000..4377443 --- /dev/null +++ b/effects/wKRySrfwHmXmNv0L.js @@ -0,0 +1,8 @@ +let actor = this.effect.sourceActor; + +let hatred = actor?.items.find(i => i.baseName == "Hatred"); + +if (hatred) +{ + this.effect.updateSource({"name" : hatred.name}) +} \ No newline at end of file diff --git a/effects/wPEffMHvctRv8rvT.js b/effects/wPEffMHvctRv8rvT.js new file mode 100644 index 0000000..19d5fa4 --- /dev/null +++ b/effects/wPEffMHvctRv8rvT.js @@ -0,0 +1 @@ +this.actor.addCondition("frenzied"); \ No newline at end of file diff --git a/effects/wYYMkQr4urxjuGoH.js b/effects/wYYMkQr4urxjuGoH.js new file mode 100644 index 0000000..9d794b2 --- /dev/null +++ b/effects/wYYMkQr4urxjuGoH.js @@ -0,0 +1 @@ +return args.attribute != "toughness" \ No newline at end of file diff --git a/effects/wZ4Mnb1Rvta5jfeI.js b/effects/wZ4Mnb1Rvta5jfeI.js new file mode 100644 index 0000000..f26144b --- /dev/null +++ b/effects/wZ4Mnb1Rvta5jfeI.js @@ -0,0 +1,2 @@ +let socials = ["cunning", "deception", "insight", "persuasion", "intimidation", "leadership"] +return !args.options.influence && !socials.includes(args.skill) \ No newline at end of file diff --git a/effects/wgn804Rr9pTBZy9y.js b/effects/wgn804Rr9pTBZy9y.js new file mode 100644 index 0000000..8552806 --- /dev/null +++ b/effects/wgn804Rr9pTBZy9y.js @@ -0,0 +1 @@ +args.fields.pool -= (this.effect.sourceTest.result.penalty) \ No newline at end of file diff --git a/effects/wnLz905L9fGRgOOO.js b/effects/wnLz905L9fGRgOOO.js new file mode 100644 index 0000000..6662967 --- /dev/null +++ b/effects/wnLz905L9fGRgOOO.js @@ -0,0 +1 @@ +args.fields.ap.value -= (args.actor.system.advances.rank) \ No newline at end of file diff --git a/effects/wt6FKxokrDb27nYD.js b/effects/wt6FKxokrDb27nYD.js new file mode 100644 index 0000000..2e79d48 --- /dev/null +++ b/effects/wt6FKxokrDb27nYD.js @@ -0,0 +1 @@ +return args.skill != this.effect.getFlag(game.system.id, "skill") \ No newline at end of file diff --git a/effects/x4TjQeYRdoCQB77M.js b/effects/x4TjQeYRdoCQB77M.js new file mode 100644 index 0000000..e1bdf32 --- /dev/null +++ b/effects/x4TjQeYRdoCQB77M.js @@ -0,0 +1 @@ +return args.options.influence; \ No newline at end of file diff --git a/effects/x6tJeIHYjtqdrP2n.js b/effects/x6tJeIHYjtqdrP2n.js new file mode 100644 index 0000000..043c6cd --- /dev/null +++ b/effects/x6tJeIHYjtqdrP2n.js @@ -0,0 +1,11 @@ +let shock = Math.ceil(CONFIG.Dice.randomUniform() * 6); +let wounds = Math.ceil(CONFIG.Dice.randomUniform() * 3); + +let healedShock = Math.floor(shock / 2); +let healedWounds = Math.floor(wounds / 2); + +let report = await this.actor.applyDamage(wounds, {shock}, {allowDetermination : false}); + +this.script.message(` Received ${report.wounds} Wounds and ${report.shock} Shock`) + +this.effect.sourceActor.applyHealing({shock : healedShock, wounds : healedWounds}, {messageData : this.script.getChatData()}) \ No newline at end of file diff --git a/effects/x7egaio7kd51yznb.js b/effects/x7egaio7kd51yznb.js new file mode 100644 index 0000000..e52767b --- /dev/null +++ b/effects/x7egaio7kd51yznb.js @@ -0,0 +1 @@ +args.fields.ed.value += (args.actor.system.test.result.edBonus) \ No newline at end of file diff --git a/effects/xY6bpcsYSWfYJGD2.js b/effects/xY6bpcsYSWfYJGD2.js new file mode 100644 index 0000000..4072ef3 --- /dev/null +++ b/effects/xY6bpcsYSWfYJGD2.js @@ -0,0 +1 @@ +return args.options.fear || args.options.resolve \ No newline at end of file diff --git a/effects/xhmaZFufaDuw5WhC.js b/effects/xhmaZFufaDuw5WhC.js new file mode 100644 index 0000000..39a9bca --- /dev/null +++ b/effects/xhmaZFufaDuw5WhC.js @@ -0,0 +1,4 @@ +if (args.test.result.isWrathCritical) +{ + args.actor.addCondition('bleeding'); +} \ No newline at end of file diff --git a/effects/xikjJXgmbcre6jIC.js b/effects/xikjJXgmbcre6jIC.js new file mode 100644 index 0000000..beaae8d --- /dev/null +++ b/effects/xikjJXgmbcre6jIC.js @@ -0,0 +1 @@ +args.fields.difficulty += parseInt(this.effect.specifier) || 1 \ No newline at end of file diff --git a/effects/xkeWd7NAWWrW9WkX.js b/effects/xkeWd7NAWWrW9WkX.js new file mode 100644 index 0000000..2fcca32 --- /dev/null +++ b/effects/xkeWd7NAWWrW9WkX.js @@ -0,0 +1 @@ +return args.targets[0]?.document.elevation > 0; \ No newline at end of file diff --git a/effects/xpl1K8zUzYlgJ8YN.js b/effects/xpl1K8zUzYlgJ8YN.js new file mode 100644 index 0000000..b7a85b7 --- /dev/null +++ b/effects/xpl1K8zUzYlgJ8YN.js @@ -0,0 +1 @@ +return !args.weapon || args.weapon?.system.isRanged \ No newline at end of file diff --git a/effects/xtv4RmAhiRMLPOae.js b/effects/xtv4RmAhiRMLPOae.js new file mode 100644 index 0000000..02ed014 --- /dev/null +++ b/effects/xtv4RmAhiRMLPOae.js @@ -0,0 +1 @@ +this.actor.addCondition("hindered"); \ No newline at end of file diff --git a/effects/xuWe1g9gmBQl8rQ5.js b/effects/xuWe1g9gmBQl8rQ5.js new file mode 100644 index 0000000..752063f --- /dev/null +++ b/effects/xuWe1g9gmBQl8rQ5.js @@ -0,0 +1 @@ +return args.options.determination \ No newline at end of file diff --git a/effects/xzHcRwl7ttJdNtXu.js b/effects/xzHcRwl7ttJdNtXu.js new file mode 100644 index 0000000..72903b8 --- /dev/null +++ b/effects/xzHcRwl7ttJdNtXu.js @@ -0,0 +1,3 @@ +let wounds = Math.ceil(CONFIG.Dice.randomUniform() * 3); + +await this.actor.applyHealing({wounds, shock: 0}, {messageData : this.script.getChatData()}); \ No newline at end of file diff --git a/effects/y0Ws5ayXVNY7TL76.js b/effects/y0Ws5ayXVNY7TL76.js new file mode 100644 index 0000000..8ee1ede --- /dev/null +++ b/effects/y0Ws5ayXVNY7TL76.js @@ -0,0 +1,11 @@ +let shock = Math.ceil(CONFIG.Dice.randomUniform() * 3) + +let add = 1 - this.effect.sourceTest.result.shock; + +shock += add; + +let tooltip = `1d3 (${shock}) + ${add}` + +let report = await this.actor.applyDamage(0, {shock}); + +this.script.message(`Received ${report.shock} Shock`); \ No newline at end of file diff --git a/effects/y491iKlFpYBFsDEY.js b/effects/y491iKlFpYBFsDEY.js new file mode 100644 index 0000000..2413fec --- /dev/null +++ b/effects/y491iKlFpYBFsDEY.js @@ -0,0 +1 @@ +return args.target && (args.target.hasKeyword("BEAST") || args.target.hasKeyword("SQUIG")); \ No newline at end of file diff --git a/effects/y6O5MpU4HSFhoKnr.js b/effects/y6O5MpU4HSFhoKnr.js new file mode 100644 index 0000000..656fd30 --- /dev/null +++ b/effects/y6O5MpU4HSFhoKnr.js @@ -0,0 +1,2 @@ +let report = await this.actor.applyDamage(0, {mortal: 1}); +this.script.message(`Received ${report.wounds} Wound`); \ No newline at end of file diff --git a/effects/yCpSKxuwkwdRmmNd.js b/effects/yCpSKxuwkwdRmmNd.js new file mode 100644 index 0000000..8060376 --- /dev/null +++ b/effects/yCpSKxuwkwdRmmNd.js @@ -0,0 +1 @@ +args.fields.pool += 8; \ No newline at end of file diff --git a/effects/yN4OM8UtNqAq2Dfy.js b/effects/yN4OM8UtNqAq2Dfy.js new file mode 100644 index 0000000..68c8560 --- /dev/null +++ b/effects/yN4OM8UtNqAq2Dfy.js @@ -0,0 +1,4 @@ +let value = await ValueDialog.create({text : this.script.label, title : this.effect.name}) + +this.effect.updateSource({name : this.effect.setSpecifier(value)}) +this.item.updateSource({name : this.item.setSpecifier(value)}) \ No newline at end of file diff --git a/effects/yUSIDl1k44HVWZpq.js b/effects/yUSIDl1k44HVWZpq.js new file mode 100644 index 0000000..71aa0b9 --- /dev/null +++ b/effects/yUSIDl1k44HVWZpq.js @@ -0,0 +1 @@ +return !args.data.weapon; \ No newline at end of file diff --git a/effects/yXbCqxGPkQilEqbY.js b/effects/yXbCqxGPkQilEqbY.js new file mode 100644 index 0000000..03c81a0 --- /dev/null +++ b/effects/yXbCqxGPkQilEqbY.js @@ -0,0 +1 @@ +return !args.options.multi; \ No newline at end of file diff --git a/effects/yjspaw14CPu5KOTK.js b/effects/yjspaw14CPu5KOTK.js new file mode 100644 index 0000000..ee13b71 --- /dev/null +++ b/effects/yjspaw14CPu5KOTK.js @@ -0,0 +1,2 @@ +let maxTargets = Math.min(args.options.multi, 6); +args.fields.difficulty -= 2 * (maxTargets - 1); \ No newline at end of file diff --git a/effects/ylHXSuC15gtMQM6U.js b/effects/ylHXSuC15gtMQM6U.js new file mode 100644 index 0000000..08bd70c --- /dev/null +++ b/effects/ylHXSuC15gtMQM6U.js @@ -0,0 +1,5 @@ +if (args.options.conviction || args.options.resolve) +{ + args.abort = true; + this.script.notification("Automatically succeed Resolve or Conviction Tests") +} \ No newline at end of file diff --git a/effects/yo50TP9ocyy5EkzQ.js b/effects/yo50TP9ocyy5EkzQ.js new file mode 100644 index 0000000..a6bbe3f --- /dev/null +++ b/effects/yo50TP9ocyy5EkzQ.js @@ -0,0 +1 @@ +return args.skill != "athletics" \ No newline at end of file diff --git a/effects/z0gTZtf2b4ZBaChd.js b/effects/z0gTZtf2b4ZBaChd.js new file mode 100644 index 0000000..ea8aff9 --- /dev/null +++ b/effects/z0gTZtf2b4ZBaChd.js @@ -0,0 +1 @@ +await this.actor.addEffectItems("Compendium.wng-core.items.Item.JVz6FoVEc7p1DJUj", this.effect); \ No newline at end of file diff --git a/effects/zRozYzzKaAXc4j5G.js b/effects/zRozYzzKaAXc4j5G.js new file mode 100644 index 0000000..ba29b23 --- /dev/null +++ b/effects/zRozYzzKaAXc4j5G.js @@ -0,0 +1 @@ +return !args.weapon?.system.isMelee; \ No newline at end of file diff --git a/effects/zZKaz4lu1seX4XTD.js b/effects/zZKaz4lu1seX4XTD.js new file mode 100644 index 0000000..92b4e92 --- /dev/null +++ b/effects/zZKaz4lu1seX4XTD.js @@ -0,0 +1 @@ +return false; \ No newline at end of file diff --git a/package.json b/package.json index 032156e..13a4358 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "description": "The **official** system for playing Warhammer 40k: Wrath & Glory on [Foundry VTT](https://foundryvtt.com/). Created by Moo Man.", "main": "wrath-and-glory.js", "scripts": { - "build": "cross-env NODE_ENV=development rollup --config rollup.config.js --watch", - "release": "cross-env NODE_ENV=production rollup --config rollup.config.js" + "build": "node scriptPacker.js&& cross-env NODE_ENV=development rollup --config rollup.config.js --watch", + "release": "node scriptPacker.js&& cross-env NODE_ENV=production rollup --config rollup.config.js" }, "repository": { "type": "git", diff --git a/scriptPacker.js b/scriptPacker.js new file mode 100644 index 0000000..d00ab5d --- /dev/null +++ b/scriptPacker.js @@ -0,0 +1,24 @@ +const fs = require("fs"); + +let path = "./effects/" +let scripts = fs.readdirSync(path); +let count = 0; +let scriptObj = {}; +for(let file of scripts) +{ + let script = fs.readFileSync(path + file, {encoding:"utf8"}); + scriptObj[file.split(".")[0]] = script; + count++; +} + +let scriptLoader = `export default function() +{ + Hooks.on("init", () => + { + foundry.utils.mergeObject(game.wng.config.effectScripts, ${JSON.stringify(scriptObj)}); + }); + +}` + +fs.writeFileSync("./scripts/loadEffects.js", scriptLoader) +console.log(`Packed ${count} scripts`); \ No newline at end of file diff --git a/scripts/apps/active-effect-config.js b/scripts/apps/active-effect-config.js deleted file mode 100644 index 2970427..0000000 --- a/scripts/apps/active-effect-config.js +++ /dev/null @@ -1,84 +0,0 @@ - -import EffectScriptConfig from "./effect-script.js" - -export default class WrathAndGloryEffectSheet extends ActiveEffectConfig { - async getData() { - let data = await super.getData() - data.modes[6] = "Dialog Effect" - data.modes[7] = "Targeter's Dialog Effect" - return data - } - - - activateListeners(html) { - - - html.find(".changes-list .effect-controls").each((i, element) => { - if (this.object.changes[i].mode > 5) - { - element.append($(``)[0]) - } - }) - - super.activateListeners(html) - html.find(".effect-script-config").click(ev => { - let index = parseInt($(ev.currentTarget).parents(".effect-change").attr("data-index")) - new EffectScriptConfig({effect : this.object, index}).render(true) - }) - - html.find(".mode select").change(ev => { - this.submit({preventClose: true}) - }) - } - - /** - * Handle adding a new change to the changes array. - * @private - */ - async _addEffectChange() { - const idx = this.document.changes.length; - super._addEffectChange().then(sheet => { - this.document.update({[`flags.wrath-and-glory.changeCondition.${idx}`] : {script : "", description : "", hide : ""}}) - }) - } - - /** - * When deleting an active effect, make sure its change condition is deleted too - */ - _onEffectControl(event) { - event.preventDefault(); - if (event.currentTarget.dataset.action == "delete") - { - - let index = $(event.currentTarget).parents(".effect-change")[0]?.dataset.index; - let newConditionals = this.deleteChangeCondition(index); - // Call normal operation, once done, delete change condition for deleted index - super._onEffectControl(event).then(() => { - this.document.update({"flags.wrath-and-glory.changeCondition" : null}).then(() => { // Delete previous object so data doesn't get merged and produce duplicates - this.document.update({"flags.wrath-and-glory.changeCondition" : newConditionals}) - }) - }) - } - else - { - return super._onEffectControl(event); - } - } - - /** - *When deleting a change condition, all indices after must be adjusted - * - * @param {Number} index Index of change being deleted - * @returns - */ - deleteChangeCondition(index) - { - let newConditionals = {}; - let conditionals = foundry.utils.deepClone(this.document.changeConditionals); - delete conditionals[index]; - Object.values(conditionals).forEach((value, index) => { - newConditionals[index] = value; - }) - return newConditionals; - } -} \ No newline at end of file diff --git a/scripts/apps/archetype-generic.js b/scripts/apps/archetype-generic.js deleted file mode 100644 index 890a926..0000000 --- a/scripts/apps/archetype-generic.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Interface for creating a generic object stored in an archetype's wargear. - * These generic objects are used when the archetype refers to an item that doesn't exist officially. - * This object could simply be a description of the item, or could include filters (to accomodate for - * some archetypes specifying a range of items e.g. "Any Common Weapon") - */ - -export default class ArchetypeGeneric extends FormApplication { - - static get defaultOptions() { - let options = super.defaultOptions; - options.classes.push("archetype-generic"); - options.template = "systems/wrath-and-glory/template/apps/archetype-generic.hbs"; - options.height = "auto"; - options.width = 285; - options.title = "Archetype Item Generic"; - options.resizable = true; - return options; - } - - getData() { - let data = super.getData(); - let generic = {} - if (Number.isNumeric(this.object.index)) - { - generic.property = this.object.item.wargear[this.object.index]?.property - generic.name = this.object.item.wargear[this.object.index]?.name - generic.filters = this.object.item.wargear[this.object.index]?.filters - } - - data.property = generic.property; - data.name = generic.name; - data.filters = generic.filters || [{test : "", property: "", value : ""}] - - return data - } - - async _updateObject(event, formData) { - let wargear = duplicate(this.object.item.wargear) - - // Create filter array with - let filters = Array.from($(event.target).find(".test")).map(t => { return {test : t.value}}) - - // Add Property to each element in the filter array - Array.from($(event.target).find(".property")).map((p, i) => filters[i].property = p.value) - - // Add value to each element in the filter array - Array.from($(event.target).find(".value")).map((p, i) => filters[i].value = p.value) - - filters = filters.filter(f => f.property) - - let generic = {type: "generic", name : formData.name, filters} - let groups = this.object.item.groups - if (Number.isNumeric(this.object.index)) - wargear[this.object.index] = generic - else - { - wargear.push(generic) - // Add new index to groups (last index + 1) - groups = this.object.item.addToGroup({type : "item", index : (wargear.length - 1 || 0)}) - - } - - this.object.item.update({"system.wargear" : wargear, "system.groups" : groups}) - } - - - activateListeners(html) - { - super.activateListeners(html) - - html.find(".add-filter").click(async ev => { - await this._updateObject(ev, new FormDataExtended(this.form)) - let wargear = duplicate(this.object.item.wargear) - let generic = wargear[this.object.index || 0] - generic.filters.push({test : "", property: "", value: ""}) - - // // Add new index to groups (last index + 1) - // let groups = this.object.item.addToGroup({type : "item", index : (wargear.length - 1 || 0)}) - - await this.object.item.update({"system.wargear" : wargear}) - this.render(true); - }) - } - - -} \ No newline at end of file diff --git a/scripts/apps/archetype-groups.js b/scripts/apps/archetype-groups.js deleted file mode 100644 index 97c9161..0000000 --- a/scripts/apps/archetype-groups.js +++ /dev/null @@ -1,302 +0,0 @@ -/** - * Welcome to my incredibly over-engineered concept for archetype wargear - * - * "groups" is the concept to convey how an archetype gets wargear A, B, C, and D (such as (A or B) and (C or D)) - * - * An archetype's wargear list is flat, however their "groups" object stores the instructions on how to distribute those items - * - * Within the groups object are objects of type "and", "or", "item". - * - * "and/or" objects have an "items" property that can store "and/or/item" type objects - * "item" type objects exist at the bottom of the structure, and they store the index of the item in the wargear array - * - * So this structure can go arbitraritly deep (ands storing ors storing ands storing ors ... to finally storing items). Absolutely overkill, but whatever - * - * Example: - * (0 AND 1 AND 2 AND (3 OR 4)) AND (5 OR 6 OR (7 AND 8)) - * { - type: "and", - items : [ - {type : 'and', items : [ - {type: 'item', index: 0}, - {type: 'item', index: 1}, - {type: 'item', index: 2}, - {type: 'or', items: [ - {type: 'item', index: 3}, - {type: 'item', index: 4}, - ]} - ]}, - {type: "or", items : [ - {type: 'item', index: 5}, - {type: 'item', index: 6}, - {type: "and", items : [ - {type: 'item', index: 7}, - {type: 'item', index: 8} - ]} - ]} - ] - } - - * - */ - - -export default class ArchetypeGroups extends Application { - - constructor(object) { - super() - this.object = object - } - - static get defaultOptions() { - return mergeObject(super.defaultOptions, { - id: "archetype-groups", - template: "systems/wrath-and-glory/template/apps/archetype-groups.hbs", - height: "auto", - width: 285, - title: "Archetype Groups", - resizable: true, - dragDrop: [{ dragSelector: ".wargear", dropSelector: ".group", permissions: { dragstart: true, drop: true } }] - }) - } - - getData() { - let data = super.getData(); - - data.groupHtml = this._constructHTML() - return data - } - - - _groupIndexToObjects() { - return ArchetypeGroups.groupIndexToObjects(this.object.groups, this.object) - } - - _constructHTML() - { - return ArchetypeGroups.constructHTML(this.object) - } - - - - // Recursive function to convert group index arrays into their corresponding objects objects - // Also assigns a temporary ID to easily handle moving groups around - static groupIndexToObjects(groupObject, item) { - if (["and", "or"].includes(groupObject.type)) - { - return { - type: groupObject.type, - items : groupObject.items.map(i => this.groupIndexToObjects(i, item)), - groupId: groupObject.groupId - } - } - - // Base case - else if (["item", "generic"].includes(groupObject.type)) - { - return mergeObject(item.wargear[groupObject.index], {index : groupObject.index, groupId : groupObject.groupId}) - } - } - - /** - * Construct html to display the groups in a readable format - * - * @param {Object} displayGroups Group object that has been processed with actual items (gone through groupIndexToObjects) - * @returns - */ - static constructHTML(item, {parentheses=false, commas=false, draggable=true}={}) { - let displayGroups = this.groupIndexToObjects(item.groups, item) - let html = ` -
- ` - - let groupToHtml = (groupObject) => { - let html = `` - if (["and", "or"].includes(groupObject.type)) // If is group collection, create group html and recursively call this function on items within - { - html += `
` - html += `
- ${parentheses // Denote groups with parenthes or not - ? " ( " - : "" } - - ${groupObject.items.map(groupToHtml).join( // Join subgroups and items with "connector", that being AND or OR (comma can be substituted for AND) - - commas && groupObject.type == "and" // If group type is AND with comma option, use comma, otherwise, use AND or OR - ? `,` - : `${groupObject.type.toUpperCase()} - `)} - - ${parentheses // End group with parentheses if option is present - ? " ) " - : ""} -
` - - html += `
` - } - else - html += `
${groupObject.name}
` - return html - } - - - html += groupToHtml(displayGroups) - - html += "
" - return html - } - - _onDragStart(ev) { - ev.dataTransfer.setData("text/plain", ev.target.dataset.id) - } - - _onDrop(ev) { - let dropID = ev.target.dataset.id - let dragID= ev.dataTransfer.getData("text/plain") - - if (dropID && dragID && dropID != dragID) - { - this.moveObject(dragID, dropID) - } - else if (dragID && !dropID) - { - this.moveObject(dragID, "root") - } - - - } - - async moveObject(moveID, destID) - { - let groups = duplicate(this.object.groups) - - let objectToMove = ArchetypeGroups.search(moveID, groups) - this.delete(moveID, groups) - this.insert(objectToMove, destID, groups) - this.clean(groups) - await this.object.update({"system.groups" : groups}) - this.render(true) - - } - - - // search groups object for ID - static search(id, groups) - { - // base case - if (groups.groupId == id) - return groups - - if (["and", "or"].includes(groups.type)) - { - for(let item of groups.items) - { - let innerSearch = ArchetypeGroups.search(id, item) - if (innerSearch) - return innerSearch - } - } - } - - delete(id, groups) - { - // base case - if (groups.groupId == id) - return groups - - if (["and", "or"].includes(groups.type)) - { - for(let [index, item] of groups.items.entries()) - { - let innerSearch = this.delete(id, item) - if (innerSearch) - { - groups.items.splice(index, 1) - if (groups.items.length == 1) - groups = groups.items[0] - } - } - } - } - - // Inserts obj into the dest, if dest is an item, create an or container for both of them, if it's a container, simply add it to the list - insert(obj, dest, groups) - { - // base case - if (groups.groupId == dest) - { - if (dest == "root") // Special case where root is the destination - groups.items.push(obj) - return groups - } - - if (["and", "or"].includes(groups.type)) - { - for(let [index, item] of groups.items.entries()) - { - let innerSearch = this.insert(obj, dest, item) - if (innerSearch?.items) // if object is a collection: easy, just add to collection - { - innerSearch.items.push(obj) - } - else if(innerSearch) // If destination is another item, create a collection for that item and the one being added - { - groups.items[index] = {type : 'or', items: [groups.items[index], obj], groupId : randomID()} - } - } - } - } - - - // Removes empty container objects (and/or with 0 items) and changes container objects that have 1 item to simply be that element - clean(groups) { - if (["and", "or"].includes(groups.type)) - { - for (let [index,item] of groups.items.entries()) - { - let action = this.clean(item) - if (action == "remove") - groups.items.splice(index, 1) - if (action == "single") - groups.items[index] = groups.items[index].items[0] - } - if (groups.items.length == 1) - { - groups = groups.items[0] - return "single" - } - else if (groups.items.length == 0 && groups.groupId != "root") - return "remove" - } - return "keep" - } - - - activateListeners(html) - { - super.activateListeners(html) - - - html.find(".connector").click(async ev => { - let id = $(ev.currentTarget).parents(".group-list").attr("data-id") - let groups = duplicate(this.object.groups); - let obj = ArchetypeGroups.search(id, groups); - obj.type = obj.type == "and" ? "or" : "and"; // flip and/or - await this.object.update({"system.groups" : groups}) - this.render(true); - }) - - html.on("dragenter", ".group-list,.wargear", ev => { - ev.currentTarget.classList.add("dragenter") - $(ev.currentTarget).parents(".dragenter").each((i, e) => e.classList.remove("dragenter")) - }) - - html.on("dragleave", ".group-list,.wargear", ev => { - ev.currentTarget.classList.remove("dragenter") - let parent = $(ev.currentTarget).parents(".group-list")[0] - if (parent) - parent.classList.add("dragenter") - }) - } - -} \ No newline at end of file diff --git a/scripts/apps/bug-report.js b/scripts/apps/bug-report.js deleted file mode 100644 index 69a920f..0000000 --- a/scripts/apps/bug-report.js +++ /dev/null @@ -1,133 +0,0 @@ -export default class BugReportFormWNG extends Application { - - constructor(app) { - super(app) - - this.endpoint = "https://aa5qja71ih.execute-api.us-east-2.amazonaws.com/Prod/wng" - - this.domains = [ - "Wrath & Glory System", - "Core Module", - "Forsaken System Module", - "Litanies of the Lost", - "Redacted Records I", - "Church of Steel", - ] - - this.domainKeys = [ - "wrath-and-glory", - "wng-core", - "wng-forsaken", - "wng-litanies", - "wng-records1", - "wng-cos", - ] - - this.domainKeysToLabel = { - "wrath-and-glory" : "system", - "wng-core" : "core", - "wng-forsaken" : "forsaken", - "wng-litanies" : "litanies", - "wng-records1" : "records1", - "wng-cos" : "cos" - } - } - - static get defaultOptions() { - const options = super.defaultOptions; - options.id = "bug-report"; - options.template = "systems/wrath-and-glory/template/apps/bug-report.hbs" - options.classes.push("wrath-and-glory", "wng-bug-report"); - options.resizable = true; - options.width = 600; - options.minimizable = true; - options.title = "W&G Bug Report" - return options; - } - - - getData() { - let data = super.getData(); - data.domains = this.domains; - data.name = game.settings.get("wrath-and-glory", "bugReportName") - return data; - } - - submit(data) { - fetch(this.endpoint, { - method: "POST", - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - title: data.title, - body: data.description, - assignees: ["moo-man"], - labels : data.labels - }) - }) - .then(res => { - if (res.status == 201) - { - ui.notifications.notify(game.i18n.localize("ImperialPost")) - res.json().then(json => { - console.log("%c%s%c%s", 'color: #8a2e2a', `ADEPTUS ADMINISTRATUM:`, 'color: unset', ` Thank you for your submission. If you wish to monitor or follow up with additional details like screenshots, you can find your issue here: ${json.hbs_url}`) - }) - } - else - { - ui.notifications.error(game.i18n.localize("ImperialPostError")) - console.error(res) - } - - }) - .catch(err => { - ui.notifications.error(game.i18n.localize("Something went wrong")) - console.error(err) - }) - } - - activateListeners(html) { - - html.find(".bug-submit").click(ev => { - let data = {}; - let form = $(ev.currentTarget).parents(".bug-report")[0]; - data.domain = $(form).find(".domain")[0].value - data.title = $(form).find(".bug-title")[0].value - data.description = $(form).find(".bug-description")[0].value - data.issuer = $(form).find(".issuer")[0].value - let label = $(form).find(".issue-label")[0].value; - - if (!data.domain || !data.title || !data.description) - return ui.notifications.error(game.i18n.localize("BugReport.ErrorForm")) - if (!data.issuer) - return ui.notifications.error(game.i18n.localize("BugReport.ErrorName1")) - - data.title = `[${this.domains[Number(data.domain)]}] ${data.title}` - data.description = data.description + `
**From**: ${data.issuer}` - - data.labels = [this.domainKeysToLabel[this.domainKeys[Number(data.domain)]]] - - if (label) - data.labels.push(label); - - game.settings.set("wrath-and-glory", "bugReportName", data.issuer); - - let officialModules = Array.from(game.modules).filter(m => this.domainKeys.includes(m.id)) - - let versions = `
wrath-and-glory: ${game.system.version}` - - for (let mod of officialModules) - { - if (mod.active) - versions = versions.concat(`
${mod.id}: ${mod.version}`) - } - - data.description = data.description.concat(versions); - data.description += `
Active Modules: ${game.modules.contents.filter(i => i.active).map(i => i.id).filter(i => !this.domainKeys.includes(i)).join(", ")}` - - this.submit(data) - this.close() - }) - } -} \ No newline at end of file diff --git a/scripts/apps/character-creation.js b/scripts/apps/character-creation.js index 318c213..b925ad2 100644 --- a/scripts/apps/character-creation.js +++ b/scripts/apps/character-creation.js @@ -1,17 +1,16 @@ import WNGUtility from "../common/utility.js"; import { WrathAndGloryItem } from "../document/item.js"; -import ArchetypeGroups from "./archetype-groups.js"; -import FilterResults from "./filter-results.js"; export default class CharacterCreation extends FormApplication { constructor(object) { super(object) this.actor = object.actor; this.archetype = object.archetype.clone(); - this.species = game.wng.utility.findItem(object.archetype.species.id, "species") - this.faction = game.wng.utility.findItem(object.archetype.faction.id, "faction") + this.species = object.archetype.species.document; + this.faction = object.archetype.faction.document; + this.archetypeAbility = object.archetype.ability.document; this.speciesAbilities = []; // Must be awaited if species is a promise - this.archetypeAbility = game.wng.utility.findItem(this.archetype.ability.id, "ability") + this.wargear = this.archetype.system.wargear.clone(); this.addedTalents = []; } @@ -62,7 +61,7 @@ export default class CharacterCreation extends FormApplication { this.species = await this.species; this.faction = await this.faction; this.archetypeAbility = await this.archetypeAbility - this.speciesAbilities = await Promise.all(this.species.abilities.map(i => game.wng.utility.findItem(i.id, "ability"))) + this.speciesAbilities = await this.species.system.abilities.awaitDocuments() await this.initializeCharacter() @@ -111,26 +110,29 @@ export default class CharacterCreation extends FormApplication { let html = "" if (["and", "or"].includes(group.type)) { let connector = `${group.type}` - html += `
` - html += group.items.map(g => { + html += `
` + html += group.options.map(g => { let groupHTML = groupToHTML(g) if (group.type == "or") { - groupHTML = `
${groupHTML}
` + groupHTML = `
${groupHTML}
` } return groupHTML - }).join(group.groupId == "root" ? "" : connector) + }).join(group.id == "root" ? "" : connector) html += "
" return html } - else if (group.type == "item" || (group.type == "generic" && group.filters.length == 0)) { - return `
${group.name}
` - } - else if (group.type == "generic") { - return `
${group.name}
` + else { + if (group.content.type == "filter") { + return `
${group.content.name}
` + } + else + { + return `
${group.content.name}
` + } } } - html += groupToHTML(ArchetypeGroups.groupIndexToObjects(this.archetype.system.groups, this.archetype), html) + html += groupToHTML(this.wargear.compileTree().structure) return html; } @@ -140,21 +142,18 @@ export default class CharacterCreation extends FormApplication { * @param {Object} filter Filter details (to replace with object) * @param {String} id ID of item chosen */ - async chooseWargear(filter, id) + async chooseWargear(option, document) { - let element = this.element.find(`.generic[data-id=${filter.groupId}]`)[0] - let group = ArchetypeGroups.search(filter.groupId, this.archetype.system.groups) - let wargearObject = this.archetype.wargear[group.index] - let item = await game.wng.utility.findItem(id) - - if (element && item) + let element = this.element.find(`.placeholder[data-id=${option.id}]`)[0] + + if (element && document) { - element.classList.remove("generic") - element.textContent = item.name + element.classList.remove("placeholder") + element.textContent = document.name - wargearObject.type = "item" - wargearObject.name = item.name; - wargearObject.id = item.id; + option.type = "item"; + option.name = document.name; + option.documentId = document.id; } } @@ -179,21 +178,21 @@ export default class CharacterCreation extends FormApplication { let faction = this.faction?.toObject() if (faction) { + let effectId = formData["background-bonus"]; if (formData["background-bonus"]) { - faction.effects = faction.effects.filter(e => e._id == formData["background-bonus"]) - - if (faction.effects[0].changes[0].mode == 0) + let effect = faction.effects.find(i => i._id == effectId) + if (effect.changes[0].mode == 0) { - let key = faction.effects[0].changes[0].key + let key = effect.changes[0].key // Some faction effects specify custom mode, specifically for wealth and influence, this should be a one time change instead of an effect this.character.updateSource({[key] : getProperty(this.character, key) + 1}) faction.effects = []; } else { - faction.effects[0].transfer = true; - faction.effects[0].name = $(ev.target).find(".background-bonus").children("option").filter(":selected").text() + // faction.effects[0].transfer = true; + effect.name = $(ev.target).find(".background-bonus").children("option").filter(":selected").text() // Gross but whatever, uses the selected text (with background name appended) as the effect name } } @@ -204,13 +203,19 @@ export default class CharacterCreation extends FormApplication { let chosenGoal = $(this.form).find(".goal .active")[0] if(chosenOrigin) { - faction.system.backgrounds.origin[chosenOrigin.dataset.index || 0].active = true; + let bg = faction.system.backgrounds.origin[chosenOrigin.dataset.index || 0] + bg.active = true; + bg.chosen = effectId == bg.effect.id; } if(chosenAccomplishment) { - faction.system.backgrounds.accomplishment[chosenAccomplishment.dataset.index || 0].active = true; + let bg = faction.system.backgrounds.accomplishment[chosenAccomplishment.dataset.index || 0] + bg.active = true; + bg.chosen = effectId == bg.effect.id; } if(chosenGoal) { - faction.system.backgrounds.goal[chosenGoal.dataset.index || 0].active = true; + let bg = faction.system.backgrounds.goal[chosenGoal.dataset.index || 0] + bg.active = true; + bg.chosen = effectId == bg.effect.id; } } else @@ -251,19 +256,20 @@ export default class CharacterCreation extends FormApplication { errors.push("Background bonus not selected") } - let unresolvedGenerics = false; + let unresolvedplaceholders = false; // WARGEAR - this.element.find(".wargear-item.generic").each((i, e) => { + this.element.find(".wargear-item.placeholder").each((i, e) => { if (!this.isDisabled(e)) { let id = e.dataset.id - let group = ArchetypeGroups.search(id, this.archetype.system.groups) - let wargear = this.archetype.wargear[group.index] - if (wargear.filters.length) - unresolvedGenerics = true; + let option = this.wargear.options.find(i => i.id == id); + if (option.type == "filter" && option.filters.length) + { + unresolvedplaceholders = true; + } } }) - if (unresolvedGenerics) - errors.push("Unresolved Generic Items") + if (unresolvedplaceholders) + errors.push("Unresolved placeholder Items") if (errors.length) { @@ -302,30 +308,21 @@ export default class CharacterCreation extends FormApplication { // Take the wargear of the archetype, check if it has the disabled class in the form (if it was not chosen), create a temporary item retrieveChosenWargear() { - let wargear = this.archetype.wargear; // Filter wargear by whether it has a disabled ancestor, if not, add to actor - return wargear.filter(e => { - let element = this.element.find(`.wargear-item[data-id='${e.groupId}']`) + return this.archetype.wargear.options.filter(e => { + let element = this.element.find(`.wargear-item[data-id='${e.id}']`) let enabled = element.parents(".disabled").length == 0 return enabled }).map(async e => { - let item; - // If chosen item is still generic, create a basic item for it - if (e.type == "generic") { - item = new WrathAndGloryItem({ type: "gear", name: e.name, img: "modules/wng-core/assets/icons/gear/gear.webp" }) + let item = await this.wargear.getOptionDocument(e.id) + if (e.type != "placeholder") + { + return item; } - else if (e.id) { - // Create a temp item and incorporate the diff - let document = await game.wng.utility.findItem(e.id) - if (document) - item = new WrathAndGloryItem(mergeObject(document.toObject(), e.diff, { overwrite: true })) - else if (e.name) - { - ui.notifications.warn(`Could not find ${e.name}, creating generic`) - item = new WrathAndGloryItem({ type: "gear", name: e.name, img: "modules/wng-core/assets/icons/gear/gear.webp" }) - } + else + { + return new Item.implementation(item) } - return item }).filter(i => i); } @@ -371,16 +368,20 @@ export default class CharacterCreation extends FormApplication { html.find(".wargear-item").click(async ev => { let id = ev.currentTarget.dataset.id - let group = ArchetypeGroups.search(id, this.archetype.system.groups) - let wargear = this.archetype.wargear[group.index] + let option = this.wargear.options.find(i => i.id == id); - if (wargear.type == "generic" && wargear.filters.length) + if (option.type == "filter") + { + let document = await this.wargear.getOptionDocument(id) + this.chooseWargear(option, document); + } + else { - new FilterResults({wargear, app: this}).render(true) + let document = await this.wargear.getOptionDocument(id) + document.sheet.render(true, {editable : false}); } - else if (wargear.type == "item") - new WrathAndGloryItem((await game.wng.utility.findItem(wargear.id)).toObject()).sheet.render(true, {editable: false}) }) + html.find(".background").click(ev => { @@ -579,12 +580,12 @@ export default class CharacterCreation extends FormApplication { { let parent = $(element).closest(".wargear-selection"); let group = parent.find(".wargear-group,.wargear-item") - let groupId = group.attr("data-id") + let id = group.attr("data-id") let choice = parent.closest(".choice") // Disable siblings choice.children().each((i, e) => { - if (e.dataset.id != groupId) { + if (e.dataset.id != id) { this.disableElements(e) } }) diff --git a/scripts/apps/effect-config.js b/scripts/apps/effect-config.js new file mode 100644 index 0000000..158dc32 --- /dev/null +++ b/scripts/apps/effect-config.js @@ -0,0 +1,12 @@ +export default class WrathAndGloryActiveEffectConfig extends WarhammerActiveEffectConfig { + + + effectKeysTemplate = "systems/wrath-and-glory/template/apps/effect-key-options.hbs"; + + static get defaultOptions() + { + const options = super.defaultOptions; + options.classes = options.classes.concat(["wrath-and-glory"]); + return options; + } +} \ No newline at end of file diff --git a/scripts/apps/effect-script.js b/scripts/apps/effect-script.js deleted file mode 100644 index 1821f6f..0000000 --- a/scripts/apps/effect-script.js +++ /dev/null @@ -1,70 +0,0 @@ -export default class EffectScriptConfig extends FormApplication { - - static get defaultOptions() { - return mergeObject(super.defaultOptions, { - id: "effect-script-config", - template: "systems/wrath-and-glory/template/apps/effect-script.hbs", - height: 400, - width: 500, - title: "Effect Script Config", - resizable: true - - }) - } - - getData() { - let data = super.getData() - data.script = this.change?.script - data.description = this.change?.description - data.hide = this.change?.hide - data.aceActive = game.modules.get("acelib")?.active; - return data - } - - _updateObject(event, formData) { - - if (this.scriptEditor) - { - formData.script = this.scriptEditor.getValue(); - } - - if (this.hideEditor) - { - formData.hide = this.hideEditor.getValue(); - } - - let script = formData.script || ""; - let description = formData.description || ""; - let hide = formData.hide || ""; - - return this.object.effect.update({[`flags.wrath-and-glory.changeCondition.${this.object.index}`] : {script, description, hide}}) - } - - activateListeners(html) - { - super.activateListeners(html); - - try - { - if (game.modules.get("acelib")?.active) - { - this.scriptEditor = ace.edit(html.find(".ace-editor.script input")[0]); - this.scriptEditor.setOptions(mergeObject(ace.userSettings, {mode : "ace/mode/js", keyboardHandler : "ace/mode/vscode"})) - this.scriptEditor.setValue(this.change?.script); - - this.hideEditor = ace.edit(html.find(".ace-editor.hide input")[0]); - this.hideEditor.setOptions(mergeObject(ace.userSettings, {mode : "ace/mode/js", keyboardHandler : "ace/mode/vscode"})) - this.hideEditor.setValue(this.change?.hide); - } - } - catch(e) - { - console.error("Error initializing ACE Editor: " + e) - } - } - - get change() - { - return this.object.effect.changeConditionals[this.object.index] - } -} \ No newline at end of file diff --git a/scripts/apps/filter-results.js b/scripts/apps/filter-results.js deleted file mode 100644 index 9028c5c..0000000 --- a/scripts/apps/filter-results.js +++ /dev/null @@ -1,136 +0,0 @@ - -export default class FilterResults extends FormApplication { - constructor(object) { - super(object) - } - - static get defaultOptions() { - let options = super.defaultOptions; - options.classes.push("filter-results"); - options.title = "Filter Results"; - options.template = "systems/wrath-and-glory/template/apps/filter-results.hbs", - options.width = 300, - options.height = 800, - options.resizable = true - return options; - } - - async getData() { - let data = super.getData(); - let filters = this.object.wargear.filters - let items = await this.getAllItems(); - items = this.applyFilters(items, filters) - data.items = items; - return data - } - - - async _updateObject(event) { - let choice = $(event.currentTarget).find(".active"); - let choiceId = choice.attr("data-id") - - if (choice) - { - this.object.app.chooseWargear(this.object.wargear, choiceId) - } - } - - async getAllItems() { - let items = game.items.contents - - let packs = await Promise.all(game.packs.filter(p => p.metadata.type == "Item").map(i => i.getDocuments())) - - packs.forEach(p => { // Remove duplicates (match by ID, don't show compendium item if world item already listed) - items = items.concat(p.filter(i => !items.find(existing => existing.id == i.id))); - }) - - return items.sort((a, b) => a.name > b.name ? 1 : -1) - } - - applyFilters(items, filters) { - filters.forEach(f => { - items = items.filter(i => { - let itemData = i.toObject(); - itemData.hasKeyword = (keyword) => { - let keywords = itemData.system.keywords - if (!keywords) return false - - if (!Array.isArray(keywords)) - keywords = keywords.split(",") - - keywords = keywords.map(i => i.trim().toLowerCase()) - return keywords.includes(keyword.toLowerCase()) - } - - if (f.property.includes("(")) - { - let split = f.property.split("(") - split[1] = split[1].substring(0, split[1].length - 1) - split[1] = split[1].split("").filter(i => i != '"').join("") - return itemData[split[0]](split[1]) // Invoke function - } - - let propValue = getProperty(itemData, f.property) - let testValue = f.value; - let test = f.test - - // Convert rarity to a number so that ranges of rarities can be used - if (f.property == "system.rarity") - { - propValue = this.rarityNumber[propValue] - testValue = this.rarityNumber[testValue] - } - - if ([propValue, test, testValue].includes(undefined)) - return false - return (0, eval)(`"${propValue}" ${this.comparisons[test]} "${testValue}"`) - }) - }) - return items - } - - get comparisons() { - return { - "lt": "<", - "le": "<=", - "eq": "==", - "gt": ">", - "ge": ">=" - } - } - - get rarityNumber() { - return { - "common": 1, - "uncommon": 2, - "rare": 3, - "very-rare": 4, - "unique": 5 - } - } - - activateListeners(html) - { - super.activateListeners(html); - - html.find(".document-name").click(ev => { - let list = $(ev.currentTarget).parents(".directory-list") - - list.find(".active").each((i, e) => { - e.classList.remove("active") - }) - - let document = $(ev.currentTarget).parents(".document")[0] - document.classList.add("active") - }) - - html.find(".document-name").contextmenu(ev => { - let document = $(ev.currentTarget).parents(".document") - let id = document.attr("data-id") - - game.items.get(id).sheet.render(true, {editable: false}) - }) - } - - -} \ No newline at end of file diff --git a/scripts/apps/item-dialog.js b/scripts/apps/item-dialog.js deleted file mode 100644 index 1d5572d..0000000 --- a/scripts/apps/item-dialog.js +++ /dev/null @@ -1,71 +0,0 @@ - -export default class ItemDialog extends Dialog { - static template = "systems/wrath-and-glory/template/apps/item-dialog.hbs" - - static get defaultOptions() { - let options = super.defaultOptions - options.width= 300, - options.height= 800, - options.resizable = true - options.classes.push("item-dialog") - return options - } - - - static async create(items= [], number, options) - { - return new Promise(async (resolve) => { - - let html = await renderTemplate(this.template, {items, number}) - new this({ - title : "Choose Items", - content : html, - number, - items, - buttons : { - submit : - { - label : "Submit", - callback : (dlg) => { - resolve(Array.from(dlg.find(".active")).map(el => items[el.dataset.index])) - } - } - }, - close : () => {resolve([])} - }).render(true, options) - }) - } - - activateListeners(html) - { - super.activateListeners(html); - - html.find(".document-name").click(ev => { - let document = $(ev.currentTarget).parents(".document")[0] - let list = $(ev.currentTarget).parents(".directory-list") - - let choices = list.find(".active").length - - if (!document.classList.contains("active")) - { - if (Number.isNumeric(this.data.number) && choices >= this.data.number) - return - - else document.classList.add("active") - } - else // Already active, remove active - { - document.classList.remove("active") - } - }) - - html.find(".document-name").contextmenu(ev => { - let document = $(ev.currentTarget).parents(".document") - let index = document.attr("data-index") - - this.data.items[index].sheet.render(true, {editable: false}) - }) - } - - -} diff --git a/scripts/apps/item-traits.js b/scripts/apps/item-traits.js index f4cbac5..de8e87c 100644 --- a/scripts/apps/item-traits.js +++ b/scripts/apps/item-traits.js @@ -20,7 +20,7 @@ export default class ItemTraits extends FormApplication data.traits = Object.keys(this.object.system.traitsAvailable).map(i => { let existing = this.object._source.system.traits.list.find(t => t.name == i) if (this.object.type == "weaponUpgrade" || this.object.type == "ammo") - existing = this.object.system.traits.find(t => t.name == i && t.type == this.options.type) // Don't include traits from the other type for existing + existing = this.object.system.traits.list.find(t => t.name == i && t.type == this.options.type) // Don't include traits from the other type for existing return { display : this.object.system.traitsAvailable[i], key : i, @@ -44,7 +44,7 @@ export default class ItemTraits extends FormApplication let newTraits = [] if (this.object.type == "weaponUpgrade" || this.object.type == "ammo") { - newTraits = this.object.system.traits.filter(i => i.type != this.options.type) // Retain traits from the other type + newTraits = this.object.system.traits.list.filter(i => i.type != this.options.type) // Retain traits from the other type } for (let key in formData) { @@ -63,7 +63,7 @@ export default class ItemTraits extends FormApplication newTraits.push(traitObj) } } - this.object.update({"system.traits" : newTraits}) + this.object.update({"system.traits.list" : newTraits}) } parseCustomTraits(string) @@ -75,7 +75,7 @@ export default class ItemTraits extends FormApplication for (let match of matches) { - traits.push({ + traits.list.push({ name : match[1].trim().slugify(), custom : true, display : match[1].trim(), @@ -90,7 +90,7 @@ export default class ItemTraits extends FormApplication constructCustomString(traits) { let customString = `` - let customTraits = traits.filter(i => i.custom) + let customTraits = traits.list.filter(i => i.custom) customTraits.forEach(t => { customString += `${t.display} : ${t.description} |` diff --git a/scripts/apps/mob-config.js b/scripts/apps/mob-config.js new file mode 100644 index 0000000..d67193d --- /dev/null +++ b/scripts/apps/mob-config.js @@ -0,0 +1,90 @@ +export class MobConfig extends WarhammerSheetMixinV2(HandlebarsApplicationMixin(ApplicationV2)) +{ + static DEFAULT_OPTIONS = { + tag : "form", + classes: ["wrath-and-glory", "mob-config", "warhammer"], + window : { + }, + position: { + height: 300 + }, + form: { + handler: this.submit, + submitOnChange: true, + closeOnSubmit: false + }, + actions : { + listDelete : this._onListDelete, + } + }; + + static PARTS = { + form: { + template: "systems/wrath-and-glory/template/apps/mob-config.hbs" + } + }; + + get title() + { + return `Mob Abilities: ${this.document.name}`; + } + + constructor(document, options) + { + super(options); + this.document = document; + } + + async _prepareContext(options) + { + let context = await super._prepareContext(options); + context.abilities = this.document.system.mob.abilities; + return context; + } + + static async submit(event, form, formData) + { + console.log(formData.object); + } + + async _onDropItem(data, event) + { + let item = await Item.implementation.fromDropData(data); + if (item.parent.uuid == this.document.uuid && !item.system.isMobAbility) + { + await this.document.update({"system.mob" : this.document.system.mob.abilities.add(item)}); + // this.document.update(this.document.system.mob.abilities.add(item)); + this.render(true); + } + } + + static async _onListEdit(ev) + { + let index = this._getIndex(ev); + let value = ev.target.value; + + await this.document.update({"system.mob" : this.document.system.mob.abilities.edit(index, value, "requiredMob")}); + this.render(true); + } + + static async _onListDelete(ev) + { + let index = this._getIndex(ev); + + await this.document.update({"system.mob" : this.document.system.mob.abilities.remove(index)}); + this.render(true); + } + + + _canDragStart(selector) + { + return true; + } + + + _canDragDrop(selector) + { + return true; + } + +} \ No newline at end of file diff --git a/scripts/apps/module-initialization.js b/scripts/apps/module-initialization.js deleted file mode 100644 index ff8d8e8..0000000 --- a/scripts/apps/module-initialization.js +++ /dev/null @@ -1,202 +0,0 @@ - - -export default class ModuleInitializer extends Dialog { - - constructor(module, title, html) { - super({ - title: title, - content: html, - module: game.modules.get(module), - buttons: { - initialize: { - label: "Initialize", - callback: async () => { - game.settings.set(module, "initialized", true) - await this.initialize() - ui.notifications.notify(game.modules.get(module).title + ": Initialization Complete") - } - }, - update: { - label: "Update", - condition : game.settings.get(module, "initialized"), - callback: async () => { - let updater = await game.wng.apps.ModuleUpdater.create(game.modules.get(module), this) - updater.render(true) - } - }, - delete : { - label: "Delete", - condition : game.settings.get(module, "initialized"), - callback: async () => { - this.deleteModuleContent(module); - } - }, - no: { - label: "No", - callback: () => { - game.settings.set(module, "initialized", true) - ui.notifications.notify("Skipped Initialization.") - } - } - } - }) - } - - rootFolders = {} - - async initialize() { - - let packList = this.data.module.flags.initializationPacks - - for (let pack of packList.map(p => game.packs.get(p))) - { - await this.createFolders(pack); - let documents = await pack.getDocuments(); - try { - switch (documents[0].documentName) { - case "Actor": - ui.notifications.notify(this.data.module.title + ": Initializing Actors") - await this.createOrUpdateDocuments(documents, game.actors) - break; - case "Item": - ui.notifications.notify(this.data.module.title + ": Initializing Items") - await this.createOrUpdateDocuments(documents, game.items) - break; - case "JournalEntry": - ui.notifications.notify(this.data.module.title + ": Initializing Journals") - await this.createOrUpdateDocuments(documents, game.journal) - break; - case "RollTable": - ui.notifications.notify(this.data.module.title + ": Initializing Tables") - await this.createOrUpdateDocuments(documents, game.tables) - break; - case "Scene": - ui.notifications.notify(this.data.module.title + ": Initializing Scenes") - await this.createOrUpdateDocuments(documents, game.scenes) - break; - } - } - catch(e) - { - console.error(e) - } - - } - } - - createFolders(pack) - { - let root = game.modules.get(pack.metadata.packageName).flags.folder - root.type = pack.metadata.type; - root._id = randomID(); - let packFolders = pack.folders.contents.map(f => f.toObject()); - for(let f of packFolders) - { - if (!f.folder) - { - f.folder = root._id; - } - } - this.rootFolders[pack.metadata.id] = root._id; - return Folder.create(packFolders.concat(root), {keepId : true}) - } - - async createOrUpdateDocuments(documents, collection, ) - { - let existingDocuments = documents.filter(i => collection.has(i.id)) - let newDocuments = documents.filter(i => !collection.has(i.id)) - await collection.documentClass.create(this._addFolder(newDocuments)) - if (existingDocuments.length) - { - console.log("Pre Existing Documents: ", null, {args : existingDocuments}) - existingDocuments = await new Promise(resolve => new ModuleDocumentResolver(existingDocuments, {resolve}).render(true)); - console.log("Post Existing Documents: ", null, {args : existingDocuments}) - } - this._addFolder(existingDocuments) - for (let doc of existingDocuments) - { - let existing = collection.get(doc.id) - await existing.update(doc.toObject()) - ui.notifications.notify(`Updated existing document ${doc.name}`) - } - } - - _addFolder(documents) - { - return documents.map(d => { - if (!d.folder) - { - d.updateSource({folder : this.rootFolders[d.pack]}); - } - return d; - }) - } - - async deleteModuleContent(id) - { - let proceed = await Dialog.confirm({ - title : game.i18n.localize("UPDATER.DeleteModuleContent"), - content : game.i18n.format("UPDATER.DeleteModuleContentPrompt", {id}), - yes : () => {return true}, - no : () => {return false}, - }) - if (proceed) - { - ui.notifications.notify(this.data.module.title + ": Deleting Scenes") - let moduleScenes = game.scenes.filter(doc => doc.flags[id]); - moduleScenes.forEach(doc => { - doc.folder?.folder?.delete(); - doc.folder?.delete()}) - Scene.deleteDocuments(moduleScenes.map(doc => doc.id)); - - ui.notifications.notify(this.data.module.title + ": Deleting Actors") - let moduleActors = game.actors.filter(doc => doc.flags[id] && !doc.hasPlayerOwner) - moduleActors.forEach(doc => { - doc.folder?.folder?.delete(); - doc.folder?.delete()}) - Actor.deleteDocuments(moduleActors.map(doc => doc.id)); - - ui.notifications.notify(this.data.module.title + ": Deleting Items") - let moduleItems = game.items.filter(doc => doc.flags[id]) - moduleItems.forEach(doc => { - doc.folder?.folder?.delete(); - doc.folder?.delete()}) - Item.deleteDocuments(moduleItems.map(doc => doc.id)); - - ui.notifications.notify(this.data.module.title + ": Deleting Journals") - let moduleJournals = game.journal.filter(doc => doc.flags[id]) - moduleJournals.forEach(doc => { - doc.folder?.folder?.delete(); - doc.folder?.delete()}) - JournalEntry.deleteDocuments(moduleJournals.map(doc => doc.id)); - - ui.notifications.notify(this.data.module.title + ": Deleting Tables") - let moduleTables = game.tables.filter(doc => doc.flags[id]) - moduleTables.forEach(doc => { - doc.folder?.folder?.delete(); - doc.folder?.delete()}) - RollTable.deleteDocuments(moduleTables.map(doc => doc.id)); - } - } -} - - -class ModuleDocumentResolver extends FormApplication -{ - static get defaultOptions() { - const options = super.defaultOptions; - options.resizable = true; - options.height = 600 - options.width = 400 - options.template = "systems/wrath-and-glory/template/apps/document-resolver.hbs"; - options.classes.push("document-resolver"); - options.title = game.i18n.localize("UPDATER.ResolveDuplicates"); - return options; - } - - - _updateObject(ev, formData) - { - this.options.resolve(this.object.filter(i => formData[i.id])) - } -} \ No newline at end of file diff --git a/scripts/apps/module-updater.js b/scripts/apps/module-updater.js deleted file mode 100644 index 9446aac..0000000 --- a/scripts/apps/module-updater.js +++ /dev/null @@ -1,141 +0,0 @@ - - -export default class ModuleUpdater extends Dialog { - - constructor(module, html) - { - - super({ - title: `${game.i18n.localize("UpdaterTitle1")} ${module.title} ${game.i18n.localize("UpdaterTitle2")}`, - content: html, - module, - buttons: - { - update: - { - label: game.i18n.localize("Update"), - callback: html => { - if (!game.settings.get(module.id, "initialized")) - return ui.notifications.notify(game.i18n.localize("UPDATER.Error")) - let settings = this.getUpdateSettings(html) - this.updateImportedContent(settings) - } - } - }, - default: "update" - }) - } - - static async create(module) - { - let html = await renderTemplate("systems/wrath-and-glory/template/apps/module-updater.hbs", module) - return new this(module, html) - } - - getUpdateSettings(html) - { - let updateSettings = {} - updateSettings.actors = html.find('[name="actors"]').is(':checked') - updateSettings.journals = html.find('[name="journals"]').is(':checked') - updateSettings.items = html.find('[name="items"]').is(':checked') - updateSettings.tables = html.find('[name="tables"]').is(':checked') - updateSettings.scenes = html.find('[name="scenes"]').is(':checked') - updateSettings.excludeNameChange = html.find('[name="excludeNameChange"]').is(':checked') - return updateSettings - } - - async updateImportedContent(settings) - { - let documents = await this.getDocuments() - this.count = {created : 0, updated : 0} - for(let type in settings) - { - if (type != "excludeNameChange" && settings[type]) - await this.updateDocuments(documents[type], settings) - } - ui.notifications.notify(`${game.i18n.format("UPDATER.Notification", { created: this.count.created, updated: this.count.updated, name: this.data.module.id, version: this.data.module.version })}`) - - } - - async updateDocuments(documents, settings) - { - if (!documents.length) - return - let toCreate = [] - let toDelete = [] - let documentClass - for (let document of documents) - { - if (!documentClass) - documentClass = CONFIG[document.documentName].documentClass - if (game[document.collectionName].has(document.id)) - { - let existingDoc = game[document.collectionName].get(document.id) - if (!settings.excludeNameChange || (settings.excludeNameChange && document.name == existingDoc.name)) - { - let folder = existingDoc.folder - let permission = existingDoc.permission - toDelete.push(existingDoc.id) - let newDoc = document.toObject() - newDoc.folder = folder; - newDoc.permission = permission - toCreate.push(newDoc) - console.log(`Updated Document ${document.name}`) - this.count.updated++; - } - } - else - { - let folder = document.getFlag(this.data.module.id, "initialization-folder") - folder = game.folders.getName(folder) - let newDoc = document.toObject() - if (folder) - newDoc.folder = folder.id - toCreate.push(newDoc) - console.log(`Imported Document ${document.name}`) - this.count.created++; - } - } - await documentClass.deleteDocuments(toDelete) - let created = await documentClass.createDocuments(toCreate) - - if (documentClass.name == "Scene") - { - created.forEach(async s => { - let thumb = await s.createThumbnail(); - s.update({ "thumb": thumb.thumb }) - }) - } - } - - async getDocuments() - { - let module = this.data.module; - let packs = module.flags.initializationPacks.map(i => game.packs.get(i)) - let documents = { - actors : [], - journals : [], - items : [], - tables : [], - scenes : [] - }; - for (let pack of packs) - { - let docs = await pack.getDocuments(); - switch (pack.metadata.type) - { - case "Actor": documents.actors = documents.actors.concat(docs) - break; - case "JournalEntry": documents.journals = documents.journals.concat(docs) - break; - case "Item": documents.items = documents.items.concat(docs) - break; - case "RollTable": documents.tables = documents.tables.concat(docs) - break; - case "Scene": documents.scenes = documents.scenes.concat(docs) - break; - } - } - return documents - } -} \ No newline at end of file diff --git a/scripts/common/chat.js b/scripts/common/chat.js index 49d32dd..56da94a 100644 --- a/scripts/common/chat.js +++ b/scripts/common/chat.js @@ -1,16 +1,27 @@ +import WNGUtility from "./utility" + export default class WNGChat { static chatListeners(html) { + html.on("click", ".apply-damage", this._onApplyDamage.bind(this)) html.on("click", ".roll-damage", this._onDamageClick.bind(this)) html.on("click", ".roll-wrath", this._onWrathClick.bind(this)) html.on("click", "a.die", this._onDieClick.bind(this)) html.on("click", ".test-effect", this._onEffectClick.bind(this)) - html.on("click", ".invoke-test", this._onTestClick.bind(this)) + html.on("click", ".roll-test", this._onTestClick.bind(this)) html.on("click", ".roll-mutation", this._onMutationClick.bind(this)) html.on("click", ".add-potency", this._onPotencyClick.bind(this)) html.on("click", ".potency-reset", this._onPotencyReset.bind(this)) - html.on("mouseenter", ".target", game.wng.utility.highlightToken.bind(this)) - html.on("mouseleave", ".target", game.wng.utility.unhighlightToken.bind(this)) - html.on("click", ".target", game.wng.utility.focusToken.bind(this)) + // html.on("mouseenter", ".target", WNGUtility.highlightToken.bind(this)) + // html.on("mouseleave", ".target", WNGUtility.unhighlightToken.bind(this)) + // html.on("click", ".target", WNGUtility.focusToken.bind(this)) + html.on("click", ".apply-target", WarhammerChatListeners.onApplyTargetEffect) + html.on("click", ".place-area", WarhammerChatListeners.onPlaceAreaEffect) + } + + static async _onApplyDamage(ev) + { + let damage = game.messages.get($(ev.target).parents(".message").attr("data-message-id")).system.damage; + damage.applyToTargets(); } static _onDamageClick(ev) { @@ -18,7 +29,7 @@ export default class WNGChat { let message = game.messages.get(id) if (message.isAuthor || message.isOwner) { - let test = message.getTest(); + let test = message.system.test; test.rollDamage() } } @@ -26,7 +37,7 @@ export default class WNGChat { static async _onWrathClick(ev) { let id = $(ev.currentTarget).parents(".message").attr("data-message-id") let message = game.messages.get(id) - let test = message.getTest(); + let test = message.system.test; let chatData = {} let table let roll @@ -89,7 +100,7 @@ export default class WNGChat { let id = $(ev.currentTarget).parents(".message").attr("data-message-id") let effectId = $(ev.currentTarget).attr("data-id") let msg = game.messages.get(id) - let test = msg.getTest(); + let test = msg.system.test; let item = test.item let effect = test.getEffect(effectId).toObject() @@ -111,58 +122,32 @@ export default class WNGChat { { let id = $(ev.currentTarget).parents(".message").attr("data-message-id") let msg = game.messages.get(id) - let msgTest = msg.getTest(); - let itemTest = msgTest.result.test; - + let test = msg.system.test; + let options = {resist : [this.key].concat(test?.item?.type || []), resistingTest : test, appendTitle : ` - ${test.item.name}`} if (canvas.tokens.controlled.length) { for (let token of canvas.tokens.controlled) { - let testFunction; - if (itemTest.type == "attribute") - testFunction = token.actor.setupAttributeTest.bind(token.actor) - else if (itemTest.type == "skill") - testFunction = token.actor.setupSkillTest.bind(token.actor) - else - { - testFunction = token.actor.setupGenericTest.bind(token.actor) - itemTest = duplicate(itemTest) - } - - await testFunction(itemTest.specification, {dn: itemTest.dn, resistPower : msgTest.item?.type == "psychicPower"}).then(async test => { - await test.rollTest(); - test.sendToChat() - }) + token.actor.setupTestFromData(test.result.test, options); } } else if (game.user.character) { - let testFunction; - if (itemTest.type == "attribute") - testFunction = game.user.character.setupAttributeTest.bind(game.user.character) - else if (itemTest.type == "skill") - testFunction = game.user.character.setupSkillTest.bind(game.user.character) - else - { - testFunction = game.user.character.setupGenericTest.bind(game.user.character) - itemTest = duplicate(itemTest) - } - - await testFunction(itemTest.specification, {dn: itemTest.dn}).then(async test => { - await test.rollTest(); - test.sendToChat() - }) + game.user.character.setupTestFromData(test.item, options); + } else + { return ui.notifications.error(game.i18n.localize("WARN.NoActorsToTest")) + } } static async _onMutationClick(ev) { let id = $(ev.currentTarget).parents(".message").attr("data-message-id") let msg = game.messages.get(id) - let test = msg.getTest(); + let test = msg.system.test; let table = game.tables.getName("Mutation Severity") let roll = new Roll(table.formula) @@ -174,7 +159,7 @@ export default class WNGChat { { let id = $(ev.currentTarget).parents(".message").attr("data-message-id") let msg = game.messages.get(id) - let test = msg.getTest(); + let test = msg.system.test; test.addAllocation(parseInt(ev.currentTarget.dataset.index)) } @@ -183,7 +168,7 @@ export default class WNGChat { { let id = $(ev.currentTarget).parents(".message").attr("data-message-id") let msg = game.messages.get(id) - let test = msg.getTest(); + let test = msg.system.test; test.resetAllocation() } } \ No newline at end of file diff --git a/scripts/common/config.js b/scripts/common/config.js index 33daf9c..824d197 100644 --- a/scripts/common/config.js +++ b/scripts/common/config.js @@ -272,187 +272,742 @@ WNG.vehicleRoles = { } +WNG.scriptTriggers = { + + equipToggle : "WH.Trigger.EquipToggle", + + preRollTest : "WH.Trigger.preRollTest", + rollTest : "WH.Trigger.rollTest", + + preRollWeaponTest : "WH.Trigger.preRollWeaponTest", + rollWeaponTest : "WH.Trigger.rollWeaponTest", + + preRollPowerTest : "WH.Trigger.preRollPowerTest", + rollPowerTest : "WH.Trigger.rollPowerTest", + + computeDamage : "WH.Trigger.computeDamage", + preComputeDamage : "WH.Trigger.preComputeDamage", + + preTakeDamage : "WH.Trigger.preTakeDamage", + preApplyDamage : "WH.Trigger.preApplyDamage", + + takeDamage : "WH.Trigger.takeDamage", + applyDamage : "WH.Trigger.applyDamage", + +} + +WNG.filterValues = { + "system.rarity" : { + "common": 1, + "uncommon": 2, + "rare": 3, + "very-rare": 4, + "unique": 5 + } +} + +WNG.avoidTestTemplate = "systems/wrath-and-glory/template/apps/effect-avoid-test.hbs", +WNG.effectScripts = {}, + +WNG.logFormat = [`%cW & G` + `%c @MESSAGE`, "color: #DDD;background: #8a2e2a;font-weight:bold", "color: unset"], +WNG.rollClasses = {}, + +WNG.premiumModules = { + "wrath-and-glory" : "Wrath & Glory System", + "wng-core" : "Core Rulebook", + "wng-forsaken" : "Forsaken System Player's Guide", + "wng-litanies" : "Litanies of the Lost", + "wng-records1" : "Redacted Records: Vol. I", + "wng-cos" : "Church of Steel", + "wng-xenos" : "Threat Assessment: Xenos", +} + +WNG.transferTypes = { + document : "WH.TransferType.Document", + damage : "WH.TransferType.Damage", + target : "WH.TransferType.Target", + area : "WH.TransferType.Area", + aura : "WH.TransferType.Aura", + other : "WH.TransferType.Other" +}, + +WNG.placeholderItemData = { + type : "gear", + img : "modules/wng-core/assets/icons/gear/gear.webp" +}, + + WNG.systemEffects = { "wounded" : { id : "wounded", + statuses : ["wounded"], name : "EFFECT.Wounded", - icon : "systems/wrath-and-glory/asset/icons/wounded.svg", - changes : [ - {key: "difficulty.base", mode : 6, value : 1} - ], - flags : { - "wrath-and-glory.changeCondition" : { - 0 : {description : "+1 DN to all Tests", script : "return true"} - } + img : "systems/wrath-and-glory/asset/icons/wounded.svg", + system : { + scriptData : [{ + trigger : "dialog", + script : "args.fields.difficulty += 1", + label : "+1 DN to all Tests", + options : { + activateScript : "return true;", + } + }] } }, "full-defence" : { id : "full-defence", + statuses : ["full-defence"], name : "EFFECT.FullDefence", - icon : "systems/wrath-and-glory/asset/icons/full-defence.svg", + img : "systems/wrath-and-glory/asset/icons/full-defence.svg", changes : [ {key: "system.combat.defence.bonus", mode : 2, value : 1}, ], }, "all-out-attack" : { id : "all-out-attack", + statuses : ["all-out-attack"], name : "EFFECT.AllOutAttack", - icon : "systems/wrath-and-glory/asset/icons/all-out-attack.svg", + img : "systems/wrath-and-glory/asset/icons/all-out-attack.svg", changes : [ - {key: "pool.bonus", mode : 6, value : 2}, {key: "system.combat.defence.bonus", mode : 2, value : -2}, ], - flags : { - "wrath-and-glory.changeCondition" : { - 0 : {description : "+2 bonus dice to melee", script : "return data.weapon && data.weapon.isMelee"} - } + system : { + scriptData : [{ + trigger : "dialog", + script : "args.fields.pool += 2", + label : "+2 bonus dice for Melee attacks", + options : { + hideScript : "return !args.weapon || args.weapon.isRanged", + activateScript : "return args.weapon.isMelee;", + } + }] } }, "halfCover" : { id : "halfCover", + statuses : ["halfCover"], name : "EFFECT.HalfCover", - icon : "systems/wrath-and-glory/asset/icons/half-cover.svg", + img : "systems/wrath-and-glory/asset/icons/half-cover.svg", changes : [ {key: "system.combat.defence.bonus", mode : 2, value : 1}, ] }, "fullCover" : { id : "fullCover", + statuses : ["fullCover"], name : "EFFECT.FullCover", - icon : "systems/wrath-and-glory/asset/icons/full-cover.svg", + img : "systems/wrath-and-glory/asset/icons/full-cover.svg", changes : [ {key: "system.combat.defence.bonus", mode : 2, value : 2}, ] - } + }, + "unbound" : { + id : "unbound", + statuses : ["unbound"], + name : "PSYCHIC_POWER.UNBOUND", + img : "systems/wrath-and-glory/asset/icons/wounded.svg", + system : { + scriptData : [{ + trigger : "dialog", + script : "if (args.fields.level == 'bound') args.fields.level = 'unbound'", + label : "Unbound", + options : { + hideScript : "return !args.power", + activateScript : "return true;", + } + }] + } + }, + "transcendent" : { + id : "transcendent", + statuses : ["transcendent"], + name : "PSYCHIC_POWER.TRANSCENDENT", + img : "systems/wrath-and-glory/asset/icons/wounded.svg", + system : { + scriptData : [{ + trigger : "dialog", + script : "if (args.fields.level == 'bound' || args.fields.level == 'unbound') args.fields.level = 'transcendent'", + label : "Transcendent", + options : { + hideScript : "return !args.power", + activateScript : "return true;", + } + }] + } + }, } +WNG.traitEffects = { + // Qualities + agonising: { + name : "TRAIT.Agonising", + system : { + transferData : { + documentType : "Item" + }, + scriptData : [{ + label : "Agonising", + trigger : "applyDamage", + script : "if (!args.actor.hasCondition('exhausted')) args.modifiers.shock.push({label : this.effect.name, value : args.wounds})", + }, + ], + } + }, + arc: { + name : "TRAIT.Arc", + system : { + transferData : { + documentType : "Item" + }, + scriptData : [{ + label : "Attacking a Vehicle", + trigger : "dialog", + script : "args.fields.ed.value += parseInt(this.item.traitList.arc.rating)", + options : { + hideScript : "return args.target && args.target.type != 'vehicle'", + activateScript : "return args.target?.type == 'vehicle'", + } + }, + ], + } + }, + assault: { + name : "TRAIT.Assault", + system : { + transferData : { + documentType : "Item" + }, + scriptData : [{ + label : "Attacking while Sprinting", + trigger : "dialog", + script : "args.fields.difficulty += 2" + }, + ], + } + }, + brutal: { + name : "TRAIT.Brutal", + system : { + transferData : { + documentType : "Item" + }, + scriptData : [{ + label : "Brutal", + trigger : "dialog", + script : "args.fields.damageDice.values.threes += 1; args.fields.damageDice.values.fives += 1;", + options : { + activateScript: "return true;" + } + }, + ], + } + }, + force: { + name : "TRAIT.Force", + system : { + transferData : { + documentType : "Item" + }, + scriptData : [{ + label : "Force Weapon", + trigger : "dialog", + script : "args.fields.damage += Math.ceil(args.actor.system.attributes.willpower.total / 2)", + options : { + hideScript : "return !args.actor.hasKeyword('PSYKER');", + activateScript: "return args.actor.hasKeyword('PSYKER');" + } + }, + { + label : "Force Weapon (Not a Psyker)", + trigger : "dialog", + script : "args.fields.damage -= 2", + options : { + hideScript : "return args.actor.hasKeyword('PSYKER');", + activateScript: "return !args.actor.hasKeyword('PSYKER');" + } + } + ], + } + }, + flamer: { + name : "TRAIT.Flamer", + system : { + transferData : { + documentType : "Item" + }, + scriptData : [ + { + label : "On Fire", + trigger : "applyDamage", + script : "args.actor.addCondition('onFire')", + } + ], + } + }, + heavy: { + name : "TRAIT.Heavy", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "Entangle", + trigger : "dialog", + script : "args.fields.difficulty += 2;", + options : { + // TODO add prone? + hideScript : "return args.actor.system.attributes.strength.total >= this.item.traitList.heavy.value", + activateScript : "return args.actor.system.attributes.strength.total < this.item.traitList.heavy.value" + } + }] + } + + }, + inflict: { + name : "TRAIT.Inflict", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "Inflict Condition", + trigger : "applyDamage", + script : "//TODO", + }] + } + }, + kustom: { + name : "TRAIT.Kustom", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "Select Trait", + trigger : "manual", + script : "[Script.mo3XmOzgaROpB97i]" + }] + } + }, + melta: { + name : "TRAIT.Melta", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "Melta - Short Range", + trigger : "dialog", + script : "args.fields.damageDice.values.threes += 1; args.fields.damageDice.values.fives += 1;", + options : { + hideScript : "return args.target && args.target.type != 'vehicle'", + activateScript : "return args.target?.type != 'vehicle'", + } + }, + { + label : "Melta - Short Range (Vehicle)", + trigger : "dialog", + script : "args.fields.damageDice.values.ones += 1; args.fields.damageDice.values.twos += 1; args.fields.damageDice.values.threes += 1; args.fields.damageDice.values.fours += 1; args.fields.damageDice.values.fives += 1;", + options : { + hideScript : "return args.target && args.target.type == 'vehicle'", + activateScript : "return args.target?.type == 'vehicle'", + } + } + ], + } + }, + parry: { + name : "TRAIT.Parry", + system : { + transferData : { + documentType : "Actor", + equipTransfer: true + }, + scriptData : [{ + label : "Parry", + trigger : "dialog", + script : "args.fields.difficulty += 1", + options : { + targeter: true, + hideScript : "return !args.weapon || args.weapon?.isRanged", + activateScript : "return args.weapon?.isMelee", + } + }] + } + }, + pistol : { + name : "TRAIT.Pistol", + system : { + transferData : { + documentType : "Item", + }, + } + }, + rad: { + name : "TRAIT.Rad", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "Rad", + trigger : "dialog", + script : "args.fields.damageDice.addValue += parseInt(this.item.traitList.rad.rating)", + options : { + activateScript : "return true;", + } + }] + } + }, + rapidFire: { + name : "TRAIT.RapidFire", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "Rapid Fire - Short Range", + trigger : "dialog", + script : "args.fields.ed.value += (parseInt(this.item.traitList.rapidFire.rating) || 0)", + options : { + activateScript : "return args.fields.range == 'short'", + } + }] + } + }, + reliable: { + name : "TRAIT.Reliable", + system : { + transferData : { + documentType : "Item" + } + } + }, + rending: { + name : "TRAIT.Rending", + system : { + transferData : { + documentType : "Item", + }, + // TODO + } + }, + silent: { + name : "TRAIT.Silent", + system : { + transferData : { + documentType : "Item", + }, + } + }, + sniper: { + name : "TRAIT.Sniper", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "Sniper - Aim", + trigger : "dialog", + script : "args.fields.pool += 1; args.fields.ed.value += (parseInt(this.item.traitList.sniper.rating) || 0);", + options : { + activateScript : "return args.fields.aim", + } + }] + } + }, + spread: { + name : "TRAIT.Spread", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "Spread - Mob", + trigger : "dialog", + script : "args.fields.pool += 3;", + options : { + activateScript : "return args.target?.isMob", + } + }] + } + }, + supercharge: { + name : "TRAIT.Supercharge", + system : { + transferData : { + documentType : "Item", + }, + //TODO + } + }, + unwieldy: { + name : "TRAIT.Unwieldy", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "Unwieldy", + trigger : "dialog", + script : "args.fields.difficulty += parseInt(this.item.traitList.unwieldy.rating);", + options : { + activateScript : "return true;", + } + }] + } + }, + "waaagh!": { + name : "TRAIT.Waaagh", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "WAAAGH!", + trigger : "dialog", + script : "args.fields.pool++; if (args.actor.statuses.has('wounded')) args.fields.ed.value++;", + options : { + activateScript : "return args.actor.hasKeyword('ORK');", + } + }] + } + }, + warpWeapons: { + name : "TRAIT.WarpWeapon", + system : { + transferData : { + documentType : "Item", + }, + scriptData : [{ + label : "Warp Weapon", + trigger : "dialog", + script : "args.fields.damage = args.target.system.combat.resilience.total - 4;", + options : { + hideScript : "return !args.target;", + activateScript : "return this.item.system.damage.base < (args.target.system.combat.resilience.total - 4);", + } + }] + } + } , + bulk: { + name : "TRAIT.Bulk", + system : { + transferData : { + equipTransfer: true + }, + scriptData : [{ + label : "Reduce Speed", + trigger : "prePrepareData", + script : "this.actor.system.combat.speed -= this.item.itemTraits.bulk.rating", + }] + } + }, + powered: { + name : "TRAIT.Powered", + system : { + transferData : { + equipTransfer: true + }, + scriptData : [{ + label : "Increase Strength", + trigger : "computeCombat", + script : "this.actor.system.attributes.strength.total -= this.item.itemTraits.bulk.rating", + }] + } + } +} CONFIG.statusEffects = [ { id : "bleeding", + statuses : ["bleeding"], name : "CONDITION.Bleeding", - icon : "systems/wrath-and-glory/asset/icons/conditions/bleeding.svg", + img : "systems/wrath-and-glory/asset/icons/conditions/bleeding.svg", }, { id : "blinded", + statuses : ["blinded"], name : "CONDITION.Blinded", - icon : "systems/wrath-and-glory/asset/icons/conditions/blinded.svg", - changes : [ - {key: "difficulty.base", mode : 6, value : 4} - ], - flags : { - "wrath-and-glory.changeCondition" : { - 0 : {description : "Blinded", script : "return data.weapon"} - } + img : "systems/wrath-and-glory/asset/icons/conditions/blinded.svg", + system : { + scriptData : [{ + trigger : "dialog", + script : "args.fields.difficulty += 4", + label : "Sight Related", + options : { + activateScript : "return ['weaponSkill', 'ballisticSkill', 'psychicMastery'].includes(args.skill)" + } + }] } }, { id : "exhausted", + statuses : ["exhausted"], name : "CONDITION.Exhausted", - icon : "systems/wrath-and-glory/asset/icons/conditions/exhausted.svg" + img : "systems/wrath-and-glory/asset/icons/conditions/exhausted.svg" }, { id : "fear", + statuses : ["fear"], name : "CONDITION.Fear", - icon : "systems/wrath-and-glory/asset/icons/conditions/fear.svg", - changes : [ - {key: "difficulty.base", mode : 6, value : 2} - ], - flags : { - "wrath-and-glory.changeCondition" : { - 0 : {description : "+2 DN to all Tests", script : "return true"} - } + img : "systems/wrath-and-glory/asset/icons/conditions/fear.svg", + system : { + scriptData : [{ + trigger : "dialog", + script : "args.fields.difficulty += 2", + label : "+2 DN to all Tests", + options : { + activateScript : "return true;" + } + }] } }, { id : "frenzied", + statuses : ["frenzied"], name : "CONDITION.Frenzied", - icon : "systems/wrath-and-glory/asset/icons/conditions/frenzied.svg", + img : "systems/wrath-and-glory/asset/icons/conditions/frenzied.svg", changes : [{key: "system.attributes.strength.bonus", mode : 2, value : 1}] }, { id : "hindered", + statuses : ["hindered"], name : "CONDITION.Hindered", - icon : "systems/wrath-and-glory/asset/icons/conditions/hindered.svg", - changes : [ - {key: "difficulty.base", mode : 6, value : 1} - ], - flags : { - "wrath-and-glory.changeCondition" : { - 0 : {description : "+DN to all Tests", script : "return true"} + img : "systems/wrath-and-glory/asset/icons/conditions/hindered.svg", + system : { + scriptData : [{ + trigger : "dialog", + script : "args.fields.difficulty += parseInt(this.effect.specifier) || 1", + label : "+DN to all Tests", + options : { + activateScript : "return true;" + } + }, + { + trigger : "immediate", + script : "this.effect.updateSource({name : this.effect.setSpecifier(this.effect.getFlag(game.system.id, 'value') || 1)})", + label : "Value" } + ] } }, { id : "onfire", + statuses : ["onfire"], name : "CONDITION.OnFire", - icon : "systems/wrath-and-glory/asset/icons/conditions/onfire.svg" + img : "systems/wrath-and-glory/asset/icons/conditions/onfire.svg" }, { id : "pinned", + statuses : ["pinned"], name : "CONDITION.Pinned", - icon : "systems/wrath-and-glory/asset/icons/conditions/pinned.svg", - changes : [{key: "difficulty.base", mode : 6, value : 2}], - flags : { - "wrath-and-glory.changeCondition" : { - 0 : {description : "Pinned: Ballistic Skill Test Penalty", script : ""} - } + img : "systems/wrath-and-glory/asset/icons/conditions/pinned.svg", + system : { + scriptData : [{ + trigger : "dialog", + script : "args.fields.difficulty += 2", + label : "+2 DN to Ballistic Skill Tests", + options : { + activateScript : "return args.attribute == 'ballisticSkill';", + hideScript : "return args.attribute != 'ballisticSkill';" + } + }] } }, { id : "poisoned", + statuses : ["poisoned"], name : "CONDITION.Poisoned", - icon : "systems/wrath-and-glory/asset/icons/conditions/poisoned.svg", - changes : [{key: "difficulty.base", mode : 6, value : 2}], - flags : { - "wrath-and-glory.changeCondition" : { - 0 : {description : "+DN to all Tests", script : "return true"} - } + img : "systems/wrath-and-glory/asset/icons/conditions/poisoned.svg", + system : { + scriptData : [{ + trigger : "dialog", + script : "args.fields.difficulty += 2", + label : "+2 DN to all Tests", + options : { + activateScript : "return true", + } + }] } }, { id : "prone", + statuses : ["prone"], name : "CONDITION.Prone", - icon : "systems/wrath-and-glory/asset/icons/conditions/prone.svg" + img : "systems/wrath-and-glory/asset/icons/conditions/prone.svg" }, { id : "restrained", + statuses : ["restrained"], name : "CONDITION.Restrained", - icon : "systems/wrath-and-glory/asset/icons/conditions/restrained.svg", + img : "systems/wrath-and-glory/asset/icons/conditions/restrained.svg", changes : [{key: "system.combat.defence.bonus", mode : 2, value : -2},{key: "system.combat.speed", mode : 5, value : "0"} ] }, { id : "staggered", + statuses : ["staggered"], name : "CONDITION.Staggered", - icon : "systems/wrath-and-glory/asset/icons/conditions/staggered.svg", + img : "systems/wrath-and-glory/asset/icons/conditions/staggered.svg", changes : [{key: "system.combat.speed", mode : 1, value : 0.5} ] }, { id : "terror", + statuses : ["terror"], name : "CONDITION.Terror", - icon : "systems/wrath-and-glory/asset/icons/conditions/terror.svg", - changes : [{key: "difficulty.base", mode : 6, value : 2}], - flags : { - "wrath-and-glory.changeCondition" : { - 0 : {description : "+2 DN to all Tests", script : "return true"} - } + img : "systems/wrath-and-glory/asset/icons/conditions/terror.svg", + system : { + scriptData : [{ + trigger : "dialog", + script : "args.fields.difficulty += 2", + label : "+2 DN to all Tests", + options : { + activateScript : "return true", + } + }] } }, { id : "vulnerable", + statuses : ["vulnerable"], name : "CONDITION.Vulnerable", - icon : "systems/wrath-and-glory/asset/icons/conditions/vulnerable.svg", - changes : [{key: "system.combat.defence.bonus", mode : 2, value : -1}] + img : "systems/wrath-and-glory/asset/icons/conditions/vulnerable.svg", + system : { + scriptData : [ + { + trigger : "immediate", + script : "this.effect.updateSource({name : this.effect.setSpecifier(this.effect.getFlag(game.system.id, 'value') || 1)})", + label : "Value" + }, + { + trigger : "prePrepareDerivedData", + label : "Defence", + script : "this.actor.system.combat.defence.bonus -= parseInt(this.effect.specifier)" + } + ] + } }, { id : "dying", + statuses : ["dying"], name : "CONDITION.Dying", - icon : "systems/wrath-and-glory/asset/icons/dying.svg" + img : "systems/wrath-and-glory/asset/icons/dying.svg", + system : { + scriptData : [{ + label : "Extra Wrath Die", + script : "args.fields.wrath++;", + trigger : "dialog", + options: { + activateScript : "return true;" + } + }] + } }, { id : "dead", + statuses : ["dead"], name : "CONDITION.Dead", - icon : "systems/wrath-and-glory/asset/icons/dead.svg" + img : "systems/wrath-and-glory/asset/icons/dead.svg" } ] @@ -476,4 +1031,4 @@ CONFIG.TextEditor.enrichers = CONFIG.TextEditor.enrichers.concat([ } }]) -export default WNG +export default mergeObject(defaultWarhammerConfig, WNG); diff --git a/scripts/common/dialogs/attack-dialog.js b/scripts/common/dialogs/attack-dialog.js new file mode 100644 index 0000000..64999b9 --- /dev/null +++ b/scripts/common/dialogs/attack-dialog.js @@ -0,0 +1,119 @@ +import { CommonDialog } from "./common-dialog.js"; + +export class AttackDialog extends CommonDialog { + + subTemplate=["systems/wrath-and-glory/template/dialog/attack-roll.hbs"] + + get tooltipConfig() { + return foundry.utils.mergeObject({ + ed : { + label : "ED", + type : 1, + noCollect: true, + path : "fields.ed.value", + }, + ap : { + label : "AP", + type : 1, + noCollect: true, + path : "fields.ap.value" + }, + damage : { + label : "Damage", + type : 1, + noCollect: true, + path : "fields.damage" + }, + ones : { + label : "Ones Value", + type : 1, + noCollect: true, + path : "fields.damageDice.values.ones", + hideLabel : true + }, + twos : { + label : "Twos Value", + type : 1, + noCollect: true, + path : "fields.damageDice.values.twos", + hideLabel : true + }, + threes : { + label : "Threes Value", + type : 1, + noCollect: true, + path : "fields.damageDice.values.threes", + hideLabel : true + }, + fours : { + label : "Fours Value", + type : 1, + noCollect: true, + path : "fields.damageDice.values.fours", + hideLabel : true + }, + fives : { + label : "Fives Value", + type : 1, + noCollect: true, + path : "fields.damageDice.values.fives", + hideLabel : true + }, + sixes : { + label : "Sixes Value", + type : 1, + noCollect: true, + path : "fields.damageDice.values.sixes", + hideLabel : true + } + }, super.tooltipConfig) +} + + static get defaultOptions() { + let options = super.defaultOptions + options.classes.push("wrath-and-glory") + options.resizable = true; + options.width = 700; + return options + } + + _getSubmissionData() + { + let data = super._getSubmissionData(); + data.damageDice.values = { + 1 : data.damageDice.values.ones, + 2 : data.damageDice.values.twos, + 3 : data.damageDice.values.threes, + 4 : data.damageDice.values.fours, + 5 : data.damageDice.values.fives, + 6 : data.damageDice.values.sixes + } + return data; + } + + _defaultFields() + { + return mergeObject({ + damage : 0, + ed : { + value : 0, + dice : 0 + }, + ap : { + value : 0, + dice : 0 + }, + damageDice : { + values : { + ones : 0, + twos : 0, + threes : 0, + fours : 1, + fives : 1, + sixes : 2, + }, + addValue : 0 + } + }, super._defaultFields()); + } +} diff --git a/scripts/common/dialogs/base-dialog.js b/scripts/common/dialogs/base-dialog.js index a7383eb..25e3d33 100644 --- a/scripts/common/dialogs/base-dialog.js +++ b/scripts/common/dialogs/base-dialog.js @@ -1,259 +1,135 @@ -export class RollDialog extends Dialog { +export class RollDialog extends WarhammerRollDialog { + + get tooltipConfig() { + return { + pool : { + label : "Pool", + type : 1, + path : "fields.pool", + hideLabel : true + }, + difficulty : { + label : "Difficulty", + type : 1, + path : "fields.difficulty", + hideLabel : true + }, + wrath : { + label : "Wrath", + type : 1, + path : "fields.wrath" + } + } + } static get defaultOptions() { let options = super.defaultOptions - options.classes.push("roll-dialog") + options.classes.push("wrath-and-glory") options.resizable = true; + options.width = 540; return options } - async _render(...args) + get template() { - await super._render(...args) - let automatic = this._runConditional("script") - this.applyAutomaticChanges(automatic) - } - - static async create(data) { - let hide = this.runConditional("hide", data) - this.removeHiddenChanges(hide, data); - data.condensedChanges = this.condenseChanges(data.changes); - const html = await renderTemplate("systems/wrath-and-glory/template/dialog/common-roll.hbs", data); - return new Promise((resolve) => { - new this({ - title: game.i18n.localize(data.title), - content: html, - actor : data.actor, - targets : data.targets, - dialogData : data, - buttons: { - roll: { - icon: '', - label: game.i18n.localize("BUTTON.ROLL"), - callback: async (html) => { - let data = this.dialogCallback(html) - resolve(data) - }, - } - }, - default: "roll" - }, { width: 550 }).render(true) - }) - } - - static dialogCallback(html) { - let testData = this._baseTestData() - testData.difficulty.target = parseInt(html.find("#difficulty-target")[0].value); - testData.difficulty.penalty = parseInt(html.find("#difficulty-penalty")[0].value); - testData.difficulty.rank = html.find("#difficulty-rank")[0].value; - testData.pool.size = parseInt(html.find("#pool-size")[0].value); - testData.pool.bonus = parseInt(html.find("#pool-bonus")[0].value); - testData.pool.rank = html.find("#pool-rank")[0].value; - testData.wounds = html.find("#wounds")[0]?.value - testData.wrath.base = parseInt(html.find("#wrath-base")[0]?.value); - - return testData + return "systems/wrath-and-glory/template/dialog/common-roll.hbs"; } - static runConditional(type, data) { - let results = {} - for (let id in data.changes) { - let change = data.changes[id]; - try { - let func = new Function("data", change.conditional[type]).bind({ actor: data.actor, targets: data.targets, effect: change.document }) - results[id] = (func(data) == true) // Only accept true returns - } - catch (e) { - console.error("Something went wrong when processing conditional dialog effect: " + e, change) - results[id] = false - } + constructor(...args) + { + super(...args); + if (this.options.determination) + { + this.subTemplate = "systems/wrath-and-glory/template/dialog/determination-roll.hbs" } - return results } - _runConditional(type) - { - return this.constructor.runConditional(type, this.data.dialogData); - } - - applyAutomaticChanges(automatic) { - try { - let automaticIds = Object.keys(automatic).filter(i => automatic[i]); - // If a condensed change includes at least one ID automatically activate, activate the whole change - let activatedIndex = this.data.dialogData.condensedChanges.map((cc, index) => { - if (cc.id.some(id => automaticIds.includes(id))) - return index; - }).filter(i => Number.isNumeric(i)); - - let select = this.element.find(".effect-select")[0] - let options = Array.from(select.children) - options.forEach((opt, index) => { - if (activatedIndex.includes(index)) { - opt.selected = true; - select.dispatchEvent(new Event("change")) - } - }) - if (Object.values(automatic).some(i => i)) - select.focus() - } - catch(e) + async getData() { - console.error("Error applying automatic dialog changes: " + e) + let data = await super.getData(); + data.title = this.options.title; + data.noDn = this.options.noDn; + data.noWrath = this.options.noWrath; + data.rollModes = CONFIG.Dice.rollModes; + return data; } - } - - static removeHiddenChanges(hidden, data) + static async setupData({pool, wrath, dn}, actor, options={}) { - for(let id in hidden) - { - if (hidden[id]) + let {data, fields} = this._baseDialogData(actor, options); + + + mergeObject(fields, options.fields || {}); + + + if (pool) { - delete data.changes[id]; + fields.pool = pool; } - } - } - - // Condense effects that have the same description into one clickable element - static condenseChanges(changes) - { - let condensed = []; - for(let id in changes) - { - let existing = condensed.find(i => i.description == changes[id].conditional.description) - if (existing) + if (wrath) { - existing.id.push(id); - - // Only push this change's document if it's unique - if (!existing.tooltip.find(i => i.id == changes[id].document.id)) - { - existing.tooltip.push(changes[id].document) - } + fields.wrath = wrath; + } + + if (dn && !this.options.noDn) + { + fields.dn = dn; } else { - condensed.push({id : [id], description : changes[id].conditional.description, tooltip : [changes[id].document]}) + options.useDn = false; } - } - condensed.forEach(i => { - i.tooltip = `From: ${i.tooltip.map(i => i.name).join(", ")}` - }) - return condensed - } - - - static _baseTestData() { - return { - difficulty: { - target: 3, - penalty: 0, - rank: "none" - }, - pool: { - size: 1, - bonus: 0, - rank: "none" - }, - wrath: { - base: 1 - } - }; - } - - submit(button) { - let html = this.options.jQuery ? this.element : this.element[0] - let target = parseInt(html.find("#difficulty-target")[0].value); - let penalty = parseInt(html.find("#difficulty-penalty")[0].value); - let rank = html.find("#difficulty-rank")[0].value; - if (!target && !penalty && rank == "none") - return ui.notifications.error(game.i18n.localize("DIALOG.NO_DIFFICULTY")) - else - return super.submit(button) + return {data, fields, options}; } - - activateListeners(html) { - super.activateListeners(html); - - html.on("mouseenter", ".target", game.wng.utility.highlightToken.bind(this)) - html.on("mouseleave", ".target", game.wng.utility.unhighlightToken.bind(this)) - html.on("click", ".target", game.wng.utility.focusToken.bind(this)) - - // Reset effect values - this.effectValues = { - "pool.base": null, - "pool.rank": null, - "pool.bonus": null, - "difficulty.base": null, - "difficulty.rank": null, - "difficulty.bonus": null - } - - - this.inputs = {} - - html.find("input").focusin(ev => { - ev.target.select(); - }) - - html.find('.difficulty,.pool').change(ev => { - let type = ev.currentTarget.classList[0] - let input = ev.currentTarget.classList[1] - this.userEntry[`${type}.${input}`] = Number.isNumeric(ev.target.value) ? parseInt(ev.target.value) : ev.target.value - this.applyEffects() - }).each((i, input) => { - this.inputs[`${input.classList[0]}.${input.classList[1]}`] = input - }) - - this.userEntry = { - "pool.base": parseInt(this.inputs["pool.base"].value), - "pool.rank": this.inputs["pool.rank"].value, - "pool.bonus": parseInt(this.inputs["pool.bonus"].value), - "difficulty.base": parseInt(this.inputs["difficulty.base"].value), - "difficulty.rank": this.inputs["difficulty.rank"].value, - "difficulty.bonus": parseInt(this.inputs["difficulty.bonus"].value), + + _getSubmissionData() + { + let data = super._getSubmissionData(); + if (this.options.noWrath) + { + data.wrath = 0; } - - html.find(".effect-select").change(this._onEffectSelect.bind(this)) + return data; } - - _onEffectSelect(ev) { - // Reset effect values - for (let key in this.effectValues) - this.effectValues[key] = null - let changes = $(ev.currentTarget).val() - .map(index => this.data.dialogData.condensedChanges[index]) // Convert indices to condensed changes - .reduce((prev, current) => prev.concat(current.id), []) // Combine all condensed ids - .map(id => this.data.dialogData.changes[id]) // Turn ids into changes + computeFields() + { + + } - for (let c of changes) { - if (ActiveEffect.implementation.numericTypes.includes(c.key)) - this.effectValues[c.key] = (this.effectValues[c.key] || 0) + parseInt(c.value) - else if (Object.keys(game.wng.config.rankTypes).concat(Object.keys(game.wng.config.difficultyRankTypes)).includes(c.value)) - this.effectValues[c.key] = c.value + computeInitialFields() + { + if (this.options.corruption) + { + let level = game.wng.config.corruptionLevels[this.actor.corruptionLevel] + this.fields.dn += level.dn; + this.tooltips.add("difficulty", level.dn, "Corruption Level") } - this.applyEffects() } - applyEffects() { - for (let input in this.inputs) { - if (!this.inputs[input]) - continue - if (this.effectValues[input] != null) { - if (Number.isNumeric(this.effectValues[input])) - this.inputs[input].value = this.userEntry[input] + this.effectValues[input] - else - this.inputs[input].value = this.effectValues[input] - } - else // if not part of effect values, use user entry only + createBreakdown() + { + let breakdown = super.createBreakdown(); + breakdown.modifiersBreakdown = this.tooltips.getCollectedTooltips(); + return breakdown; + } + + _defaultFields() + { + let fields = mergeObject({ + difficulty : 3, + pool : 1, + wrath : 1, + }, super._defaultFields()); + + if (this.options.determination && game.user.isGM) { - this.inputs[input].value = this.userEntry[input] + fields.rollMode = "gmroll"; } - } + return fields } } \ No newline at end of file diff --git a/scripts/common/dialogs/common-dialog.js b/scripts/common/dialogs/common-dialog.js new file mode 100644 index 0000000..370dc3a --- /dev/null +++ b/scripts/common/dialogs/common-dialog.js @@ -0,0 +1,80 @@ +import { RollDialog } from "./base-dialog"; + +export class CommonDialog extends RollDialog { + + get skill() + { + return this.data.skill; + } + + get attribute() + { + return this.data.attribute; + } + + static async setupData({attribute, skill}, actor, options={}) + { + let dialogData = await super.setupData({}, actor, options) + dialogData.data.attribute = attribute || game.wng.config.skillAttribute[skill]; + dialogData.data.skill = skill; + dialogData.options.title = this._constructTitle(dialogData); + dialogData.data.item = options.item; + + return dialogData + } + + static _constructTitle({data, fields, options}) + { + let title = options.title + if (!title && data.skill) + { + title = options.title || `${game.wng.config.skills[data.skill]} Test` + } + else if (!title && data.attribute) + { + title = options.title || `${game.wng.config.attributes[data.attribute]} Test`; + } + title += options.appendTitle || ""; + return title; + } + + computeFields() + { + super.computeFields(); + this.fields.wrath += this.actor.itemTypes.traumaticInjury.length + this.tooltips.add("wrath", this.actor.itemTypes.traumaticInjury.length, "Traumatic Injuries") + } + + computeInitialFields() + { + super.computeInitialFields(); + if (this.data.skill || this.data.attribute) + { + let pool = this.data.skill ? this.actor.system.skills[this.data.skill].total : this.actor.system.attributes[this.data.attribute].total + this.fields.pool = pool + this.tooltips.set("pool", pool, game.wng.config.skills[this.data.skill] || game.wng.config.attributes[this.data.attribute]) + } + else if (this.options.initialTooltip) + { + this.tooltips.set("pool", this.initialFields.pool, this.options.initialTooltip) + // this.options.initialTooltip = game.wng.config.skills[this.data.skill] || game.wng.config.attributes[this.data.attribute] + } + + if (this.options.corruption) + { + let level = game.wng.config.corruptionLevels[this.actor.corruptionLevel] + this.difficulty += level.dn; + this.tooltips.add("difficulty", level.dn, "Corruption Level") + } + } + + _defaultFields() + { + return mergeObject({ + pool : 1, + wrath : 1, + difficulty : 3, + }, super._defaultFields()); + } + } + \ No newline at end of file diff --git a/scripts/common/dialogs/power-dialog.js b/scripts/common/dialogs/power-dialog.js index b67f41e..14b5293 100644 --- a/scripts/common/dialogs/power-dialog.js +++ b/scripts/common/dialogs/power-dialog.js @@ -1,115 +1,112 @@ -import { RollDialog } from "./base-dialog.js"; +import { AttackDialog } from "./attack-dialog.js"; -export class PowerDialog extends RollDialog { +export class PowerDialog extends AttackDialog { - static async create(data) { - let hide = this.runConditional("hide", data) - this.removeHiddenChanges(hide, data); - data.condensedChanges = this.condenseChanges(data.changes); - const html = await renderTemplate("systems/wrath-and-glory/template/dialog/psychic-roll.hbs", data); - return new Promise((resolve) => { - new this({ - title: game.i18n.localize(data.title), - content: html, - actor: data.actor, - targets: data.targets, - dialogData: data, - buttons: { - roll: { - icon: '', - label: game.i18n.localize("BUTTON.ROLL"), - callback: async (html) => { - let data = this.dialogCallback(html) - resolve(data) - }, - } - }, - default: "roll" - }, { width: 550 }).render(true) - }) - } + subTemplate=["systems/wrath-and-glory/template/dialog/attack-roll.hbs", "systems/wrath-and-glory/template/dialog/power-roll.hbs"] - static dialogCallback(html) { - let testData = super.dialogCallback(html) - testData.damage.base = parseInt(html.find("#damage-base")[0].value); - testData.damage.bonus = parseInt(html.find("#damage-bonus")[0].value); - testData.damage.rank = html.find("#damage-rank")[0].value; - testData.ed.base = parseInt(html.find("#ed-base")[0].value); - testData.ed.bonus = parseInt(html.find("#ed-bonus")[0].value); - testData.ed.rank = html.find("#ed-rank")[0].value; - testData.ed.damageValues[1] = parseInt(html.find("#die-one")[0].value); - testData.ed.damageValues[2] = parseInt(html.find("#die-two")[0].value); - testData.ed.damageValues[3] = parseInt(html.find("#die-three")[0].value); - testData.ed.damageValues[4] = parseInt(html.find("#die-four")[0].value); - testData.ed.damageValues[5] = parseInt(html.find("#die-five")[0].value); - testData.ed.damageValues[6] = parseInt(html.find("#die-six")[0].value); - testData.wrath.base = parseInt(html.find("#wrath-base")[0].value); - return testData - } - static _baseTestData() { - return mergeObject({ - damage: { - base: 0, - rank: "none", - bonus: 0 - }, - ed: { - base: 0, - rank: "none", - bonus: 0, - damageValues: { - 1: 0, - 2: 0, - 3: 0, - 4: 1, - 5: 1, - 6: 2 - } - }, - potency: 0 - }, super._baseTestData()) + get power() + { + return this.data.power; } - activateListeners(html) { - super.activateListeners(html); + static async setupData(power, actor, options={}) + { + if (typeof power == "string") + { + power = actor.items.get(power) || await fromUuid(power) + } + + + let skill = "psychicMastery" + + let dialogData = await super.setupData({skill}, actor, options) + + dialogData.data.levels = { + bound : game.i18n.localize("PSYCHIC_POWER.BOUND"), + unbound : game.i18n.localize("PSYCHIC_POWER.UNBOUND"), + transcendent : game.i18n.localize("PSYCHIC_POWER.TRANSCENDENT") + } - // Reset effect values - this.effectValues = flattenObject(mergeObject(this.effectValues, { - "damage.base": null, - "damage.rank": null, - "damage.bonus": null, - "ed.base": null, - "ed.rank": null, - "ed.bonus": null, - "wrath": null - })) + dialogData.data.power = power; + dialogData.data.item = power; + + dialogData.data.scripts = dialogData.data.scripts.concat(power?.getScripts("dialog")); + foundry.utils.setProperty(dialogData, "fields.ed.dice", power.system.damage.ed.dice); + foundry.utils.setProperty(dialogData, "fields.ap.dice", power.system.damage.ap.dice); + options.title = `${power.name} Test` + + if (power.system.DN == "?") + { + ui.notifications.warn(game.i18n.localize("DIALOG.TARGET_DEFENSE_WARNING")) + } + return dialogData; + } - html.find('.damage,.ed').change(ev => { - let type = ev.currentTarget.classList[0] - let input = ev.currentTarget.classList[1] - this.userEntry[`${type}.${input}`] = Number.isNumeric(ev.target.value) ? parseInt(ev.target.value) : ev.target.value - this.applyEffects() - }).each((i, input) => { - this.inputs[`${input.classList[0]}.${input.classList[1]}`] = input - }) + computeFields() + { + super.computeFields(); + if (this.fields.level == "unbound") + { + this.fields.wrath += 1 + this.tooltips.add("wrath", 1, this.data.levels[this.fields.level]) + } + if (this.fields.level == "transcendent") + { + this.fields.wrath += 2 + this.tooltips.add("wrath", 2, this.data.levels[this.fields.level]) + } - this.inputs.wrath = html.find("#wrath-base").change(ev => { - this.userEntry["wrath"] = parseInt(ev.target.value) - this.applyEffects(); - })[0] + let power = this.power; + + this.tooltips.start(this) + this.fields.damage += power.system.damage.base + power.system.damage.bonus + this.fields.ed.value += power.system.damage.ed.base + power.system.damage.ed.bonus + this.fields.ap.value += power.system.damage.ap.base + power.system.damage.ap.bonus + this.tooltips.finish(this, "Power") + + } + + /** + * Transforms dialog data and fields into a options into data that will be given to some test object for evaluation + * @returns {object} Formatted submission data + */ + _getSubmissionData() + { + let submitData = super._getSubmissionData(); + if (this.fields.level == "unbound" && !this.actor.hasCondition("unbound")) + { + this.actor.addCondition("unbound") + } + if (this.fields.level == "transcendent" && !this.actor.hasCondition("transcendent")) + { + this.actor.removeCondition("unbound") + this.actor.addCondition("transcendent") + } + return submitData; + } - this.userEntry = flattenObject(mergeObject(this.userEntry, { - "damage.base": parseInt(this.inputs["damage.base"].value), - "damage.rank": this.inputs["damage.rank"].value, - "damage.bonus": parseInt(this.inputs["damage.bonus"].value), - "ed.base": parseInt(this.inputs["ed.base"].value), - "ed.rank": this.inputs["ed.rank"].value, - "ed.bonus": parseInt(this.inputs["ed.bonus"].value), - "wrath": parseInt(this.inputs["wrath"].value) - })) + computeInitialFields() + { + super.computeInitialFields(); + let DN = this.power.system.DN; + if(!isNaN(DN)) + { + this.fields.difficulty = this.power.system.DN + this.tooltips.set("difficulty", this.fields.difficulty, this.power.name + " DN"); + } + else + { + this.fields.difficulty = null; + } } + _defaultFields() + { + return mergeObject({ + level : "bound", + }, super._defaultFields()); + } } \ No newline at end of file diff --git a/scripts/common/dialogs/weapon-dialog.js b/scripts/common/dialogs/weapon-dialog.js index 4b3c2b4..ffdc974 100644 --- a/scripts/common/dialogs/weapon-dialog.js +++ b/scripts/common/dialogs/weapon-dialog.js @@ -1,259 +1,172 @@ -import { RollDialog } from "./base-dialog.js"; +import { AttackDialog } from "./attack-dialog.js"; + +export class WeaponDialog extends AttackDialog { -export class WeaponDialog extends RollDialog { - async _render(...args) - { - await super._render(...args) - if (this.distance) - this.distance.dispatchEvent(new Event("change")) + get weapon() + { + return this.data.weapon; } + static async setupData(weapon, actor, options={}) + { + if (typeof weapon == "string") + { + weapon = actor.items.get(weapon) || await fromUuid(weapon) + } + + options.combi = weapon.system.combi.document ? await Dialog.confirm({title : "Combi-Weapons", content : "Fire both Combi-Weapons?"}) : false - static async create(data) { - let hide = this.runConditional("hide", data) - this.removeHiddenChanges(hide, data); - data.condensedChanges = this.condenseChanges(data.changes); - const html = await renderTemplate("systems/wrath-and-glory/template/dialog/weapon-roll.hbs", data); - return new Promise((resolve) => { - new this({ - title: game.i18n.localize(data.title), - content: html, - actor : data.actor, - targets : data.targets, - dialogData : data, - buttons: { - roll: { - icon: '', - label: game.i18n.localize("BUTTON.ROLL"), - callback: async (html) => { - let data = await this.dialogCallback(html) - - if (data.damage.dice) - { - let damageRoll = new Roll(`${data.damage.dice}d6`); - await damageRoll.roll(); - damageRoll.toMessage({speaker : ChatMessage.getSpeaker({actor : data.actor}), flavor : "Damage Dice Roll"}) - data.damage.bonus += damageRoll.total; - } - - if (data.ed.dice) - { - let edRoll = new Roll(`${data.ed.dice}d6`); - await edRoll.roll(); - edRoll.toMessage({speaker : ChatMessage.getSpeaker({actor : data.actor}), flavor : "ED Dice Roll"}) - data.ed.bonus += edRoll.total; - } - - - if (data.ap.dice) - { - let apRoll = new Roll(`${data.ap.dice}d6`); - await apRoll.roll(); - apRoll.toMessage({speaker : ChatMessage.getSpeaker({actor : data.actor}), flavor : "AP Dice Roll"}) - data.ap.bonus += apRoll.total; - } - - - resolve(data) - }, - } - }, - default: "roll" - }, { width: 550 }).render(true) - }) - } + let skill = weapon.isMelee ? "weaponSkill" : "ballisticSkill" + let attribute = weapon.getSkillFor(actor).attribute - static dialogCallback(html) { - let testData = super.dialogCallback(html) - testData.damage.base = parseInt(html.find("#damage-base")[0].value); - testData.damage.bonus = parseInt(html.find("#damage-bonus")[0].value); - testData.damage.rank = html.find("#damage-rank")[0].value; - testData.damage.dice = Number(html.find("#damage-dice")[0].value); - testData.ed.base = parseInt(html.find("#ed-base")[0].value); - testData.ed.bonus = parseInt(html.find("#ed-bonus")[0].value); - testData.ed.rank = html.find("#ed-rank")[0].value; - testData.ed.dice = Number(html.find("#ed-dice")[0].value); - testData.ap.base = parseInt(html.find("#ap-base")[0].value); - testData.ap.bonus = parseInt(html.find("#ap-bonus")[0].value); - testData.ap.rank = html.find("#ap-rank")[0].value; - testData.ap.dice = Number(html.find("#ap-dice")[0].value); - testData.ed.damageValues[1] = parseInt(html.find("#die-one")[0].value); - testData.ed.damageValues[2] = parseInt(html.find("#die-two")[0].value); - testData.ed.damageValues[3] = parseInt(html.find("#die-three")[0].value); - testData.ed.damageValues[4] = parseInt(html.find("#die-four")[0].value); - testData.ed.damageValues[5] = parseInt(html.find("#die-five")[0].value); - testData.ed.damageValues[6]= parseInt(html.find("#die-six")[0].value); - testData.wrath.base = parseInt(html.find("#wrath-base")[0].value); - testData.range = html.find(".range")[0]?.value - testData.aim = !!html.find(".aim.checked")[0] - - return testData - } + let dialogData = await super.setupData({skill, attribute}, actor, options) - static _baseTestData() { - return mergeObject({ - damage: { - base: 0, - rank: "none", - bonus: 0 - }, - ed: { - base: 0, - rank: "none", - bonus: 0, - damageValues: { - 1: 0, - 2: 0, - 3: 0, - 4: 1, - 5: 1, - 6: 2 + dialogData.data.item = weapon; + dialogData.data.weapon = weapon; + dialogData.data.scripts = dialogData.data.scripts.concat(weapon?.getScripts("dialog")); + foundry.utils.setProperty(dialogData, "fields.ed.dice", weapon.system.damage.ed.dice); + foundry.utils.setProperty(dialogData, "fields.ap.dice", weapon.system.damage.ap.dice); + + if (dialogData.data.targets[0]) + { + let token = actor.getActiveTokens()[0]?.document + let target = dialogData.data.targets[0]; + if (target && token) + { + dialogData.fields.distance = canvas.grid.measureDistances([{ ray: new Ray({ x: token.x, y: token.y }, { x: target.x, y: target.y }) }], { gridSpaces: true })[0] } - }, - ap: { - base: 0, - rank: "none", - bonus: 0 - }, - }, super._baseTestData()) + } + + + options.title = `${weapon.name} Test` + + return dialogData; } - _calculateRange(range) + computeFields() { - let weapon = this.data.dialogData.weapon - if (range == "short") + let weapon = this.weapon; + + this.tooltips.start(this) + this.fields.pool += weapon.attack.base + weapon.attack.bonus + this.fields.damage += weapon.system.damage.base + weapon.system.damage.bonus + (weapon.system.damage.rank * this.actor.system.advances?.rank || 0) + this.fields.ed.value += weapon.system.damage.ed.base + weapon.system.damage.ed.bonus + (weapon.system.damage.ed.rank * this.actor.system.advances?.rank || 0) + this.fields.ap.value += weapon.system.damage.ap.base + weapon.system.damage.ap.bonus + (weapon.system.damage.ap.rank * this.actor.system.advances?.rank || 0) + + if (weapon.isMelee) { + this.fields.damage += this.actor.system.attributes.strength.total + } + this.tooltips.finish(this, "Weapon") + + if (this.fields.aim) { - this.inputs["pool.bonus"].value = parseInt(this.inputs["pool.bonus"].value) + 1 - if (weapon.traitList.rapidFire) - this.inputs["ed.bonus"].value = parseInt(this.inputs["ed.bonus"].value) + (parseInt(weapon.traitList.rapidFire.rating) || 0) + this.fields.pool++; + this.tooltips.add("pool", 1, game.i18n.localize("WEAPON.AIM")) } - else if (range == "long") + + if (this.fields.charging) { - this.inputs["difficulty.bonus"].value = parseInt(this.inputs["difficulty.bonus"].value) + 2 + this.fields.pool++; + this.tooltips.add("pool", 1, game.i18n.localize("WEAPON.CHARGING")) } - } - _calculateAim() { - let weapon = this.data.dialogData.weapon - if (this.userEntry.aim) + if (this.actor.isMob) { - if (weapon.traitList.sniper) - { - this.inputs["pool.bonus"].value = parseInt(this.inputs["pool.bonus"].value) + 2 - this.inputs["ed.bonus"].value = parseInt(this.inputs["ed.bonus"].value) + (parseInt(weapon.traitList.sniper.rating) || 0) - } - else - { - this.inputs["pool.bonus"].value = parseInt(this.inputs["pool.bonus"].value) + 1 - } + this.fields.pool += Math.ceil(this.actor.mob / 2) + this.tooltips.add("pool", Math.ceil(this.actor.mob / 2), "Mob") } } - applyEffects() { - super.applyEffects(); - if (this.range) + computeInitialFields() + { + super.computeInitialFields(); + this.computeRange(); + this.computeTargets(); + } + + + computeTargets() + { + if (this.data.targets[0]) { - this._calculateRange(this.range.value); - this._calculateAim() + let target = this.data.targets[0] + + if (target && target.actor) + { + this.fields.difficulty = target.actor.combat.defence.total + this.tooltips.set("difficulty", target.actor.combat.defence.total, "Target Defence") + + + this.tooltips.start(this) + if (this.options.multi) + { + this.fields.difficulty += (this.options.multi - 1) * 2; + } + this.tooltips.finish(this, `Multi-Attack (${this.options.multi} Targets)`) + + + this.tooltips.start(this) + if (target.actor.system.combat.size == "large") + { + this.fields.pool += 1; + } + else if (target.actor.system.combat.size == "huge") + { + this.fields.pool += 2; + } + else if (target.actor.system.combat.size == "gargantuan") + { + this.fields.pool += 3; + } + this.tooltips.finish(this, "Target Size") + } } } - activateListeners(html) { - super.activateListeners(html); - - // Reset effect values - this.effectValues = flattenObject(mergeObject(this.effectValues, { - "damage.base": null, - "damage.rank": null, - "damage.bonus": null, - "damage.dice": null, - "ed.base": null, - "ed.rank": null, - "ed.bonus": null, - "ed.dice": null, - "ap.base": null, - "ap.rank": null, - "ap.bonus": null, - "ap.dice": null, - })) - - - html.find('.damage,.ed,.ap').change(ev => { - let type = ev.currentTarget.classList[0] - let input = ev.currentTarget.classList[1] - this.userEntry[`${type}.${input}`] = Number.isNumeric(ev.target.value) ? parseInt(ev.target.value) : ev.target.value - this.applyEffects() - }).each((i, input) => { - this.inputs[`${input.classList[0]}.${input.classList[1]}`] = input - }) - - this.range = html.find(".range").change(ev => { - this.applyEffects(); - })[0] - - this.distance = html.find(".distance").change(ev => { - let rangeNum = parseInt(ev.target.value); - let weapon = this.data.dialogData.weapon + computeRange() + { let range + let weapon = this.weapon; + if (this.fields.distance && weapon.isRanged) { - if (rangeNum <= weapon.range.short) + if (this.fields.distance <= weapon.range.short) { range = "short" } - else if (rangeNum > weapon.range.short && rangeNum <= weapon.range.medium) + else if (this.fields.distance > weapon.range.short && this.fields.distance <= weapon.range.medium) { range = "medium" } - else if (rangeNum > weapon.range.medium && rangeNum <= weapon.range.long) + else if (this.fields.distance > weapon.range.medium && this.fields.distance <= weapon.range.long) { range = "long" } - + else { range = "" - if (rangeNum) + if (this.fields.distance) { ui.notifications.warn(game.i18n.localize("DIALOG.OUT_OF_RANGE")) + } } - this.range.value = range; - this.range.dispatchEvent(new Event("change")) - })[0] - - html.find(".aim").click(ev => { - ev.currentTarget.classList.toggle("checked") + } - if (ev.currentTarget.classList.contains("checked")) - { - ev.currentTarget.innerHTML = '' - this.userEntry.aim = true - } - else - { - ev.currentTarget.innerHTML = '' - this.userEntry.aim = false - } + if (range) + { + this.fields.range = range; + } + } - this.applyEffects(); - }) - - this.userEntry = flattenObject(mergeObject(this.userEntry, { - "damage.base": parseInt(this.inputs["damage.base"].value), - "damage.rank": this.inputs["damage.rank"].value, - "damage.bonus": parseInt(this.inputs["damage.bonus"].value), - "damage.dice": parseInt(this.inputs["damage.dice"].value), - "ed.base": parseInt(this.inputs["ed.base"].value), - "ed.rank": this.inputs["ed.rank"].value, - "ed.bonus": parseInt(this.inputs["ed.bonus"].value), - "ed.dice": parseInt(this.inputs["ed.dice"].value), - "ap.base": parseInt(this.inputs["ap.base"].value), - "ap.rank": this.inputs["ap.rank"].value, - "ap.bonus": parseInt(this.inputs["ap.bonus"].value), - "ap.dice": parseInt(this.inputs["ap.dice"].value), - "aim" : false - })) + _defaultFields() + { + return mergeObject({ + distance : null, + range : null, + aim : false, + }, super._defaultFields()); } } diff --git a/scripts/common/handlebars.js b/scripts/common/handlebars.js index 83ea305..5fe7afd 100644 --- a/scripts/common/handlebars.js +++ b/scripts/common/handlebars.js @@ -1,9 +1,4 @@ export const initializeHandlebars = () => { - registerHandlebarsHelpers(); - preloadHandlebarsTemplates(); -}; - -function preloadHandlebarsTemplates() { const templatePaths = [ "systems/wrath-and-glory/template/actor/tab/effects.hbs", "systems/wrath-and-glory/template/item/tab/bonus.hbs", @@ -35,60 +30,11 @@ function preloadHandlebarsTemplates() { "systems/wrath-and-glory/template/chat/roll/mutation/mutation-result.hbs", "systems/wrath-and-glory/template/chat/roll/mutation/mutation-buttons.hbs", "systems/wrath-and-glory/template/apps/combatant-list.hbs", - //"systems/wrath-and-glory/template/partials/damage.hbs", ]; - return loadTemplates(templatePaths); -} - -function registerHandlebarsHelpers() { - Handlebars.registerHelper("removeMarkup", function (text) { - const markup = /<(.*?)>/gi; - return text.replace(markup, ""); - }); - - Handlebars.registerHelper("ifIsGM", function (options) { - return game.user.isGM ? options.fn(this) : options.inverse(this) - }) - - Handlebars.registerHelper("isGM", function (options) { - return game.user.isGM - }) - - Handlebars.registerHelper("config", function (key) { - return game.wng.config[key] - }) - - Handlebars.registerHelper("configLookup", function (obj, key) { - return game.wng.config[obj][key] - }) - - - Handlebars.registerHelper("array", function (array, cls) { - if (typeof cls == "string") - return array.map(i => `${i}`).join(`,`) - else - return array.join(", ") + loadTemplates({ + damage : "systems/wrath-and-glory/template/partials/damage.hbs", + test : "systems/wrath-and-glory/template/partials/test.hbs", + chatTargets : "systems/wrath-and-glory/template/partials/chat-targets.hbs" }) - - Handlebars.registerHelper("tokenImg", function (actor) { - let tokens = actor.getActiveTokens(); - let tokenDocument = actor.prototypeToken; - if (tokens.length == 1) { - tokenDocument = tokens[0].document; - } - return tokenDocument.hidden ? "systems/wfrp4e/tokens/unknown.png" : tokenDocument.texture.src; - }) - - Handlebars.registerHelper("tokenName", function (actor) { - let tokens = actor.getActiveTokens(); - let tokenDocument = actor.prototypeToken; - if (tokens.length == 1) { - tokenDocument = tokens[0].document; - } - return tokenDocument.hidden ? "???" : tokenDocument.name; - }) - - Handlebars.registerHelper("settings", function (key) { - return game.settings.get("wfrp4e", key); - }) -} + return loadTemplates(templatePaths); +}; \ No newline at end of file diff --git a/scripts/common/hooks.js b/scripts/common/hooks.js index 9d73709..801e25c 100644 --- a/scripts/common/hooks.js +++ b/scripts/common/hooks.js @@ -1,15 +1,10 @@ import entryContextHooks from "../hooks/entryContext.js" import ready from "../hooks/ready.js" -import settings from "../hooks/settings.js" import init from "../hooks/init.js"; -import effects from "../hooks/effects.js" import chat from "../hooks/chat.js"; import combat from "../hooks/combat.js"; -import actor from "../hooks/actor.js"; import token from "../hooks/token.js"; import WNGUtility from "./utility.js"; -import sidebar from "../hooks/sidebar.js"; -import item from "../hooks/item.js"; import hotbar from "../hooks/hotbar.js"; import setting from "../hooks/setting.js"; import i18n from "../hooks/i18n.js"; @@ -18,21 +13,13 @@ export default function() { entryContextHooks(); ready(); init(); - effects(); chat(); - item(); combat(); - actor(); token(); - sidebar(); hotbar(); setting(); i18n(); - Hooks.on("preCreateJournalEntry", _keepID) - Hooks.on("preCreateScene", _keepID) - Hooks.on("preCreateRollTable", _keepID) - Hooks.on("renderActorSheet", _addKeywordListeners) Hooks.on("renderJournalTextPageSheet", _addKeywordListeners) diff --git a/scripts/common/migration.js b/scripts/common/migration.js index 55176ba..bbb2b7c 100644 --- a/scripts/common/migration.js +++ b/scripts/common/migration.js @@ -1,204 +1,604 @@ -export async function migrateWorld() { - const schemaVersion = 8; - const worldSchemaVersion = Number(game.settings.get("wrath-and-glory", "worldSchemaVersion")); - if (worldSchemaVersion !== schemaVersion && game.user.isGM) { - ui.notifications.info("Upgrading the world, please wait..."); - for (let actor of game.actors.contents) { - try { - const update = migrateActorData(actor); - if (!isEmpty(update)) { - console.log(`Migrating ${actor.name}`) - await actor.update(update); +import { StandardWNGActorModel } from "../model/actor/components/standard"; + +export default class Migration { + static stats = {}; + static MIGRATION_VERSION = "6.0.0" + + // #region High Level Migration Handling + static async migrateWorld(update=false, updateVersion=false) { + this.stats = { + actors : { + updated : 0, + skipped : 0, + error : [], + total : 0, + items : 0, + effects : 0, + itemEffects : 0 + }, + items : { + updated : 0, + skipped : 0, + error : [], + total : 0, + effects : 0 + } + } + ui.notifications.notify(`>>> Initiated Wrath & Glory Version ${game.system.version} Migration <<<`); + console.log(`%c+++++++++++++++++| Begin Migration of World Actors |+++++++++++++++++`, "color: #DDD;background: #8a2e2a;font-weight:bold"); + for (let doc of game.actors.contents) + { + this.stats.actors.total++; + warhammer.utility.log(`+++| Actor: ${doc.name} |+++`, true, null, {groupCollapsed : true}) + try { + let migration = await this.migrateActor(doc); + let didMigrate = await this.migrateActorEffectRefactor(doc); + if (!isEmpty(migration) || didMigrate) + { + this.stats.actors.updated++; + if (update) + { + await doc.update(migration); } - } catch (e) { - console.error(e); + warhammer.utility.log(`+++| Migration Data: `, true, migration) + } + else + { + this.stats.actors.skipped++; + warhammer.utility.log(`+++| Nothing to migrate for ${doc.name} |+++`, true) } } - for (let item of game.items.contents) { - try { - console.log(`Migrating ${item.name}`) - const update = migrateItemData(item); - if (!isEmpty(update)) { - console.log(`Migrating ${item.name}`) - await item.update(update); + catch (e) { + this.stats.actors.error.push(doc.name); + warhammer.utility.error("+++| MIGRATION FAILED |+++ Error: " + e.stack, true, doc) + } + finally + { + console.groupEnd(); + } + } + + console.log(`%c+++++++++++++++++| Begin Migration of World Items |+++++++++++++++++`, "color: #DDD;background: #8a2e2a;font-weight:bold"); + for (let doc of game.items.contents) + { + this.stats.items.total++; + warhammer.utility.log(`+++| Item: ${doc.name} |+++`, true, null, {groupCollapsed : true}) + try { + let migration = await this.migrateItem(doc); + let didMigrate = await this.migrateItemEffectRefactor(doc); + if (!isEmpty(migration) || didMigrate) + { + this.stats.items.updated++; + if (update) + { + await doc.update(migration); } - } catch (e) { - console.error(e); + warhammer.utility.log(`+++| Migration Data: `, true, migration) + } + else + { + this.stats.items.skipped++; + warhammer.utility.log(`+++| Nothing to migrate for ${doc.name} |+++`, true) } } - for (let journal of game.journal.contents) { - try { - console.log(`Migrating ${journal.name}`) - const update = migrateJournalData(journal); - if (!isEmpty(update)) { - console.log(`Migrating ${journal.name}`) - await journal.update(update); - } - } catch (e) { - console.error(e); - } - } - for (let table of game.tables.contents) { - try { - console.log(`Migrating ${table.name}`) - const update = migrateTableData(table); - if (!isEmpty(update)) { - console.log(`Migrating ${table.name}`) - await table.update(update); - } - } catch (e) { - console.error(e); - } - } - game.settings.set("wrath-and-glory", "worldSchemaVersion", schemaVersion); - ui.notifications.info("Upgrade complete!"); - } -}; - - -let v10Conversions = { - "wng-core.bestiary" : "wng-core.actors", - "wng-core.abilities" : "wng-core.items", - "wng-core.archetypes" : "wng-core.items", - "wng-core.equipment" : "wng-core.items", - "wng-core.keywords" : "wng-core.items", - "wng-core.species-factions" : "wng-core.items", - "wng-core.spells" : "wng-core.items", - "wng-core.talents" : "wng-core.items", - "wng-core.mutations" : "wng-core.items", - "wng-forsaken.archetypes" : "wng-forsaken.items", - "wng-forsaken.species-factions" : "wng-forsaken.items", - "wng-forsaken.equipment" : "wng-forsaken.items", - "wng-forsaken.abilities" : "wng-forsaken.items", - } - - -function migrateActorData(actor) { - const updateData = { - items : [] + catch (e) { + this.stats.actors.error.push(doc.name); + warhammer.utility.error("+++| MIGRATION FAILED |+++ Error: " + e, true, doc) + } + finally + { + console.groupEnd(); + } + } + + console.log(`%c+++++++++++++++++| ${game.system.version} Migration Complete |+++++++++++++++++`, "color: #DDD;background: #8a2e2a;font-weight:bold"); + this._printStatistics(this.stats) + if (this.stats.actors.error.length || this.stats.items.error.length) + { + ui.notifications.warn(`>>> Migration Complete with ${this.stats.actors.error.length + this.stats.items.error.length} errors — See Console for details <<<`) + } + else + { + ui.notifications.notify(`>>> Migration Complete — See Console for details <<<`) + } + if (updateVersion) + { + game.settings.set("wrath-and-glory", "systemMigrationVersion", game.system.version) + } } - let html = _migrateV10Links(actor.system.notes) - if (html != actor.system.notes) + static async migratePacks(update=false, {world=true, compendium=false}={}) { - updateData["system.notes"] = html; + this.stats = { + actors : { + updated : 0, + skipped : 0, + error : [], + total : 0, + items : 0, + effects : 0, + itemEffects : 0 + }, + items : { + updated : 0, + skipped : 0, + error : [], + total : 0, + effects : 0 + } + } + + for(let pack of game.packs) + { + if (world && pack.metadata.package == "world") + { + await this.migratePack(pack, update); + } + else if (compendium && pack.metadata.package != "world") + { + await this.migratePack(pack, update); + } + } + + this._printStatistics(this.stats) } - for(let item of actor.items) + static async migratePack(pack, update) { - let itemData = migrateItemData(item); - if (!foundry.utils.isEmpty(itemData)) + if (typeof pack == "string") + { + pack = game.packs.get(pack); + } + if (!["Actor", "Item"].includes(pack.metadata.type)) + { + return + } + + if (update && pack.locked) + { + console.error(`Skipping ${pack.metadata.label} - Locked`); + return; + } + + console.log(`%c+++++++++++++++++| Begin Migration of ${pack.metadata.label} |+++++++++++++++++`, "color: #DDD;background: #8a2e2a;font-weight:bold"); + let documents = await pack.getDocuments(); + for(let doc of documents) { - itemData._id = item.id; - updateData.items.push(itemData); + if (doc.documentName == "Actor") + { + this.stats.actors.total++; + warhammer.utility.log(`+++| Actor: ${doc.name} |+++`, true, null, {groupCollapsed : true}) + try { + let migration = await this.migrateActor(doc); + if (!isEmpty(migration)) + { + this.stats.actors.updated++; + if (update) + { + await doc.update(migration); + } + warhammer.utility.log(`+++| Migration Data: `, true, migration) + } + else + { + this.stats.actors.skipped++; + warhammer.utility.log(`+++| Nothing to migrate for ${doc.name} |+++`, true) + } + } + catch (e) { + this.stats.actors.error.push(doc.name); + warhammer.utility.error("+++| MIGRATION FAILED |+++ Error: " + e.stack, true, doc) + } + finally + { + console.groupEnd(); + } + } + if (doc.documentName == "Item") + { + this.stats.items.total++; + warhammer.utility.log(`+++| Item: ${doc.name} |+++`, true, null, {groupCollapsed : true}) + try { + let migration = await this.migrateItem(doc); + if (!isEmpty(migration)) + { + this.stats.items.updated++; + if (update) + { + await doc.update(migration); + } + warhammer.utility.log(`+++| Migration Data: `, true, migration) + } + else + { + this.stats.items.skipped++; + warhammer.utility.log(`+++| Nothing to migrate for ${doc.name} |+++`, true) + } + } + catch (e) { + this.stats.actors.error.push(doc.name); + warhammer.utility.error("+++| MIGRATION FAILED |+++ Error: " + e, true, doc) + } + finally + { + console.groupEnd(); + } + } } + console.log(`%c+++++++++++++++++| ${pack.metadata.label} Migration Complete |+++++++++++++++++`, "color: #DDD;background: #8a2e2a;font-weight:bold"); } - if (updateData.items.length == 0) - { - delete updateData.items; + static async migrateActor(actor) { + let migration = { + items : (await Promise.all(actor.items.map(i => this.migrateItem(i, actor)))).filter(i => !isEmpty(i)), + effects: (await Promise.all(actor.effects.map(e => this.migrateEffect(e, actor)))).filter(i => !isEmpty(i)) + }; + + foundry.utils.mergeObject(migration, await this.actorDataMigration(actor)) + + this.stats.actors.items += migration.items.length; + this.stats.actors.effects += migration.effects.length; + + if (actor.effects.size) + { + warhammer.utility.log(`\t|--- Migrated ${migration.effects.length} / ${actor.effects.size} Embedded Effects`, true) + } + if (actor.items.size) + { + warhammer.utility.log(`\t|--- Migrated ${migration.items.length} / ${actor.items.size} Embedded Items`, true) + } + + if (migration.items.length == 0) + { + delete migration.items; + } + if (migration.effects.length == 0) + { + delete migration.effects; + } + if (!isEmpty(migration)) + { + migration._id = actor._id; + } + return migration; } + static async migrateItem(item, parent) { + if (parent) + { + warhammer.utility.log(`\t|--- Embedded Item: ${item.name}`, true) + } + + let migration = { + effects: (await Promise.all(item.effects.map(e => this.migrateEffect(e, item)))).filter(e => !isEmpty(e)) + }; - return updateData; -} + if (parent) + { + this.stats.actors.itemEffects += migration.effects.length; + } + else + { + this.stats.items.effects += migration.effects.length; + } -function migrateJournalData(journal) -{ - let updateData = {_id : journal.id, pages : []}; + if (migration.effects.size) + { + warhammer.utility.log(`${parent ? '\t' : ""}\t|--- Migrated ${migration.effects.length} / ${actor.effects.size} Embedded Effects`, true) + } - for(let page of journal.pages) - { - let html = page.text.content; - console.log(`Checking Journal Page HTML ${journal.name}.${page.name}`) - let newHTML = _migrateV10Links(html) + foundry.utils.mergeObject(migration, await this.itemDataMigration(item)) - if (html != newHTML) - { - updateData.pages.push({_id : page.id, "text.content" : newHTML}); + if (migration.effects.length == 0) + { + delete migration.effects; + } + + if (!isEmpty(migration)) + { + migration._id = item._id; + } + return migration; } - } - return updateData; -} -function migrateTableData(table) -{ - let updateData = {_id : table.id, results : []}; + static async migrateEffect(effect, parent) { + warhammer.utility.log(`\t${parent.parent ? "\t" : ""}|--- Active Effect: ${effect.name}`, true) + let migration = {}; + + foundry.utils.mergeObject(migration, await this.effectDataMigration(effect)) + + if (!isEmpty(migration)) { + migration._id = effect._id; + } + return migration; + } + //#endregion + + + // #region Data Migrations + static async actorDataMigration(actor) { + let migrated = {} + + if (actor.system instanceof StandardWNGActorModel) { - for(let result of table.results) - { - if (result.type == 0) + let faction = actor.itemTypes.faction[0]; + let species = actor.itemTypes.species[0]; + let archetype = actor.itemTypes.archetype[0]; + + if (faction) + { + foundry.utils.setProperty(migrated, "system.faction.id", faction.id); + } + if (species) + { + foundry.utils.setProperty(migrated, "system.species.id", species.id); + } + if (archetype) + { + foundry.utils.setProperty(migrated, "system.archetype.id", archetype.id); + } + } + + return migrated; + } + static async itemDataMigration(item) + { + let migrated = {} + + if (item.type == "weapon") + { + + } + + if (item.type == "faction" && item.actor) + { + let factionEffectOnActor = item.actor.effects.find(i => i.sourceName == item.name); + + let factionEffectOnItem = item.effects.contents.find(e => e.name == factionEffectOnActor?.name) + if (factionEffectOnItem) + { + let backgrounds = item.system.toObject().backgrounds; + let foundBG = backgrounds.origin.concat(backgrounds.accomplishment).concat(backgrounds.goal).find(bg => bg.effect.id == factionEffectOnItem?.id); + if (foundBG) + { + foundBG.chosen = true; + foundry.utils.setProperty(migrated, "system.backgrounds", backgrounds) + } + } + } + + if (item.type == "archetype") + { + this._migrateReference(item, "species", migrated) + this._migrateReference(item, "faction", migrated) + this._migrateReference(item, "ability", migrated) + this._migrateReferenceList(item, "suggested.talents", migrated); + } + + if (item.type == "species") + { + this._migrateReferenceList(item, "abilities", migrated); + } + + return migrated; + } + static async effectDataMigration(effect) { - let html = result.text; - let newHTML = _migrateV10Links(html) + let migrated = {} + + return migrated; + } - if (html != newHTML) - { - updateData.results.push({_id : result.id, text : newHTML}); - } + + + static async migrateActorEffectRefactor(actor) + { + let effectsToDelete = []; + let migrated = false; + + for(let effect of actor.effects) + { + let originItem = await fromUuid(actor.pack ? `Compendium.${actor.pack}.${effect.origin}` : effect.origin); + if (originItem) + { + let originEffect = originItem.effects.getName(effect.name); + if (originEffect) + { + let effectData = effect.toObject(); + effectData.transfer = originEffect.transfer; + originEffect.update(this.migrateEffectRefactor(effectData, effect)); + effectsToDelete.push(effect.id); + } + } + } + if (effectsToDelete.length) + { + migrated = true; + await actor.deleteEmbeddedDocuments("ActiveEffect", effectsToDelete); + } + for(let item of actor.items) + { + if (await this.migrateItemEffectRefactor(item)) + { + migrated = true; + } + } + return migrated; } - else if (result.type == 2 && v10Conversions[result.documentCollection]) + static async migrateItemEffectRefactor(item) { - updateData.results.push({_id : result.id, documentCollection : v10Conversions[result.documentCollection]}); + for(let effect of item.effects) + { + let migratedEffect = this.migrateEffectRefactor(effect.toObject(), effect) + if (!isEmpty(migratedEffect)) + { + await effect.update(migratedEffect); + return true; + } + else return false; + } } - } - return updateData; -} + static migrateEffectRefactor(data, document) + { + let changes = data?.changes || []; + let migrateScripts = false; + if (changes.some(c => c.mode == 6 || c.mode == 7) || data.system?.scriptData?.length == 0) + { + migrateScripts = true; + } + + if (document.parent.documentName == "Item" && !document.getFlag("wrath-and-glory", "migrated")) + { + if (data.transfer == false && document.parent.type != "ammo") + { + setProperty(data, "system.transferData.type", "target"); + } + if (document.parent.type == "ammo") + { + setProperty(data, "system.transferData.type", "document"); + setProperty(data, "system.transferData.documentType", "Item"); -function migrateItemData(item) { - let updateData + } + setProperty(data, "flags.wrath-and-glory.migrated", true); + } + + if (document.parent.documentName == "Item" && document.parent.type == "psychicPower" && data.system?.transferData?.type == "document") + { + setProperty(data, "system.transferData.type", "target"); + } - let newDescription = _migrateV10Links(item.system.description); - let newBenefits = _migrateV10Links(item.system.benefits); + if (migrateScripts) + { + let scriptData = [] - if (item.system.description != newDescription) - { - updateData["system.description"] = newDescription + let changeConditon = foundry.utils.getProperty(data, "flags.wrath-and-glory.changeCondition"); + for (let i in changeConditon) { + if (changes[i]?.mode >= 6) { + let script; + + if (changes[i].value === "true" || changes[i].value === "false") { + script = `args.fields.${changes[i].key.split("-").map((i, index) => index > 0 ? i.capitalize() : i).join("")} = ${changes[i].value}` + } + else if (changes[i].value.includes("@") && !changes[i].value.includes("@test")) + { + script = `args.fields.${changes[i].key.split("-").map((i, index) => index > 0 ? i.capitalize() : i).join("")} += (${changes[i].value.replace("@", "args.actor.system.")})` + } + else { + script = `args.fields.${changes[i].key.split("-").map((i, index) => index > 0 ? i.capitalize() : i).join("")} += (${changes[i].value})` + } + scriptData.push({ + trigger: "dialog", + label: changeConditon[i].description, + script: script, + options: { + targeter: changes[i].mode == 7, + activateScript: changeConditon[i].script, + hideScript: changeConditon[i].hide + } + }) + } + } + + const convertScript = (str = "") => { + str = str.replaceAll("@test", "this.effect.sourceTest"); + str = str.replaceAll("data.", "args."); + str = str.replaceAll("pool.bonus", "pool"); + str = str.replaceAll("difficulty.bonus", "difficulty"); + str = str.replaceAll("ed.bonus", "ed.value"); + str = str.replaceAll("ap.bonus", "ap.value"); + str = str.replaceAll("damage.bonus", "damage"); + str = str.replaceAll("damage.other.shock.bonus", "damage.other.shock"); + return str; + } + + + for (let newScript of scriptData) { + newScript.script = convertScript(newScript.script); + newScript.options.hideScript = convertScript(newScript.options.hideScript); + newScript.options.activateScript = convertScript(newScript.options.activateScript); + newScript.options.submissionScript = convertScript(newScript.options.submissionScript); + } + + + + data.changes = data.changes.filter(i => i.mode < 6); + setProperty(data, "system.scriptData", scriptData) + } + return data; } - if (item.system.benefits != newBenefits) + + static async _migrateReference(document, field, migration) { - updateData["system.benefits"] = newBenefits - } + let property = foundry.utils.getProperty(document.system, field); + if (!property || property.uuid) + { + return; + } + if (property.id) + { + let uuid = warhammer.utility.findUuid(property.id); - return updateData; -} + if (uuid) + { + foundry.utils.setProperty(migration, `system.${field}`, {uuid, id : property.id, name : property.name}); + } + } + } -function migrateEffectData(effect) -{ - let effectData = effect.toObject() - let description = getProperty(effectData, "flags.wrath-and-glory.description") - effectData.changes.forEach((change, i) => { - if (change.mode == 0) + static async _migrateReferenceList(document, field, migration) + { + let property = foundry.utils.getProperty(document.system, field); + if (property?.list) { - change.mode = 6 - setProperty(effectData, `flags.wrath-and-glory.changeCondition.${i}`, {description, script:""}) + let migratedList = property.list.map(i => { + return { + id : i.id, + uuid : warhammer.utility.findUuid(i.id), + name : i.name + } + }) + foundry.utils.setProperty(migration, `system.${field}.list`, migratedList); } - }) - return effectData + } + + + + + //#endregion + + //#region Utilities + + static shouldMigrate() + { + let systemMigrationVersion = game.settings.get("wrath-and-glory", "systemMigrationVersion") + + return foundry.utils.isNewerVersion(this.MIGRATION_VERSION, systemMigrationVersion); + } + + static _printStatistics(stats) + { + let errors = stats.actors.error.length + stats.items.error.length; + warhammer.utility.log(`Migration Statistics ${errors > 0 ? "(" + errors + " Errors)" : ""}`, true, stats, {groupCollapsed : true}) + warhammer.utility.log(`Actors - Updated: ${stats.actors.updated}; Skipped: ${stats.actors.skipped}; Error: ${stats.actors.error.length} ${stats.actors.error.length ? "(" + stats.actors.error.join(", ") + ")" : ""}`, true) + warhammer.utility.log(`Items - Updated: ${stats.items.updated}; Skipped: ${stats.items.skipped}; Error: ${stats.items.error.length} ${stats.items.error.length ? "(" + stats.items.error.join(", ") + ")" : ""}`, true) + console.groupEnd(); + } + //#endregion } -function _migrateV10Links(html) +Hooks.on("ready", () => { - try - { - if (!html) return - - for(let key in v10Conversions) + if(game.wng.migration.shouldMigrate()) { - let priorHTML = html - html = html.replaceAll(key, v10Conversions[key]) - if (html != priorHTML) - { - console.log(`Replacing ${key} with ${v10Conversions[key]}`) - } + ChatMessage.create({content : ` +

The Effect Refactor Arrives

+

If (and only if) you are updating from Wrath & Glory version 5.1.7 (or earlier), effects have been vastly improved with the integration of th Warhammer Library. However, this means that your Actors/Items in the world are out of date and don't utilize these new features.

+

It is recommended that your important Actors (notably player characters) replace their Talents, Powers, Upgrades, etc. fresh from the Compendium. Note: Implementing these effects is an ongoing process, while a great many Items from the preimum modules have been updated, there may be some that have not received attention and haven't been updated.

+

If you have questions, please utilize the Discord channels where I or other community members will be happy to answer your questions. Thanks!

+ `}) + game.wng.migration.migrateWorld(true, true); } - return html; - } - catch (e) - { - console.error("Error replacing links: " + e); - } -} \ No newline at end of file +}); \ No newline at end of file diff --git a/scripts/common/overrides.js b/scripts/common/overrides.js index 6e0b98b..aec5427 100644 --- a/scripts/common/overrides.js +++ b/scripts/common/overrides.js @@ -1,36 +1,6 @@ export default function() { - // Convert functions that move data between world and compendium to retain ID - Actors.prototype.fromCompendium = keepID(Actors.prototype.fromCompendium); - Items.prototype.fromCompendium = keepID(Items.prototype.fromCompendium); - Journal.prototype.fromCompendium = keepID(Journal.prototype.fromCompendium); - Scenes.prototype.fromCompendium = keepID(Scenes.prototype.fromCompendium); - RollTables.prototype.fromCompendium = keepID(RollTables.prototype.fromCompendium); - - Actor.implementation.prototype.toCompendium = keepID(Actor.implementation.prototype.toCompendium); - Item.implementation.prototype.toCompendium = keepID(Item.implementation.prototype.toCompendium); - JournalEntry.implementation.prototype.toCompendium = keepID(JournalEntry.implementation.prototype.toCompendium); - Scene.implementation.prototype.toCompendium = keepID(Scene.implementation.prototype.toCompendium); - RollTable.implementation.prototype.toCompendium = keepID(RollTable.implementation.prototype.toCompendium); - - - - function keepID(orig) - { - return function(...args) - { - try { - args[1].keepId = true; - } - catch(e) - { - console.error("Error setting keepId: " + e); - } - return orig.bind(this)(...args); - } - } - // Since IDs are maintained in WNG, we have to clean actor imports from their IDs function WNGImportFromJson(json) { const data = JSON.parse(json); diff --git a/scripts/common/tag-manager.js b/scripts/common/tag-manager.js deleted file mode 100644 index a146939..0000000 --- a/scripts/common/tag-manager.js +++ /dev/null @@ -1,38 +0,0 @@ - -export default class TagManager { - createTags() - { - this.tags = {} - Array.from(game.packs.keys()).forEach(packKey => { - this.tags[packKey] = this.findTagsFromIndex(game.packs.get(packKey).index) - }) - } - - findTagsFromIndex(index) - { - let tags = [] - index.forEach(i => { - if (!tags.includes(i.type)) - tags.push(i.type) - }) - return tags - } - - getPacksWithTag(tags) - { - if (!tags) - return Object.keys(this.tags).map(k => game.packs.get(k)) - - if (!Array.isArray(tags)) - tags = [tags] - - let keys = [] - - for(let key in this.tags) - if (this.tags[key].some(t => tags.includes(t))) - keys.push(key) - - return keys.map(k => game.packs.get(k)) - } - -} diff --git a/scripts/common/tests/ability-roll.js b/scripts/common/tests/ability-roll.js index 12366f2..2c3e097 100644 --- a/scripts/common/tests/ability-roll.js +++ b/scripts/common/tests/ability-roll.js @@ -4,61 +4,79 @@ import { WNGTest } from "./test.js" export default class AbilityRoll extends WNGTest { constructor(data = {}) { - super(data) - if (!data) - return + if (foundry.utils.isEmpty(data)) + { + // Recreated test + super(data) + return; + } + else + { + // New Test + data.targets = Array.from(game.user.targets).map(t => t.actor?.speakerData(t.document)); + super(data) + } - this.data.testData.ed = data.ed || {} - this.data.testData.ap = data.ap || {} - this.data.testData.damage= data.damage || {} + this.testData.itemId = data.item?.uuid; + + let item = data.item; - this.data.testData.otherDamage = data.otherDamage || {} + if (item.system.test.self) + { + this.testSelf = true; + } - this.testData.itemId = data.itemId + this.data.context.title = data.title; } get template() { - return "systems/wrath-and-glory/template/chat/roll/damage/ability-roll.hbs" + return "systems/wrath-and-glory/template/chat/roll/ability/ability-use.hbs" } - get damageTemplate() { - return "systems/wrath-and-glory/template/chat/roll/damage/ability-roll.hbs" - } async rollTest() { this._computeResult(); + this.result.enrichedDescription = await TextEditor.enrichHTML(this.item.system.description, {secrets: false, relativeTo: this.item}) } _computeResult() { this.data.result = {} if (this.item.hasTest) this.result.test = duplicate(this.item.test); - this.computeDamage() - this.rollDamage() + this.result.isSuccess = true; + if (this.testData.damage) + { + this.computeDamage() + } } - sendToChat() { - this.sendDamageToChat() - } - - get testEffects() { - if(this.item) - return this.item.effects.filter(e => !e.transfer) - else - return [] - } + async sendToChat({ newMessage = null, chatDataMerge = {} } = {}) { - get showEffects() { - return this.testEffects.length - } + const html = await renderTemplate(this.template, this); + let chatData = { + _id : randomID(), + type: "test", + rolls: [], + system: this.data, + user: game.user.id, + rollMode: this.context.rollMode, + content: html, + speaker: this.context.speaker + }; + chatData.speaker.alias = this.actor.token ? this.actor.token.name : this.actor.prototypeToken.name + ChatMessage.applyRollMode(chatData, chatData.rollMode); - get showTest() { - return this.item && this.item.hasTest + if (newMessage || !this.message) + { + this.context.messageId = chatData._id + await ChatMessage.create(chatData, {keepId : true}); + } + else { + delete chatData.roll + return this.message.update(chatData) + } } - get ability() {return true} - - - + get ability() {return true} } diff --git a/scripts/common/tests/corruption-test.js b/scripts/common/tests/corruption-test.js index 8efce0b..95b5be1 100644 --- a/scripts/common/tests/corruption-test.js +++ b/scripts/common/tests/corruption-test.js @@ -1,11 +1,6 @@ import { WNGTest } from "./test.js" export default class CorruptionTest extends WNGTest { - constructor(data = {}) - { - super(data) - } - get template() { return "systems/wrath-and-glory/template/chat/roll/corruption/corruption-roll.hbs" } @@ -45,9 +40,8 @@ export default class CorruptionTest extends WNGTest { if (prevLevel < newLevel) { ui.notifications.notify(game.i18n.localize("ROLL.NewCorruptionLevel")) - this.actor.setupGenericTest("mutation").then(async mutationTest => { - await mutationTest.rollTest(); - mutationTest.sendToChat(); + this.actor.setupGenericTest("mutation").then(test => { + test.rollTest(); }) } } diff --git a/scripts/common/tests/damage.js b/scripts/common/tests/damage.js new file mode 100644 index 0000000..96ec331 --- /dev/null +++ b/scripts/common/tests/damage.js @@ -0,0 +1,345 @@ +import { PoolDie } from "./test"; + +export class DamageRoll { + constructor(data) { + this.data = data + if (this.result.roll) + this.roll = Roll.fromData(this.result.roll) + this.rerolledDamage = this.rerollData.rerolls.map(i => Roll.fromData(i)); + } + + static DEFAULT_DAMAGE = { + damage : 0, + ed : {value : 0}, + ap : {value : 0}, + damageDice : { + values : { + 1 : 0, + 2 : 0, + 3 : 0, + 4 : 1, + 5 : 1, + 6 : 2, + }, + addValue : 0 + }, + other : { + mortal: "", + wounds: "", + shock: "", + } + } + + static fromTest(test) + { + return new this({ + damageData : foundry.utils.mergeObject(foundry.utils.deepClone(this.DEFAULT_DAMAGE), test.result.damage), + context : { + title : test.context.title, + targets : test.context.targets, + speaker : test.context.speaker, + source : test.message?.id, + itemId : test.testData.itemId + }, + rerollData : { + indices : [], + rerolls : [] + }, + result : { + + } + }) + } + + get template() { + return "systems/wrath-and-glory/template/chat/roll/damage/damage-roll.hbs" + } + + async rollTest() { + // Total dice in the test + let result = { + base: this.damageData.damage, + ed: this.damageData.ed.value, + ap: this.damageData.ap.value, + total: this.damageData.damage, + other : { + mortal : 0, + wounds: 0, + shock : 0 + } + } + + let modifiers = { + damage : [], + ed : [], + ap : [], + wounds : [], + mortal : [], + shock: [] + } + + await Promise.all(this.actor.runScripts("preComputeDamage", {damage : this.data.result, roll : this, test : this.source?.system.test, modifiers}) || []); + await Promise.all(this.item?.runScripts("preComputeDamage", {damage : this.data.result, roll : this, test : this.source?.system.test, modifiers}) || []); + + this.result.ed += modifiers.ed.reduce((acc, mod) => acc + mod.value, 0); + + if (this.damageData.ed.dice) + { + result.edDice = (await new Roll(this.damageData.ed.dice || "0").roll()).total; + result.ed += result.edDice; + } + + if (this.damageData.ap.dice) + { + result.apDice = (await new Roll(this.damageData.ap.dice || "0").roll()).total; + result.ap += result.apDice; + } + + this.roll = Roll.fromTerms([ + new PoolDie({ number: result.ed || 0, faces: 6, options: { values: this.damageData.damageDice.values, add : this.damageData.damageDice.addValue } }), + ]) + + await this.roll.evaluate(); + + this.damageData.roll = this.roll.toJSON(); + this.damageData.dice = this.roll.dice.reduce((prev, current) => prev.concat(current.results), []); + + // Set dice indices before filtering out shifted + this.damageData.dice.forEach((die, index) => die.index = index); + + result.other.mortal = (await new Roll(this.damageData.other.mortal?.toString() || "0").roll()).total + result.other.wounds = (await new Roll(this.damageData.other.wounds?.toString() || "0").roll()).total + result.other.shock = (await new Roll(this.damageData.other.shock?.toString() || "0").roll()).total + + + this.data.result = result; + await Promise.all(this.actor.runScripts("computeDamage", {damage : this.data.result, roll : this, test : this.source?.system.test, modifiers}) || []); + await Promise.all(this.item?.runScripts("computeDamage", {damage : this.data.result, roll : this, test : this.source?.system.test, modifiers}) || []); + this.result.base += modifiers.damage.reduce((acc, mod) => acc + mod.value, 0); + this.result.ap += modifiers.ap.reduce((acc, mod) => acc + mod.value, 0); + this.result.other.mortal += modifiers.mortal.reduce((acc, mod) => acc + mod.value, 0); + this.result.other.shock += modifiers.shock.reduce((acc, mod) => acc + mod.value, 0); + + this.computeDamage(); + + let getModifierBreakdown = (modifiers) => { + // modifiers[type].map(mod => `
  • ${mod.label}: ${HandlebarsHelpers.numberFormat(mod.value, { hash: { sign: true } })}

  • `); + return modifiers.map(mod => `${mod.value >= 0 ? (" + " + mod.value) : (" - " + Math.abs(mod.value))} (${mod.label})`) + + // Should return " + X (label) - Y (label) or similar" + // return modifiers[0]?.value >= 0 ? ` + ${str}` : str + } + + this.result.breakdown = { + damage : `

    ${this.damageData.damage} (Base) + ${result.rolledValue} (ED) ${getModifierBreakdown(modifiers.damage)}

    `, + mortal : `

    ${this.damageData.other.mortal} (@DICE) ${getModifierBreakdown(modifiers.mortal)}`, + wounds : `

    ${this.damageData.other.wounds} (@DICE) ${getModifierBreakdown(modifiers.wounds)}`, + shock : `

    ${this.damageData.other.shock} (@DICE) ${getModifierBreakdown(modifiers.shock)}`, + } + + + if ((this.damageData.other.mortal?.toString() || "").includes("d")) + { this.result.breakdown.mortal = this.result.breakdown.mortal.replace("@DICE", result.other.mortal)} + else + {this.result.breakdown.mortal = this.result.breakdown.mortal.replace("(@DICE)", "")} + + if ((this.damageData.other.wounds?.toString() || "").includes("d")) + { this.result.breakdown.wounds = this.result.breakdown.wounds.replace("@DICE", result.other.wounds)} + else + {this.result.breakdown.wounds = this.result.breakdown.wounds.replace("(@DICE)", "")} + + if ((this.damageData.other.shock?.toString() || "").includes("d")) + { this.result.breakdown.shock = this.result.breakdown.shock.replace("@DICE", result.other.shock)} + else + {this.result.breakdown.shock = this.result.breakdown.shock.replace("(@DICE)", "")} + + + if (this.result.ap) + { + this.result.breakdown.ap = `

    ${this.damageData.ap.value} (Base) ${getModifierBreakdown(modifiers.ap)}

    ` + } + + if (this.result.ed) + { + this.result.breakdown.ed = `

    ED: ${this.damageData.ed.value} (Base) ${getModifierBreakdown(modifiers.ed)}

    `; + } + await this.sendToChat(); + } + + computeDamage() + { + + this.result.roll = foundry.utils.deepClone(this.damageData.roll); + this.result.dice = foundry.utils.deepClone(this.damageData.dice); + + if (this.rerollData.indices?.length) { + this._computeReroll(); + } + this.result.total = this.result.base; + this.result.rolledValue = 0; + this.result.dice.forEach((die) => { + this.result.rolledValue += die.value; + this.result.total += die.value; + }); + + + + } + + + _computeReroll() { + let rerolledResults = []; + for (let reroll of this.rerolledDamage) + rerolledResults.push(reroll.dice.reduce((prev, current) => prev.concat(current.results.map(i => { + i.rerolled = true; + return i; + })), [])) + + // Merge rerolls and roll - For each reroll set, take the corresponding reroll indices and keep the dice that the indices indicate + for (let i = 0; i < rerolledResults.length; i++) { + let rerollResult = rerolledResults[i]; + let shouldRerollSet = this.rerollData.indices[i]; + this.result.dice = this.result.dice.reduce((prev, current, i) => { + if (shouldRerollSet.includes(i)) { + prev.push(rerollResult[i]); + } else { + prev.push(current); + } + return prev; + }, []) + } + } + + async reroll(diceIndices) { + + this.rerollData.indices.push(diceIndices) + + let reroll = await this.roll.reroll({async: true}); + this.rerollData.rerolls.push(reroll.toJSON()) + this.rerolledDamage.push(reroll) + + this.computeDamage(); + + if (game.dice3d) { + let rerollShow = reroll.toJSON(); + rerollShow.terms = rerollShow.terms.map((term, t) => { + if (term.results) { + term.results = term.results.map((die, i) => { + if (diceIndices.includes(this.roll.terms[t].results[i]?.index)) + return die + }).filter(i => i) + } + return term + }) + await game.dice3d.showForRoll(Roll.fromData(rerollShow)) + } + + this.sendToChat() + } + + + clearRerolls() { + this.rerollData.indices = [] + this.rerollData.rerolls = []; + this.computeDamage(); + this.sendToChat() + } + + + async sendToChat({ newMessage = null} = {}) { + const html = await renderTemplate(this.template, this); + let chatData = { + _id : randomID(), + type: "damage", + rolls: [this.roll], + system: this.data, + user: game.user.id, + rollMode: game.settings.get("core", "rollMode"), + content: html, + speaker: this.context.speaker + }; + chatData.speaker.alias = this.actor.token ? this.actor.token.name : this.actor.prototypeToken.name + ChatMessage.applyRollMode(chatData, chatData.rollMode); + + if (newMessage || !this.message) + { + this.context.messageId = chatData._id + await ChatMessage.create(chatData, {keepId : true}); + } + else { + delete chatData.roll + return this.message.update(chatData) + } + } + + // Update message data without rerendering the message content + updateMessageFlags() { + if (this.message) + return this.message.update({ system: this.data }) + } + + addReport(reports, replace=false) + { + let newReports = reports.map(r => `

    ${r.resisted ? '' : ''} ${r.message}

    `).join("") + + this.context.appliedReport = replace ? newReports : ((this.context.appliedReport || "") + newReports) + this.sendToChat() + } + + async applyToTargets() + { + let tokens = (game.user.targets.size ? Array.from(game.user.targets).map(t => t.document) : this.targetTokens); + let reports = await Promise.all(tokens.map(t => t.actor?.applyDamage(this.result.total + this.result.other.wounds, {ap : this.result.ap, shock : this.result.other.shock, mortal : this.result.other.mortal}, {test : this.source?.system.test, damageRoll: this, token : t}))); + this.addReport(reports); + } + + get hasRerolled() + { + return this.rerollData.indices.length + } + + + get damageData() + { + return this.data.damageData; + } + + get rerollData() + { + return this.data.rerollData; + } + + + get context() + { + return this.data.context; + } + + get result() + { + return this.data.result; + } + + get message() + { + return game.messages.get(this.context.messageId) + } + + get source() + { + return game.messages.get(this.context.source) + } + + get targetTokens() + { + // Ability rolls aren't sourced from a Test, they roll damage directly + return this.source ? this.source.system.test.targetTokens : this.context.targets.map(i => game.scenes.get(i.scene)?.tokens.get(i.token)); + } + + get actor() { return game.wng.utility.getSpeaker(this.context.speaker) } + + get item() + { + return this.source?.system.test?.item || fromUuidSync(this.context.itemId); + } +} \ No newline at end of file diff --git a/scripts/common/tests/determination.js b/scripts/common/tests/determination.js index c482eec..183e45e 100644 --- a/scripts/common/tests/determination.js +++ b/scripts/common/tests/determination.js @@ -3,10 +3,12 @@ import { PoolDie, WNGTest } from "./test.js"; export default class DeterminationRoll extends WNGTest { constructor(data = {}) { super(data) - if (data) - { - this.testData.wounds = data.wounds - } + if (foundry.utils.isEmpty(data)) + return + + + this.testData.wounds = data.wounds; + this.testData.ignoreShock = data.ignoreShock; this.testData.useDN = false } @@ -14,25 +16,13 @@ export default class DeterminationRoll extends WNGTest { get template() { return "systems/wrath-and-glory/template/chat/roll/determination/determination-roll.hbs" } - - async rollTest() { - this.result.poolSize = this.testData.pool.size + this.testData.pool.bonus + this.getRankNum(this.testData.pool.rank); - await this._rollDice() - this._computeResult(); - } - - async _rollDice() { - - this.roll = Roll.fromTerms([ - new PoolDie({ number: this.result.poolSize, faces: 6 }), - ]) - - await this.roll.evaluate({ async: true }); - } - _computeResult() { - super._computeResult(false,false); - this.result.shock = this.result.success >= this.result.wounds ? this.result.wounds : Math.min(this.result.success, this.testData.wounds) + super._computeResult(); + // Convert number of successes to shock (but no more than the original wounds value) + this.result.converted = this.result.success >= this.result.wounds ? this.result.wounds : Math.min(this.result.success, this.testData.wounds) + this.result.shock = this.testData.ignoreShock ? 0 : this.result.converted; + this.result.shockIgnored = this.testData.ignoreShock; + // Reduce Wounds by successes rolled this.result.wounds = this.testData.wounds >= this.result.success ? this.testData.wounds - this.result.success : 0 } diff --git a/scripts/common/tests/initiative.js b/scripts/common/tests/initiative.js index ce1be26..944a525 100644 --- a/scripts/common/tests/initiative.js +++ b/scripts/common/tests/initiative.js @@ -3,6 +3,8 @@ import { PoolDie, WNGTest } from "./test.js"; export default class InitiativeRoll extends WNGTest { constructor(data = {}) { super(data) + if (foundry.utils.isEmpty(data)) + return this.testData.useDN = false } diff --git a/scripts/common/tests/mutation-test.js b/scripts/common/tests/mutation-test.js index baf5de4..6ed80fa 100644 --- a/scripts/common/tests/mutation-test.js +++ b/scripts/common/tests/mutation-test.js @@ -1,10 +1,6 @@ import { WNGTest } from "./test.js" export default class MutationTest extends WNGTest { - constructor(data = {}) - { - super(data) - } get template() { return "systems/wrath-and-glory/template/chat/roll/mutation/mutation-roll.hbs" diff --git a/scripts/common/tests/power-test.js b/scripts/common/tests/power-test.js index 713c3e5..ca12273 100644 --- a/scripts/common/tests/power-test.js +++ b/scripts/common/tests/power-test.js @@ -4,38 +4,43 @@ export default class PowerTest extends WNGTest { constructor(data) { super(data) - if (!data) + if (foundry.utils.isEmpty(data)) return - - this.data.testData.ed = data.ed || {} - this.data.testData.ap = data.ap || {} - this.data.testData.damage= data.damage || {} - this.data.testData.itemId = data.itemId + this.data.testData.potency = foundry.utils.deepClone(this.item.potency.list) + this.data.testData.potency.forEach(p => p.allocation = 0) + this.data.testData.edit.potency = 0; - // TODO: add to dialog - this.data.testData.otherDamage = { - "mortalWounds": { value: this.item.otherDamage.mortalWounds, bonus : 0 }, - "wounds": { value: this.item.otherDamage.wounds, bonus : 0 }, - "shock": { value: this.item.otherDamage.shock, bonus : 0 }, + if (this.item.system.damage?.enabled) + { + this.addDamageData(data); } - - this.data.testData.potency = duplicate(this.item.potency) - this.data.testData.potency.forEach(p => p.allocation = 0) - this.data.context.edit.potency = 0; + } get template() { return "systems/wrath-and-glory/template/chat/roll/power/power-roll.hbs" } + async runPreScripts() + { + await super.runPreScripts(); + await Promise.all(this.actor.runScripts("preRollPowerTest", this)); + await Promise.all(this.item.runScripts("preRollPowerTest", this)); + } + + async runPostScripts() + { + await super.runPostScripts(); + await Promise.all(this.actor.runScripts("rollPowerTest", this)); + await Promise.all(this.item.runScripts("rollPowerTest", this)); + } + _computeResult() { super._computeResult() - if (this.item.hasTest) this.result.test = duplicate(this.item.test); - this.computeDamage() if (this.result.isSuccess) { this.result.range = this.item.range @@ -46,7 +51,7 @@ export default class PowerTest extends WNGTest { computePotencies() { - this.result.potency = {spent : 0, options : duplicate(this.testData.potency), available : this.testData.shifted.potency.length + this.context.edit.potency} + this.result.potency = {spent : 0, options : duplicate(this.testData.potency), available : this.testData.shifted.potency.dice.length + this.testData.edit.potency} this.result.potency.options.forEach(p => { // Set initial potency values (before potency allocation) @@ -64,7 +69,7 @@ export default class PowerTest extends WNGTest { newValue = propValue.replace(propValueNum, propValueNum + addToValue) // Replace the number with the potency value added } else - newValue = propValue + addToValue // If numeric property, just add the potency value + newValue = parseInt(propValue) + addToValue // If numeric property, just add the potency value if (p.property) @@ -94,10 +99,10 @@ export default class PowerTest extends WNGTest { async edit({pool=0, wrath=0, icons=0, damage=0, ed=0, ap=0, potency=0}={}) { - this.data.context.edit.damage += damage; - this.data.context.edit.ed += ed; - this.data.context.edit.ap += ap; - this.data.context.edit.potency += potency; + this.data.testData.edit.damage += damage; + this.data.testData.edit.ed += ed; + this.data.testData.edit.ap += ap; + this.data.testData.edit.potency += potency; await super.edit({pool, wrath, icons}) } diff --git a/scripts/common/tests/resolve-test.js b/scripts/common/tests/resolve-test.js index 328bb8d..c4df696 100644 --- a/scripts/common/tests/resolve-test.js +++ b/scripts/common/tests/resolve-test.js @@ -4,7 +4,7 @@ export default class ResolveTest extends WNGTest { constructor(data = {}) { super(data) - if (!data) + if (foundry.utils.isEmpty(data)) return this.testData.type = data.type diff --git a/scripts/common/tests/stealth.js b/scripts/common/tests/stealth.js index 638bf7c..b2e5c53 100644 --- a/scripts/common/tests/stealth.js +++ b/scripts/common/tests/stealth.js @@ -3,6 +3,8 @@ import { PoolDie, WNGTest } from "./test.js"; export default class StealthRoll extends WNGTest { constructor(data = {}) { super(data) + if (foundry.utils.isEmpty(data)) + return this.testData.useDN = false } @@ -10,20 +12,6 @@ export default class StealthRoll extends WNGTest { return "systems/wrath-and-glory/template/chat/roll/stealth/stealth-roll.hbs" } - async rollTest() { - this.result.poolSize = this.testData.pool.size + this.testData.pool.bonus + this.getRankNum(this.testData.pool.rank); - await this._rollDice() - this._computeResult(); - } - - async _rollDice() { - this.roll = Roll.fromTerms([ - new PoolDie({ number: this.result.poolSize, faces: 6 }), - ]) - - await this.roll.evaluate({ async: true }); - } - _computeResult() { super._computeResult(); this.result.stealth = this.result.success; diff --git a/scripts/common/tests/test.js b/scripts/common/tests/test.js index e5a500d..5143627 100644 --- a/scripts/common/tests/test.js +++ b/scripts/common/tests/test.js @@ -1,5 +1,11 @@ -export class WNGTest { +import { DamageModel } from "../../model/item/components/damage"; +import { DamageRoll } from "./damage"; + +export class WNGTest extends WarhammerTestBase { + static rollFunction = "rollTest"; + constructor(data = {}) { + super(); this.data = { testData: { difficulty: data.difficulty, @@ -7,20 +13,50 @@ export class WNGTest { attribute: data.attribute, skill: data.skill, wrath: data.wrath, - shifted: data.shifted || { damage: [], glory: [], other: [], potency: [] }, + shifted: data.shifted || { damage: { + label : game.i18n.localize("SHIFT.DAMAGE"), + dice : [], + letter : "D" + }, glory: { + label : game.i18n.localize("SHIFT.GLORY"), + dice : [], + letter : "G" + }, other: { + label : game.i18n.localize("SHIFT.OTHER"), + dice : [], + letter : "?" + }, potency: { + label : game.i18n.localize("SHIFT.POTENCY"), + dice : [], + letter : "P" + }}, + // shifted: data.shifted || { damage: [], glory: [], other: [], potency: [], added: {} }, rerolls: [], // Indices of reroll sets, - useDN: true + useDN: true, + itemId : data.item?.uuid, + edit: { pool: 0, wrath: 0, icons: 0, damage: 0, ed: 0, ap: 0 }, }, context: { - title: data.title, - targets: data.targets ? data.targets.map(i => i.document.toObject()) || [] : [], + title: data.options?.title, + targets: data.targets || [], type: data.type, + breakdown : data.context?.breakdown, speaker: data.speaker, rollClass: this.constructor.name, + rollMode : data.rollMode, rerolled: data.rerolled || false, - edit: { pool: 0, wrath: 0, icons: 0, damage: 0, ed: 0, ap: 0 } }, - result: {} + result: { + text : { + + } + }, + class: this.constructor.name + } + + if (this.item?.system.damage?.enabled) + { + this.addDamageData(this.item.system.damage); } } @@ -28,10 +64,6 @@ export class WNGTest { return "systems/wrath-and-glory/template/chat/roll/common/common-roll.hbs" } - get damageTemplate() { - return "systems/wrath-and-glory/template/chat/roll/damage/damage-roll.hbs" - } - static recreate(data) { if (!data.context) { return; @@ -48,12 +80,54 @@ export class WNGTest { return test } + async runPreScripts() + { + await super.runPreScripts(); + await Promise.all(this.item?.runScripts("preRollTest", this) || []); + } + + async runPostScripts() + { + await super.runPostScripts(); + await Promise.all(this.item?.runScripts("rollTest", this) || []); + } + + addDamageData(data) + { + if (data instanceof DamageModel) + { + this.testData.damage = { + base : data.base, + ed : {value : data.ed.base + data.ed.bonus + (data.ed.rank * (this.actor.system.advances?.rank || 0)), dice : data.ed.dice}, + ap : {value : data.ap.base + data.ed.bonus + (data.ap.rank * (this.actor.system.advances?.rank || 0)), dice : data.ap.dice}, + damageDice : data.damageDice, + other : data.otherDamage + } + } + else // From a Dialog + { + this.testData.damage = { + base : data.damage, + ed : data.ed, + ap : data.ap, + damageDice : data.damageDice, + other : this.item.system.damage.otherDamage + } + } + } + + static fromData(data) + { + return new this(data); + } + async rollTest() { + await this.runPreScripts() // Total dice in the test - let diceNum = this.testData.pool.size + this.testData.pool.bonus + this.getRankNum(this.testData.pool.rank); + let diceNum = this.testData.pool // Wrath = wrath value inputted, but can't be above total number of dice, and can't be negative - this.result.wrathSize = this.testData.wrath.base < 0 ? 0 : Math.min(this.testData.wrath.base, diceNum); + this.result.wrathSize = this.testData.wrath < 0 ? 0 : Math.min(this.testData.wrath, diceNum); // Leftover, if any, is pool dice this.result.poolSize = Math.max(diceNum - this.result.wrathSize, 0) @@ -62,6 +136,7 @@ export class WNGTest { this._computeResult(); this.handleCounters(); + await this.runPostScripts(); return this @@ -79,8 +154,8 @@ export class WNGTest { } _computeResult() { - this.data.result = {} - this.result.dn = (this.testData.useDN) ? this.testData.difficulty.target + this.testData.difficulty.penalty - this.getRankNum(this.testData.difficulty.rank) : 0; + this.data.result = {text : {}} + this.result.dn = (this.testData.useDN) && this.testData.difficulty this.result.roll = this.roll.toJSON(); this.result.dice = this.roll.dice.reduce((prev, current) => prev.concat(current.results), []); @@ -99,12 +174,18 @@ export class WNGTest { this.result.allDice = duplicate(this.result.dice); this.result.dice = this.result.dice.filter(die => !this.isShifted(die.index)); - this.result.success = this.result.dice.reduce((prev, current) => prev + current.value, 0) + this.context.edit.icons; + this.result.success = this.result.dice.reduce((prev, current) => prev + current.value, 0) + this.testData.edit.icons; this.result.failure = this.result.dice.reduce((prev, current) => prev + (current.value === 0 ? 1 : 0), 0); this.result.shiftsPossible = (this.isShiftable) ? this._countShifting() : 0; this.result.isSuccess = this.result.success >= this.result.dn; if (this.result.isWrathCritical) this.result.isWrathCritical = this.result.isWrathCritical && this.result.isSuccess // Only critical if test is successful + + if (this.result.isSuccess) + this.computeDamage() + + if (this.item?.hasTest && !this.item.system.test.self) this.result.test = duplicate(this.item.test); + } _computeReroll() { @@ -131,6 +212,24 @@ export class WNGTest { } } + /** + * Set Base values for damage + */ + computeDamage() { + if (this.testData.damage) + { + + this.result.damage = foundry.utils.deepClone({ + damage : this.testData.damage.base + this.testData.edit.damage, + ed : { value : this.testData.damage.ed.value + this.testData.shifted.damage.dice.length + this.testData.edit.ed, dice : this.testData.damage.ed.dice}, + ap : { value : this.testData.damage.ap.value + this.testData.edit.ap, dice : this.testData.damage.ap.dice}, + damageDice : this.testData.damage.damageDice, + other : this.testData.damage.other || this.item?.system?.damage.otherDamage + }) + } + } + + _handleWrath() { this.result.isWrathComplication = this.result.dice.some(r => r.isWrath && r.result === 1); @@ -146,14 +245,14 @@ export class WNGTest { _computeShifted() { this.result.shifted = this.result.dice.filter(die => this.isShifted(die.index)); this.result.shifted.forEach(die => { - if (this.testData.shifted.damage.includes(die.index)) - die.shift = "damage"; - else if (this.testData.shifted.glory.includes(die.index)) - die.shift = "glory"; - else if (this.testData.shifted.potency.includes(die.index)) - die.shift = "potency" - else - die.shift = "other"; + + for(let type in this.testData.shifted) + { + if (this.testData.shifted[type].dice.includes(die.index)) + { + die.shifted = this.testData.shifted[type]; + } + } }) } @@ -170,7 +269,7 @@ export class WNGTest { rerollShow.terms = rerollShow.terms.map((term, t) => { if (term.results) { term.results = term.results.map((die, i) => { - if (diceIndices.includes(this.roll.terms[t].results[i]?.index)) + if (diceIndices.includes(this.roll.terms[t].results?.[i]?.index)) return die }).filter(i => i) } @@ -208,11 +307,15 @@ export class WNGTest { wrathDice = new WrathDie({ number: wrath, faces: 6 }) let added - if (poolDice || wrathDice) { + if (poolDice && wrathDice) { added = Roll.fromTerms([ - poolDice, wrathDice + poolDice, new OperatorTerm({operator : "+"}), wrathDice ].filter(d => d)) } + else + { + added = Roll.fromTerms([poolDice || wrathDice]); + } if (pool < 0) { removePool = Math.abs(pool); @@ -225,6 +328,8 @@ export class WNGTest { let oldTerms = foundry.utils.deepClone(this.roll.terms).filter(t => t instanceof OperatorTerm || t.results.length > 0); + + // Find the last term of what is being deleted, and delete dice from that term // Example: Normal dice rolls will look like [PoolDieTerm, Operator, WrathDieTerm] // But, the user previously added dice, it will look like [PoolDieTerm, Operator, WrathDieTerm, PoolDieTerm] @@ -264,14 +369,22 @@ export class WNGTest { newRoll.splice(newRoll.length - 1, 1); } + // For some reason operator terms aren't evaluated but they are required to be to use fromTerms? + for(let term of newRoll) + { + if (!term._evaluated) + { + await term.evaluate(); + } + } this.roll = Roll.fromTerms(newRoll) } async edit({ pool = 0, wrath = 0, icons = 0 } = {}) { - this.context.edit.icons += icons; - this.context.edit.pool += pool; - this.context.edit.wrath += wrath; + this.testData.edit.icons += icons; + this.testData.edit.pool += pool; + this.testData.edit.wrath += wrath; if (pool || wrath) await this._addDice({ pool, wrath }) @@ -279,6 +392,11 @@ export class WNGTest { this.sendToChat(); } + addShiftOption(key, label, letter) + { + this.testData.shifted[key] = {dice : [], letter, label} + } + handleCounters() { if (this.result.isWrathCritical && !this.context.counterChanged && this.actor.getFlag("wrath-and-glory", "generateMetaCurrencies")) { @@ -299,9 +417,14 @@ export class WNGTest { } - shift(shift, type) { + async shift(shift, type) + { + if (type == "other" && Object.keys(this.testData.shifted).length > 4) + { + type = await this.promptShiftType() + } - this.testData.shifted[type] = this.testData.shifted[type].concat(shift) + this.testData.shifted[type].dice = this.testData.shifted[type].dice.concat(shift) this._computeResult() this.sendToChat() } @@ -314,10 +437,10 @@ export class WNGTest { ui.notifications.notify(game.i18n.format("COUNTER.GLORY_CHANGED", { change: glorySubtract })) }) //this.result.allDice.filter(die => die.shift).forEach(die => die.shift = "") - this.testData.shifted.other = [] - this.testData.shifted.damage = [] - this.testData.shifted.glory = [] - this.testData.shifted.potency = [] + for(let option of Object.values(this.testData.shifted)) + { + option.dice = []; + } this._computeResult() this.sendToChat() } @@ -327,24 +450,37 @@ export class WNGTest { return true } + async promptShiftType() + { + let options = Object.keys(this.testData.shifted).filter(i => !["damage", "potency", "glory"].includes(i)).map(id => { + return { + id, + name : this.testData.shifted[id].label, + img : "modules/wng-core/assets/dice/die-pool-6.webp" + } + }) + return (await ItemDialog.create(options, 1, {title : "Shift Options"}))[0].id + } + async sendToChat({ newMessage = null, chatDataMerge = {} } = {}) { const html = await renderTemplate(this.template, this); let chatData = { - type: CONST.CHAT_MESSAGE_TYPES.ROLL, + _id : randomID(), + type: "test", rolls: [this.roll], - flags: { "wrath-and-glory.testData": this.data }, + system: this.data, user: game.user.id, - rollMode: game.settings.get("core", "rollMode"), + rollMode: this.context.rollMode, content: html, speaker: this.context.speaker }; chatData.speaker.alias = this.actor.token ? this.actor.token.name : this.actor.prototypeToken.name ChatMessage.applyRollMode(chatData, chatData.rollMode); - if (newMessage || !this.message) { - return ChatMessage.create(chatData).then(msg => { - msg.update({ "flags.wrath-and-glory.testData.context.messageId": msg.id }) - }); + if (newMessage || !this.message) + { + this.context.messageId = chatData._id + await ChatMessage.create(chatData, {keepId : true}); } else { delete chatData.roll @@ -355,7 +491,7 @@ export class WNGTest { // Update message data without rerendering the message content updateMessageFlags() { if (this.message) - return this.message.update({ "flags.wrath-and-glory.testData": this.data }) + return this.message.update({ system: this.data }) } _countShifting() { @@ -371,16 +507,7 @@ export class WNGTest { } isShifted(dieIndex) { - if (this.testData.shifted.damage.includes(dieIndex)) - return true - if (this.testData.shifted.glory.includes(dieIndex)) - return true - if (this.testData.shifted.potency.includes(dieIndex)) - return true - if (this.testData.shifted.other.includes(dieIndex)) - return true - - return false + return Object.values(this.testData.shifted).some(option => option.dice.includes(dieIndex)); } getRankNum(rank) { @@ -400,132 +527,41 @@ export class WNGTest { } } - /** - * Set Base values for damage, before any rolling - */ - computeDamage() { - this.result.damage = { - ed: {}, - ap: (this.testData.ap.base + this.testData.ap.bonus + this.getRankNum(this.testData.ap.rank) + this.context.edit.ap) || 0, - dice: [], - flat: this.testData.damage.base + this.testData.damage.bonus + this.getRankNum(this.testData.damage.rank), - total: 0, - other: duplicate(this.testData.otherDamage || {}) - } - this.result.damage.total = this.result.damage.flat + this.context.edit.damage - this.result.damage.ed = { number: this.testData.ed.base + this.testData.ed.bonus + this.getRankNum(this.testData.ed.rank) + this.testData.shifted.damage.length + this.context.edit.ed}; - this.result.damage.ed.values = this.testData.ed.damageValues - } - async rollDamage() { - - this.result.damage.total = this.result.damage.flat + this.context.edit.damage - this.result.damage.dice = []; - - let add = 0 - if (this.weapon && this.weapon.traitList.rad) - add = Number(this.weapon.traitList.rad.rating) || 0; - - - let damage = this.result.damage - - - // Don't like this but will work for now - if (this.weapon && this.weapon.traitList.melta && this.result.range == "short") { - damage.ed.values = { - 1: 0, - 2: 0, - 3: 1, - 4: 1, - 5: 2, - 6: 2 - } - if (game.actors.get(this.data.context.targets[0]?.actorId)?.type == "vehicle") - { - damage.ed.values = { - 1: 1, - 2: 1, - 3: 1, - 4: 2, - 5: 2, - 6: 2 - } - } - } - - let r = Roll.fromTerms([ - new PoolDie({ number: damage.ed.number, faces: 6, options: { values: damage.ed.values, add } }), - ]) - - await r.evaluate({ async: true }); - r.terms.forEach((term) => { - if (typeof term === 'object' && term !== null) { - term.results.forEach(die => { - this.result.damage.total += die.value; - this.result.damage.dice.push(die); - }); - } - }); - - // Other Damage - for (let damage in this.result.damage.other) { - if (this.result.damage.other[damage].value) - this.result.damage.other[damage].total = (await new Roll(this.result.damage.other[damage].value).evaluate({ async: true })).total + this.result.damage.other[damage].bonus - else if (this.result.damage.other[damage].bonus) - this.result.damage.other[damage].total = this.result.damage.other[damage].bonus - - } - - this.damageRoll = r; - this.result.damage.roll = r.toJSON() + let damage = DamageRoll.fromTest(this); + await damage.rollTest(); + this.result.damageRoll = damage.context.messageId; this.updateMessageFlags() - this.sendDamageToChat() } - - async sendDamageToChat() { - const html = await renderTemplate(this.damageTemplate, this); - let chatData = { - type: CONST.CHAT_MESSAGE_TYPES.ROLL, - roll: this.damageRoll, - flags: { "wrath-and-glory.testData": this.data }, - user: game.user.id, - rollMode: game.settings.get("core", "rollMode"), - content: html, - speaker: this.context.speaker - }; - - ChatMessage.applyRollMode(chatData, chatData.rollMode); - return ChatMessage.create(chatData); + get hasRerolled() + { + return this.testData.rerolls?.length } - // Need a specialized function to account for both item and ammo effects - getEffect(effectId) { - return this.testEffects.find(e => e.id == effectId) + _formatBreakdown(breakdown) + { + breakdown.modifiersBreakdown = `

    ${game.i18n.localize("DIALOG.MODIFIER_BREAKDOWN")}

    ${breakdown.modifiersBreakdown}`; + return Object.values(breakdown).join(""); } get doesDamage() { - return (this.testData.damage && (this.testData.damage.base || this.testData.damage.bonus || this.testData.damage.rank != "none")) || (this.testData.ed && (this.testData.ed.base || this.testData.ed.bonus || this.testData.ed.rank != "none")) + return this.testData.damage; } - get testEffects() { - if (this.item) { - let effects = this.item.effects.filter(e => !e.transfer) - if (this.item.isRanged && this.item.Ammo) - effects = effects.concat(this.item.Ammo.ammoEffects) - return effects + get showTest() { + let effects = this.targetEffects.concat(this.damageEffects).concat(this.areaEffects) + + // Effects already prompt a test + if (effects.some(e => e.system.transferData.avoidTest.value == "item")) + { + return false; } else - return [] - } - - get showEffects() { - return this.testEffects.length && this.result.isSuccess - } - - get showTest() { - return this.result.isSuccess && this.result.test + { + return this.result.isSuccess && this.result.test + } } get testDisplay() { diff --git a/scripts/common/tests/weapon-test.js b/scripts/common/tests/weapon-test.js index 4fc3e59..61f4483 100644 --- a/scripts/common/tests/weapon-test.js +++ b/scripts/common/tests/weapon-test.js @@ -4,16 +4,14 @@ export default class WeaponTest extends WNGTest { constructor(data = {}) { super(data) - if (!data) + if (foundry.utils.isEmpty(data)) return - this.data.testData.ed = data.ed - this.data.testData.ap = data.ap - this.data.testData.damage= data.damage this.data.testData.range = data.range this.data.testData.aim = data.aim - this.testData.itemId = data.itemId + this.addDamageData(data); + //this.data.context.edit = mergeObject(this.data.context.edit, {damage : 0, ed : 0, ap : 0}) } @@ -21,12 +19,23 @@ export default class WeaponTest extends WNGTest { return "systems/wrath-and-glory/template/chat/roll/weapon/weapon-roll.hbs" } + async runPreScripts() + { + await super.runPreScripts(); + await Promise.all(this.actor.runScripts("preRollWeaponTest", this)); + } + + async runPostScripts() + { + await super.runPostScripts(); + await Promise.all(this.actor.runScripts("rollWeaponTest", this)); + } async edit({pool=0, wrath=0, icons=0, damage=0, ed=0, ap=0}={}) { - this.data.context.edit.damage += damage; - this.data.context.edit.ed += ed; - this.data.context.edit.ap += ap; + this.data.testData.edit.damage += damage; + this.data.testData.edit.ed += ed; + this.data.testData.edit.ap += ap; await super.edit({pool, wrath, icons}) } @@ -36,9 +45,6 @@ export default class WeaponTest extends WNGTest { this.result.range = this.testData.range this.result.aim = this.testData.aim - if (this.item.hasTest) this.result.test = duplicate(this.item.test); - if (this.result.isSuccess) - this.computeDamage() } get weapon() {return fromUuidSync(this.testData.itemId)} diff --git a/scripts/common/utility.js b/scripts/common/utility.js index e2b881d..15b8426 100644 --- a/scripts/common/utility.js +++ b/scripts/common/utility.js @@ -1,30 +1,6 @@ import { WrathAndGloryItem } from "../document/item.js"; export default class WNGUtility { - /** - * Searches an object for a key that matches the given value. - * - * @param {String} value value whose key is being searched for - * @param {Object} obj object to be searched in - */ - static findKey(value, obj, options = {}) { - if (!value || !obj) - return undefined; - - if (options.caseInsensitive) { - for (let key in obj) { - if (obj[key].toLowerCase() == value.toLowerCase()) - return key; - } - } - else { - for (let key in obj) { - if (obj[key] == value) - return key; - } - } - } - static getAttributeCostTotal(rating, base = 0) { let total = 0 @@ -71,9 +47,9 @@ export default class WNGUtility { let item = game.items.contents.find(i => i.type == "keyword" && i.name.toLowerCase() == name.toLowerCase()) if (!item) { - let packs = game.wng.tags.getPacksWithTag("keyword") - for (let pack of packs) { - let i = pack.index.contents.find(i => i.name == name) + for (let pack of game.packs) { + + let i = pack.index.contents.find(i => i.name == name && i.type == "keyword") if (i) { // If item is in pack item = await pack.getDocument(i._id) } @@ -87,31 +63,6 @@ export default class WNGUtility { return new WrathAndGloryItem({ name, type: "keyword", img: "modules/wng-core/assets/ui/aquila-white.webp" }) } - static _keepID(id, document) { - try { - let compendium = !!document.pack - let world = !compendium - let collection - - if (compendium) { - let pack = game.packs.get(document.pack) - collection = pack.index - } - else if (world) - collection = document.collection - - if (collection.has(id)) { - ui.notifications.notify(`${game.i18n.format("ERROR.ID", { name: document.name })}`) - return false - } - else return true - } - catch (e) { - console.error(e) - return false - } - } - static _getTargetDefence() { const targets = game.user.targets.size; if (0 >= targets) { @@ -156,32 +107,6 @@ export default class WNGUtility { } } - static highlightToken(ev) { - if (!canvas.ready) return; - const li = ev.target; - let tokenId = li.dataset.tokenId - const token = canvas.tokens.get(tokenId); - if (token?.isVisible) { - if (!token._controlled) token._onHoverIn(ev); - this._highlighted = token; - } - } - - static unhighlightToken(ev) { - const li = ev.target; - let tokenId = li.dataset.tokenId - if (this._highlighted) this._highlighted._onHoverOut(ev); - this._highlighted = null; - } - - - static focusToken(ev) { - const li = ev.target; - let tokenId = li.dataset.tokenId - const token = canvas.tokens.get(tokenId); - canvas.animatePan({ x: token.center.x, y: token.center.y, duration: 250 }); - } - static async tableToHTML(table, label, options=[]) { let noCenter = options.includes("no-center"); @@ -235,29 +160,25 @@ export default class WNGUtility { // Trigger the item roll switch (itemType) { case "attribute": - test = await actor.setupAttributeTest(itemName) + actor.setupAttributeTest(itemName) break; case "skill": - test = await actor.setupSkill(itemName) + actor.setupSkill(itemName) break; case "weapon": - test = await actor.setupWeaponTest(item) + actor.setupWeaponTest(item) break; case "psychicPower": - test = await actor.setupPowerTest(item) + actor.setupPowerTest(item) break; case "ability": - test = await actor.setupAbilityRoll(item) + actor.setupAbilityRoll(item) break; default: - test = await actor.setupGenericTest(itemType) + actor.setupGenericTest(itemType) break; } - return Array.isArray(test) - ? test.forEach(t => t.rollTest().then(roll => roll.sendToChat())) - : test.rollTest().then(roll => roll.sendToChat()) - } } diff --git a/scripts/document/actor.js b/scripts/document/actor.js index 0c9deb3..096fe69 100644 --- a/scripts/document/actor.js +++ b/scripts/document/actor.js @@ -1,4 +1,3 @@ -import WNGDocumentMixin from "./mixin.js"; import { WNGTest } from "../common/tests/test.js"; import WeaponTest from "../common/tests/weapon-test.js"; import PowerTest from "../common/tests/power-test.js"; @@ -7,41 +6,28 @@ import MutationTest from "../common/tests/mutation-test.js"; import ResolveTest from "../common/tests/resolve-test.js"; import DeterminationRoll from "../common/tests/determination.js"; import AbilityRoll from "../common/tests/ability-roll.js"; -import WNGUtility from "../common/utility.js"; import StealthRoll from "../common/tests/stealth.js"; import CharacterCreation from "../apps/character-creation.js"; import { RollDialog } from "../common/dialogs/base-dialog.js"; import { WeaponDialog } from "../common/dialogs/weapon-dialog.js"; import { PowerDialog } from "../common/dialogs/power-dialog.js"; +import { CommonDialog } from "../common/dialogs/common-dialog.js"; -export class WrathAndGloryActor extends WNGDocumentMixin(Actor) { +export class WrathAndGloryActor extends WarhammerActor { prepareBaseData() { - // this.propagateDataModels(this.system, "runScripts", this.runScripts.bind(this)); - this._itemTypes = null; - this.derivedEffects = [] - this.system.computeBase(); - // this.runScripts("prepareBaseData", this); + this.derivedEffects = []; + super.prepareBaseData(); + this.keywords = new Set(this.itemTypes.keyword.map(i => i.name)); } prepareDerivedData() { - this.runScripts("prePrepareDerivedData", this); - this.system.computeDerived(); - this.items.forEach(i => i.prepareOwnedData()); - } - - - prepareDerivedData() { - // this.runScripts("prePrepareDerivedData", this); - this._applyDerivedEffects() - this.system.computeDerived(); - this.items.forEach(i => i.prepareOwnedData()); - // this.runScripts("prepareOwnedItems", this); - // this.system.computeDerived(); - // this.runScripts("postPrepareDerivedData", this); + this._applyDerivedEffects(); + super.prepareDerivedData(); } + _applyDerivedEffects() { this.derivedEffects.forEach(change => { change.effect.fillDerivedData(this, change) @@ -49,395 +35,210 @@ export class WrathAndGloryActor extends WNGDocumentMixin(Actor) { }) } + async _onUpdate(data, options, user) + { + await super._onUpdate(data, options, user); + if (options.deltaWounds > 0) + { + TokenHelpers.displayScrollingText("+" + options.deltaWounds, this, {fill: "0xFF0000", direction : CONST.TEXT_ANCHOR_POINTS.TOP}); + } + else if (options.deltaWounds < 0) + { + TokenHelpers.displayScrollingText(options.deltaWounds, this, {fill: "0x00FF00", direction : CONST.TEXT_ANCHOR_POINTS.BOTTOM}); + } + + if (options.deltaShock > 0) + { + TokenHelpers.displayScrollingText("+" + options.deltaShock, this, {fill: "0x6666FF", direction : CONST.TEXT_ANCHOR_POINTS.TOP}); + } + else if (options.deltaShock < 0) + { + TokenHelpers.displayScrollingText(options.deltaShock, this, {fill: "0x6666FF", direction : CONST.TEXT_ANCHOR_POINTS.BOTTOM}); + } + } + //#region Rolling async setupAttributeTest(attribute, options = {}) { - let attributeObject = this.attributes[attribute] - - let dialogData = this._baseDialogData(); - dialogData.title = `${game.i18n.localize(attributeObject.label)} Test` - dialogData.pool.size = attributeObject.total - this._addOptions(dialogData, options) - dialogData.type = "attribute" - dialogData.attribute = attribute - let testData = await RollDialog.create(dialogData) - testData.targets = dialogData.targets - testData.title = dialogData.title - testData.speaker = this.speakerData(); - testData.attribute = attribute; - return new WNGTest(testData) + return this._setupTest(CommonDialog, WNGTest, {attribute}, options) } async setupSkillTest(skill, options = {}) { - let skillObject = this.skills[skill] - - let dialogData = this._baseDialogData(); - dialogData.title = `${game.i18n.localize(skillObject.label)} Test` - dialogData.pool.size = skillObject.total - this._addOptions(dialogData, options) - dialogData.type = "skill" - dialogData.skill = skill - let testData = await RollDialog.create(dialogData) - testData.targets = dialogData.targets - testData.title = dialogData.title - testData.speaker = this.speakerData(); - testData.skill = skill - testData.attribute = skillObject.attribute - return new WNGTest(testData) + return this._setupTest(CommonDialog, WNGTest, {skill}, options) } async setupGenericTest(type, options = {}) { - let dialogData = this._baseDialogData(); - let testClass = WNGTest + options = foundry.utils.mergeObject(options, {fields : {}, [type] : true}) + + if (type == "conviction") + { + type = await Dialog.wait({ + title : game.i18n.localize(`ROLL.CONVICTION`), + buttons : { + corruption : { + label : game.i18n.localize(`ROLL.CORRUPTION`) + }, + mutation : { + label : game.i18n.localize(`ROLL.MUTATION`) + } + }}) + } + switch (type) { case "stealth": - dialogData.pool.size = this.skills.stealth.total; - dialogData.title = game.i18n.localize(`ROLL.STEALTH`); - dialogData.noDn = true; - testClass = StealthRoll; - break; + options.title = game.i18n.localize(`ROLL.STEALTH`); + options.noDn = true; + options.noWrath = true; + return this._setupTest(CommonDialog, StealthRoll, {skill: "stealth"}, options) case "determination": - dialogData.pool.size = this.combat.determination.total - dialogData.title = game.i18n.localize(`ROLL.DETERMINATION`) - dialogData.determination = true; - dialogData.noDn = true; - testClass = DeterminationRoll; - break; - case "conviction": - dialogData.pool.size = this.combat.conviction.total - dialogData.title = game.i18n.localize(`ROLL.CONVICTION`) - break; + options.title = game.i18n.localize(`ROLL.DETERMINATION`) + options.noDn = true; + options.noWrath = true; + return this._setupTest(RollDialog, DeterminationRoll, {pool : this.combat.determination.total,}, options) case "corruption": - dialogData.pool.size = this.combat.conviction.total - dialogData.title = game.i18n.localize(`ROLL.CORRUPTION`) - this._addCorruptionData(dialogData) - testClass = CorruptionTest; - break; + options.title = game.i18n.localize(`ROLL.CORRUPTION`) + options.conviction = true; + return this._setupTest(RollDialog, CorruptionTest, {pool : this.combat.conviction.total}, options) case "mutation": - dialogData.pool.size = this.combat.conviction.total - dialogData.title = game.i18n.localize(`ROLL.MUTATION`) - dialogData.difficulty.target = 3 - testClass = MutationTest; - break; + options.title = game.i18n.localize(`ROLL.MUTATION`) + options.conviction = true; + return this._setupTest(RollDialog, MutationTest, {pool : this.combat.conviction.total}, options) case "fear": - dialogData.pool.size = this.combat.resolve.total - dialogData.title = game.i18n.localize(`ROLL.FEAR`) - dialogData.type == "fear" - testClass = ResolveTest - break; + options.title = game.i18n.localize(`ROLL.FEAR`) + options.resolve = true; + options.noWrath = true; + return this._setupTest(RollDialog, ResolveTest, {pool : this.combat.resolve.total}, options) case "terror": - dialogData.pool.size = this.combat.resolve.total - dialogData.title = game.i18n.localize(`ROLL.TERROR`) - dialogData.type == "terror" - testClass = ResolveTest - break; + options.title = game.i18n.localize(`ROLL.TERROR`) + options.resolve = true; + options.noWrath = true; + return this._setupTest(RollDialog, ResolveTest, {pool : this.combat.resolve.total}, options) case "influence": - dialogData.pool.size = this.resources.influence - dialogData.title = game.i18n.localize(`ROLL.INFLUENCE`) - break; + options.fields.pool = this.resources.influence + options.title = game.i18n.localize(`ROLL.INFLUENCE`) + options.noWrath = true; + return this._setupTest(RollDialog, ResolveTest, {pool : this.combat.resolve.total}, options) default: throw new Error("Unknown roll type: " + type) } - this._addOptions(dialogData, options) - dialogData.type = type - let testData = await RollDialog.create(dialogData) - testData.title = dialogData.title - testData.speaker = this.speakerData(); - testData.type = type - ui.sidebar.activateTab("chat") - return new testClass(testData) } - - async setupWeaponTest(weapon, options={}) { - if (typeof weapon == "string") - weapon = this.items.get(weapon) || await fromUuid(weapon) - - options.combi = weapon.system.combi.document ? await Dialog.confirm({title : "Combi-Weapons", content : "Fire both Combi-Weapons?"}) : false - - let tests = [] - let multi = options.combi ? 2 : 0; - - // If targets, call this function again with single target option - if (game.user.targets.size) + if (game.user.targets.size > 1) { - let targets = Array.from(game.user.targets) - game.user.updateTokenTargets([]) - options.multi = targets.length + multi; - // Function needs to return an array of WeaponTests so need to do some funky stuff to convert - targets.forEach(target => { - options.target = target; - tests.push(this._promptWeaponDialog(weapon, options)) - if (options.combi) - { - tests.push(this._promptWeaponDialog(weapon.system.combi.document, options)) - } - }) - tests = await Promise.all(tests) + return Promise.all(game.user.targets.map(i => { + let optionsCopy = foundry.utils.deepClone(options); + optionsCopy.targets = [i]; + optionsCopy.multi = game.user.targets.size; + return this._setupTest(WeaponDialog, WeaponTest, weapon, optionsCopy); + })) } else { - options.multi = multi; - tests = [await this._promptWeaponDialog(weapon, options)]; - if (options.combi) - { - tests.push(await this._promptWeaponDialog(weapon.system.combi.document, options)) - } + return this._setupTest(WeaponDialog, WeaponTest, weapon, options); } - - - return tests - } - - async _promptWeaponDialog(weapon, options) - { - let dialogData = this._weaponDialogData(weapon, {multi : options.multi, targets : [options.target].filter(t => t)}); - dialogData.title = `${weapon.name} Test` - this._addOptions(dialogData, options) - dialogData.type = "weapon" - dialogData.skill = weapon.isMelee ? "weaponSkill" : "ballisticSkill" - dialogData.attribute = weapon.getSkillFor(this).attribute - let testData = await WeaponDialog.create(dialogData) - testData.targets = dialogData.targets - testData.title = dialogData.title - testData.speaker = this.speakerData(); - testData.itemId = weapon.uuid - testData.skill = dialogData.skill - testData.attribute = dialogData.attribute - return new WeaponTest(testData); } async setupPowerTest(power, options = {}) { - if (typeof power == "string") - power = this.items.get(power) || await fromUuid(power) - - let dialogData = this._powerDialogData(power); - dialogData.title = `${power.name}` - this._addOptions(dialogData, options) - dialogData.type = "power" - dialogData.skill = "psychicMastery" - dialogData.attribute = power.skill.attribute - let testData = await PowerDialog.create(dialogData) - testData.targets = dialogData.targets - testData.title = dialogData.title - testData.speaker = this.speakerData(); - testData.itemId = power.uuid - testData.skill = dialogData.skill - testData.attribute = dialogData.attribute - ui.sidebar.activateTab("chat") - return new PowerTest(testData) + return this._setupTest(PowerDialog, PowerTest, power, options) } async setupAbilityRoll(ability, options = {}) { let testData = { title: ability.name, speaker: this.speakerData(), - itemId: ability.uuid, - damage: {}, - ed: {}, - ap: {} - } - if (ability.hasDamage) { - testData.damage.base = ability.damage.base - testData.damage.bonus = ability.damage.bonus - testData.damage.rank = ability.damage.rank - testData.ed.base = ability.ed.base - testData.ed.bonus = ability.ed.bonus - testData.ed.rank = ability.ed.rank - testData.ap.base = ability.ap.base - testData.ap.bonus = ability.ap.bonus - testData.ap.rank = ability.ap.rank - testData.otherDamage = { - mortalWounds: { value: ability.otherDamage.mortalWounds, bonus : 0 }, - wounds: { value: ability.otherDamage.wounds, bonus : 0 }, - shock: { value: ability.otherDamage.shock, bonus : 0 }, - } - + item: ability, } - ui.sidebar.activateTab("chat") - return new AbilityRoll(testData) - } - _baseDialogData() { - return { - difficulty: { - target: 3, - penalty: 0, - rank: "none" - }, - pool: { - size: 1, - bonus: 0, - rank: "none" - }, - wrath: { - base: this.hasCondition("dying") ? 1 + this.itemTypes["traumaticInjury"].length : 1 - }, - changes: this.allDialogChanges( {targets : Array.from(game.user.targets).map(t => t.actor)}), - actor: this, - targets: Array.from(game.user.targets) - }; - } - - - _weaponDialogData(weapon, options={}) { - - let dialogData = this._baseDialogData() - if (options.targets) + if (ability.system.test.self) { - dialogData.targets = options.targets; - dialogData.changes = this.allDialogChanges({targets: options.targets.map(i => i.actor), vehicle : weapon.actor?.type == "vehicle" ? weapon.actor : null}); - // Weapon dialogs need to get dialog changes separately because of special target handling - } - - if (weapon.Ammo) { - // Add ammo dialog changes if any exist - weapon.Ammo.effects.forEach(e => { - mergeObject(dialogData.changes, e.getDialogChanges()) - }) + return this.setupTestFromItem(ability, {item : ability}); } - dialogData.weapon = weapon - dialogData.skill = weapon.getSkillFor(this) - dialogData.pool.size = dialogData.skill.total; - dialogData.pool.bonus = weapon.attack.base + weapon.attack.bonus; - if (this.isMob) - dialogData.pool.bonus += Math.ceil(this.mob / 2) - dialogData.pool.rank = weapon.attack.rank; - dialogData.damageValues = weapon.damageValues - dialogData.damage = duplicate(weapon.system.damage) - dialogData.ed = duplicate(weapon.system.damage.ed) - dialogData.ap = duplicate(weapon.system.damage.ap) - - if (weapon.isMelee) { - dialogData.damage.base += this.attributes.strength.total - } - - if (weapon.traitList.force) { - if (this.hasKeyword("PSYKER")) - dialogData.damage.bonus += Math.ceil(this.attributes.willpower.total / 2) - else - dialogData.damage.bonus -= 2 - } - - if (dialogData.targets[0]) + if (this.type == "threat" && ability.type == "ability" && ability.system.cost) { - let target = dialogData.targets[0] - let token - dialogData.difficulty.target = target.actor.combat.defence.total - - if (this.isToken) - token = this.token - else - token = this.getActiveTokens()[0]?.document - - if (token) - dialogData.distance = canvas.grid.measureDistances([{ ray: new Ray({ x: token.x, y: token.y }, { x: target.x, y: target.y }) }], { gridSpaces: true })[0] - - if (target.actor.system.combat.size == "large") + if (!(await this.spend("system.resources.ruin", ability.system.cost || 0))) { - dialogData.pool.bonus += 1; - } - else if (target.actor.system.combat.size == "huge") - { - dialogData.pool.bonus += 2; + if (game.counter.ruin > 0) + { + game.wng.RuinGloryCounter.changeCounter(-1, "ruin"); + ui.notifications.notify(`${ability.name}: Spent ${ability.system.cost} Ruin (Counter)`) + } + else + { + ui.notifications.error(`${ability.name}: Not enough Ruin!`) + return; + } } - else if (target.actor.system.combat.size == "gargantuan") + else { - dialogData.pool.bonus += 3; + ui.notifications.notify(`${ability.name}: Spent ${ability.system.cost} Ruin (Personal)`) } - - - // If using melee and target has parry weapon equipped, increase difficulty - if (weapon.system.category == "melee" && target.actor.itemTypes.weapon.find(i => i.equipped && i.traitList["parry"])) - { - dialogData.difficulty.penalty += 1; } + + ui.sidebar.activateTab("chat") + let roll = new AbilityRoll(testData) + await roll.rollTest(); + roll.sendToChat(); + } - } - dialogData.difficulty.penalty += weapon.traitList.unwieldy ? parseInt(weapon.traitList.unwieldy.rating) : 0 - - if (this.hasKeyword("ORK") && weapon.traitList["waaagh!"]) + async setupTestFromItem(item, options) + { + if (typeof item == "string") { - dialogData.pool.bonus += 1; - if (this.combat.wounds.value > 0) - dialogData.ed.bonus += 1 + item = await fromUuid(item); } - if (options.multi > 1) + if (item) { - dialogData.difficulty.penalty += (options.multi - 1) * 2; - dialogData.multi = options.multi + options.appendTitle = ` - ${item.name}`; + return this.setupTestFromData(item.system.test, options); } - - return dialogData } - _powerDialogData(power) { - let dialogData = this._baseDialogData() - dialogData.power = power - dialogData.difficulty.target = power.system.DN - if (!Number.isNumeric(dialogData.difficulty.target)) { - ui.notifications.warn(game.i18n.localize("DIALOG.TARGET_DEFENSE_WARNING")) + async setupTestFromData(data, options={}) + { + let dn = data.dn; + let type = data.type; + let specification = data.specification; + foundry.utils.setProperty(options, "fields.difficulty", dn); + + if (type == "attribute") + { + return this.setupAttributeTest(specification, options) } - dialogData.pool.size = power.skill.total; - return dialogData - } - - _addOptions(dialogData, options) { - dialogData.difficulty.target = options.dn || dialogData.difficulty.target - dialogData.pool.size = options.pool || dialogData.pool.size - dialogData.title = options.title || dialogData.title - delete options.title; - delete options.pool; - delete options.dn; - - mergeObject(dialogData, options); - } - - _addCorruptionData(dialogData) { - let level = game.wng.config.corruptionLevels[this.corruptionLevel] - dialogData.difficulty.penalty += level.dn - } - - speakerData() { - if (this.isToken) { - return { - token: this.token.id, - scene: this.token.parent.id - } + else if (type == "skill") + { + return this.setupSkillTest(specification, options) } - else { - return { - actor: this.id - } + else if (type == "resolve") + { + return this.setupGenericTest(specification, options) + } + else if (type == "corruption") + { + return this.setupGenericTest(specification, options) } } - allDialogChanges({targets=[], vehicle} = {}) { - let effects = this.effects.contents.concat(vehicle?.effects.contents || []); - // Aggregate dialog changes from each effect - let changes = effects.filter(e => !e.disabled).reduce((prev, current) => mergeObject(prev, current.getDialogChanges()), {}) - - if (targets.length) { - let target = targets[0] - let targetChanges = target.effects.filter(e => !e.disabled).reduce((prev, current) => mergeObject(prev, current.getDialogChanges({target : true})), {}) - mergeObject(changes, targetChanges); + async rollDetermination(wounds, message) + { + if (this.statuses.has("exhausted")) + { + return; } - - return changes + let test = await this.setupGenericTest("determination", {message, fields: {wounds}, resolveClose: true}) + return test; } - characterCreation(archetype) { new Dialog({ title: "Character Creation", content: "

    Begin Character Creation?

    ", yes: () => new CharacterCreation({ actor: this, archetype }).render(true), no: async () => { - let species = await game.wng.utility.findItem(archetype.species.id, "species") - let faction = await game.wng.utility.findItem(archetype.faction.id, "faction") + let species = await warhammer.utility.findItemId(archetype.species.id, "species") + let faction = await warhammer.utility.findItemId(archetype.faction.id, "faction") this.createEmbeddedDocuments("Item", [archetype.toObject(), faction?.toObject(), species?.toObject()].filter(i => i)) } }).render(true) @@ -451,7 +252,7 @@ export class WrathAndGloryActor extends WNGDocumentMixin(Actor) { } else if (this.type == "threat" && apply) // If threat, apply archetype statistics { - ui.notifications.notify(`Applying ${archetype.name} Archetype`) + message.push(`Applying ${archetype.name} Archetype`) let actorData = this.toObject(); let items = await archetype.GetArchetypeItems() @@ -498,6 +299,248 @@ export class WrathAndGloryActor extends WNGDocumentMixin(Actor) { } } + async applyDamage(damage=0, {ap=0, shock=0, mortal=0}, {test, damageRoll, token, allowDetermination=true}={}) { + + let resilience = foundry.utils.deepClone(this.system.combat.resilience) + let res = resilience.total || 1 + ap = Math.abs(ap); + + token = token || this.prototypeToken; + + // label, value, description + let modifiers = { + damage : [], + ap : [], + shock: [], + mortal: [], + resilience : [], + wounds : [], + }; + + let addModifierBreakdown = (type, label) => { + for(let mod of modifiers[type]) + { + report.breakdown.push(`${mod.label}: ${HandlebarsHelpers.numberFormat(mod.value, { hash: { sign: true } })} ${label}` + (mod.description ? ` (${mod.description})` : "")) + } + } + + let mortalDetermination = false; + let args = {damage, ap, shock, mortal, test, damageRoll, modifiers, resilience, actor: this} + this.runScripts("preTakeDamage", args) + test?.actor?.runScripts("preApplyDamage", args) + test?.item?.runScripts("preApplyDamage", args) + damage = args.damage; + ap = args.ap; + shock = args.shock; + mortal = args.mortal; + mortalDetermination = args.mortalDetermination; + + let invuln = resilience.invulnerable + if (resilience.forceField) + { + mortalDetermination = true; + } + + let wounds = 0 + + let report = { + message : null, + breakdown : [], + uuid : token?.uuid + } + + if (args.abort) + { + report.message = game.i18n.format(`${token?.name} received no damage`); + report.breakdown = `

    ${args.abort}

    ` + return report; + } + + damage += modifiers.damage.reduce((acc, mod) => acc + mod.value, 0); + ap += modifiers.ap.reduce((acc, mod) => acc + mod.value, 0); + mortal += modifiers.mortal.reduce((acc, mod) => acc + mod.value, 0); + + if (invuln) + { + ap = 0; + } + + if (ap) + { + let resilienceReduction = ap + if (game.settings.get('wrath-and-glory', 'advancedArmour')) + { + resilienceReduction = Math.min(ap, target.system.combat.resilience.armour) + } + addModifierBreakdown("ap", "AP"); + report.breakdown.push(`AP: Reduced Resilience to ${Math.max(0, res - resilienceReduction)} (${res} - ${resilienceReduction})`) + res = Math.max(0, res - resilienceReduction); + } + else if (invuln) + { + report.breakdown.push(`Invulnerable: Ignore AP`); + } + + if (res <= 0) + res = 1 + + if (damage) + { + addModifierBreakdown("damage", "Damage"); + if (res > damage) + { + report.message = game.i18n.format("NOTE.APPLY_DAMAGE_RESIST", {name : token?.name}) + report.breakdown.push(`Resilience: Resisted ${damage} Damage`) + report.resisted = true; + } + + if (res == damage) + { + report.breakdown.push(`Resilience: Suffered 1 Shock (${res} vs. ${damage} Damage)`) + shock++ + } + if (res < damage) + { + wounds = damage - res + report.breakdown.push(`Resilience: ${damage} Damage reduced to ${wounds} Wounds (-${res})`) + } + } + + if (mortal) + { + addModifierBreakdown("mortal", "Mortal Wounds"); + report.breakdown.push(`Mortal Wounds: ${mortal}`) + if (mortalDetermination) + { + report.breakdown.push(`Mortal Wounds: ${mortal} converted to Wounds (${wounds + mortal})`); + wounds += mortal; + mortal = 0; + } + } + + if (wounds && allowDetermination) + { + let determination = await this.rollDetermination(wounds, damageRoll?.message?.id) + if (determination) + { + wounds = determination.result.wounds; + shock += determination.result.shock; + if (determination.result.shockIgnored) + { + report.breakdown.push(`Determination: Ignored ${determination.result.converted} Wounds`) + } + else + { + report.breakdown.push(`Determination: Converted ${determination.result.converted} Wounds to Shock`) + } + report.determination = determination; + } + } + + if (shock && (this.hasCondition("exhausted"))) + { + mortal += shock; + shock = 0; + report.breakdown.push(`Exhausted: ${shock} Shock converted to Mortal Wounds (${mortal})`); + } + + + let updateObj = {} + args = {wounds, shock, mortal, report, updateObj, actor: this, test, damageRoll} + this.runScripts("takeDamage", args) + test?.actor?.runScripts("applyDamage", args) + test?.item?.runScripts("applyDamage", args) + + // If you want to modify wounds before determination, use damage modifier + // modifier.wounds is for modifying wounds after determination + wounds += modifiers.wounds.reduce((acc, mod) => acc + mod.value, 0); + shock += modifiers.shock.reduce((acc, mod) => acc + mod.value, 0); + addModifierBreakdown("shock", "Shock"); + addModifierBreakdown("wounds", "Wounds"); + + + shock = Math.max(shock, 0); + wounds = Math.max(wounds, 0); + mortal = Math.max(mortal, 0); + + if (shock > 0) + { + let newShock = this.system.combat.shock.value + shock + updateObj["system.combat.shock.value"] = newShock; + if (newShock >= this.system.combat.shock.max) + { + await this.addCondition("exhausted") + } + } + if (wounds > 0 || mortal > 0) + { + let newWounds = this.system.combat.wounds.value + wounds + mortal; + updateObj["system.combat.wounds.value"] = newWounds; + if (newWounds >= this.system.combat.wounds.max) + { + await this.addCondition("dying") + } + } + let applyDamageEffects = false + if (shock + wounds + mortal > 0 && !args.abort) // if shock or wounds or mortal + { + report.breakdown.push(game.i18n.format("NOTE.APPLY_DAMAGE", {wounds : wounds + mortal, shock, name : token?.name})); + report.message = game.i18n.format(`${token?.name} received damage`); + applyDamageEffects = true; + } + else + { + report.message = game.i18n.format(`${token?.name} received no damage`); + } + + if (args.abort) + { + report.breakdown = `

    ${args.abort}

    ` + } + else + { + report.breakdown = `` + } + + report.wounds = wounds + mortal; + report.mortal = mortal; + report.shock = shock; + if (!args.abort) + { + await this.update(updateObj); + } + else if (args.abort) + { + return report; + } + + let damageEffects = test?.damageEffects || [] + if (damageEffects.length && applyDamageEffects) + { + this.applyEffect({effects : damageEffects, messageId : test.message.id}) + } + + return report; + } + + applyHealing({wounds=0, shock=0}, {messageData={}, suppressMessage=false}) + { + let newWounds = this.system.combat.wounds.value - wounds; + let newShock = this.system.combat.shock.value - shock; + + this.update({"system.combat.wounds.value" : newWounds, "system.combat.shock.value" : newShock}); + + let token = this.getActiveTokens()[0]; + let name = token ? token.name : this.prototypeToken.name; + let content = `${name} healed ${[shock ? (shock + " Shock") : null, wounds ? (wounds + " Wounds") : null].filter(i => i).join(" and ")}`; + if (!suppressMessage) + { + ChatMessage.create(foundry.utils.mergeObject({content, speaker : {alias : name}, flavor : "Healing"}, messageData)); + } + return {shock : newShock, wounds : newWounds} + } + + //#endregion get Size() { @@ -549,9 +592,7 @@ export class WrathAndGloryActor extends WNGDocumentMixin(Actor) { if (!existing) { effect.name = game.i18n.localize(effect.name) - effect.statuses = [effect.id]; - delete effect.id - return this.createEmbeddedDocuments("ActiveEffect", [effect]) + return this.createEmbeddedDocuments("ActiveEffect", [effect], {condition: true}) } } @@ -581,8 +622,14 @@ export class WrathAndGloryActor extends WNGDocumentMixin(Actor) { } - hasKeyword(keyword) { - return !!this.itemTypes.keyword.find(i => i.name == keyword) + hasKeyword(keyword) + { + if (typeof keyword == "string") + { + keyword = [keyword]; + } + + return keyword.some(k => this.keywords.has(k)); } @@ -595,7 +642,7 @@ export class WrathAndGloryActor extends WNGDocumentMixin(Actor) { get resources() { return this.system.resources } get corruption() { return this.system.corruption } get notes() { return this.system.notes } - get mob() { return this.system.mob } + get mob() { return this.system.mob.value } get traitsAvailable() { if (this.type == "vehicle") diff --git a/scripts/document/effect.js b/scripts/document/effect.js index bc226df..95e78cd 100644 --- a/scripts/document/effect.js +++ b/scripts/document/effect.js @@ -1,166 +1,141 @@ -export default class WrathAndGloryEffect extends ActiveEffect { +export default class WrathAndGloryEffect extends WarhammerActiveEffect { - /** @override - * Adds support for referencing actor data - * */ - apply(actor, change) { - if (change.value.includes("@")) - actor.derivedEffects.push((change)) - else - super.apply(actor, change) - } + // Config object used by systems to hide properties that aren't relevant + static CONFIGURATION = { + zones : false, + exclude : {}, + bracket : ["[", "]"] + }; - fillDerivedData(actor, change) + async resistEffect() { - try { - change.value = (0, eval)(Roll.replaceFormulaData(change.value, actor.getRollData())).toString(); + let result = await super.resistEffect(); + if (result === false || result === true) + { + return result; } - catch(e) + + let transferData = this.system.transferData; + + //TODO + let test; + let options = {appendTitle : " - " + this.name, resist : [this.key].concat(this.sourceTest?.item?.type || []), resistingTest : this.sourceTest, fields: {}}; + if (this.sourceTest && this.sourceTest.result?.test) { - console.error("Something went wrong when filling derived effect: " + e) + transferData.avoidTest.dn = this.sourceTest.result.test.dn; + } + if (transferData.avoidTest.value == "item") + { + test = await this.actor.setupTestFromItem(this.item, options); + } + else if (transferData.avoidTest.value == "custom") + { + test = await this.actor.setupTestFromData(transferData.avoidTest, options); } - } - get item() { - if (this.parent && this.parent.documentName == "Item") - return this.parent - else if (this.origin && this.parent.documentName == "Actor") + if (!transferData.avoidTest.reversed) { - let origin = this.origin.split(".") - if (origin[1] == this.parent.id) // If origin ID is same as parent ID + // If the avoid test is marked as opposed, it has to win, not just succeed + if (transferData.avoidTest.opposed && this.sourceTest) { - if (origin[3]) - { - return this.parent.items.get(origin[3]) - } + return test.result.success > this.sourceTest.result.success; + } + else + { + return test.result.isSuccess; } } - } - - getDialogChanges({target = false}={}) { - let allChanges = {} - this.changes - .filter((c) => c.mode == (target ? 7 : 6)) - .forEach((c, i) => + else // Reversed - Failure removes the effect { - let dialogChange = mergeObject(foundry.utils.deepClone(c), { - conditional : this.changeConditionals[i] || {}, - target : !!target, - document: this - }) - - if (!dialogChange.conditional.description) + // If the avoid test is marked as opposed, it has to win, not just succeed + if (transferData.avoidTest.opposed && this.sourceTest) { - dialogChange.conditional.description = this.name; + return test.result.success < this.sourceTest.result.success; } - - if (target) + else { - dialogChange.conditional.description = `Target: ${dialogChange.conditional.description}`; + return !test.result.isSuccess; } - - if (this.parent?.documentName == "Actor") - this.fillDerivedData(this.parent, dialogChange) - - allChanges[randomID()] = dialogChange - }) - - return allChanges + } } - /** - * Takes a test object and returns effect data populated with the results and overcasts computed - * - * @param {Test} test - */ - static populateEffectData(effectData, test, item) - { - effectData.origin = test.actor.uuid - - effectData.statuses = effectData.statuses || effectData.name.slugify() - - if(!item) - item = test.item + + get testDisplay() { - // Prioritize test result duration over item duration (test result might be overcasted) - let duration = test.result.duration || item.duration - if (duration) + let avoidTestData + if (this.system.transferData.avoidTest.value == "custom") { - if (duration.unit == "round") - effectData.duration.rounds = parseInt(duration.value) - else if (duration.unit == "minute") - effectData.duration.seconds = parseInt(duration.value) * 60 - else if (duration.unit == "hour") - effectData.duration.seconds = parseInt(duration.value) * 60 * 60 - else if (duration.unit == "day") - effectData.duration.seconds = parseInt(duration.value) * 60 * 60 * 24 + avoidTestData = this.system.transferData.avoidTest; } - - // Some effects may need to take from test data to fill its change value (to match with possible overcasts) - // These effects have a change value of `@test.result.` - for(let change of effectData.changes) + else if (this.system.transferData.avoidTest.value == "item") { - let split = change.value.split(".") - // Remove @test and replace it with the value - let value = change.value - if (split[0] == "@test") - { - // Remove @test and get the property from the test (@test.result.damage.total -> result.damage.total -> actual value) - split.splice(0, 1) - value = split.join(".") - value = getProperty(test, value) - if (Number.isNumeric(value)) - change.value = Number(value) - else - change.value = 0 - } + avoidTestData = this.item.system.test; + } + else + { + return "" } - return effectData - + if (avoidTestData.type == "attribute") + return `DN ${avoidTestData.dn} ${game.wng.config.attributes[avoidTestData.specification]} Test` + if (avoidTestData.type == "skill") + return `DN ${avoidTestData.dn} ${game.wng.config.skills[avoidTestData.specification]} (${game.wng.config.attributeAbbrev[game.wng.config.skillAttribute[avoidTestData.specification]]}) Test` + if (avoidTestData.type == "resolve") + return `DN ${avoidTestData.dn} ${game.wng.config.resolveTests[avoidTestData.specification]} Test` + if (avoidTestData.type == "corruption") + return `DN ${avoidTestData.dn} Corruption Test` } - get changeConditionals() { - return (getProperty(this, "flags.wrath-and-glory.changeCondition") || {}) - } - get hasRollEffect() { - return this.changes.some(c => c.mode == 0) + /** @override + * Adds support for referencing actor data + * */ + apply(actor, change) { + if (change.value.includes("@")) + { + log(`Deferring ${this.name} for ${this.parent?.name}`) + if (change.value == "@doom" && !game.ready) + actor.postReadyEffects.push(change) + else + actor.derivedEffects.push(change) + } + else + { + log(`Applying ${this.name} to ${this.parent?.name}`) + super.apply(actor, change) + } } - get sourceName() { - if (!this.origin) - return super.sourceName + fillDerivedData(actor, change) { + try { + + if (change.value.includes("@test")) { + let path = change.value.replace("@test.", ""); + change.value = getProperty(this.sourceTest, path)?.toString() || "0"; + } + else { - let data = this.origin.split(".") - if (data.length == 4) { - let item = this.parent.items.get(data[3]) - if (item) - return item.name + let data = (0, eval)(Roll.replaceFormulaData(change.value, actor.getRollData())) + //Foundry Expects to find a String for numbers + //Raw Numbers don't work anymore + if (typeof data === "number") { + change.value = data.toString(); + } else { + change.value = data; + } + } + } + catch (e) { + change.value = "0"; } - return super.sourceName } + get isCondition() { return CONFIG.statusEffects.map(i => i.id).includes(Array.from(this.statuses)[0]) } - static get numericTypes() { - return [ - "pool.base", - "pool.bonus", - "difficulty.base", - "difficulty.bonus", - "damage.base", - "damage.bonus", - "ed.base", - "ed.bonus", - "ap.base", - "ap.bonus", - "wrath" - ] - } } \ No newline at end of file diff --git a/scripts/document/item.js b/scripts/document/item.js index e98cfc7..2523f22 100644 --- a/scripts/document/item.js +++ b/scripts/document/item.js @@ -1,44 +1,15 @@ import WNGUtility from "../common/utility.js"; -export class WrathAndGloryItem extends Item { +export class WrathAndGloryItem extends WarhammerItem { - constructor(data, context) - { - super(data, context) - if (context && context.archetype) - { - this.archetype = context.archetype.item; - this.archetypeItemIndex = context.archetype.index; - this.archetypeItemPath = context.archetype.path - } - } - - // Upon creation, assign a blank image if item is new (not duplicated) instead of mystery-man default - async _preCreate(data, options, user) { - if (data._id && !this.isOwned) - options.keepId = WNGUtility._keepID(data._id, this) - - await super._preCreate(data, options, user) - } + static bracket = ["[", "]"]; _preUpdate(updateData, options, user) { + // TODO Move this to model if (getProperty(updateData, "system.type") == "corruption") setProperty(updateData, "system.specification", "corruption") } - prepareBaseData() - { - this.system.computeBase(); - } - prepareDerivedData() - { - this.system.computeDerived(); - } - - prepareOwnedData() { - this.system.computeOwned() - } - async sendToChat() { const item = new CONFIG.Item.documentClass(this._source) if (item.img.includes("/unknown")) { @@ -62,37 +33,28 @@ export class WrathAndGloryItem extends Item { return { text: this.description } } + // TODO move this to model handleArchetypeItem(item) { if (["weapon", "weaponUpgrade", "armour", "gear", "ammo", "augmentic"].includes(item.type)) { - let wargear = duplicate(this.wargear); - wargear.push({ - name : item.name, - id : item.uuid, - type: "item", - diff : {} - }) - let groups = this.addToGroup({index: wargear.length - 1, type : "item"}) - return this.update({"system.wargear" : wargear, "system.groups" : groups}) + ui.notification.error("Open the Choice Config window to add Wargear.") } if(item.type == "ability") { - return this.update({"system.ability.id" : item.uuid, "system.ability.name" : item.name}) + return this.update(this.system.ability.set(item)); } if(item.type == "faction") { - return this.update({"system.faction.id" : item.uuid, "system.faction.name" : item.name}) + return this.update(this.system.faction.set(item)); } if(item.type == "species") { - return this.update({"system.species.id" : item.uuid, "system.species.name" : item.name}) + return this.update(this.system.species.set(item)); } if (item.type == "talent") { - let talents = duplicate(this.suggested.talents) - talents.push({"id" : item.id, "name" : item.name}) - this.update({"system.suggested.talents" : talents}) + this.update(this.system.suggested.talents.add(item)); } if (item.type == "keyword") { @@ -102,37 +64,15 @@ export class WrathAndGloryItem extends Item { } } + // TODO move this to model handleSpeciesItem(item) { if(item.type == "ability") { - let abilities = duplicate(this.abilities); - abilities.push({id : item.uuid, name : item.name}) - return this.update({"system.abilities" : abilities}) + return this.update(this.system.abilities.add(item)); } } - addToGroup(object) - { - let groups = duplicate(this.groups) - object.groupId = randomID() - groups.items.push(object) - return groups - } - - resetGroups() - { - this.update({ "system.groups": {type: "and", groupId: "root", items : Array.fromRange(this.wargear.length).map(i => {return {type: "item", index : i, groupId : randomID()}})} }) // Reset item groupings - } - - _deleteIndex(index, path) - { - let array = duplicate(getProperty(this, path)) - array.splice(index, 1) - this.update({ [path]: array}) - } - - async addCondition(effect) { if (typeof (effect) === "string") effect = duplicate(CONFIG.statusEffects.find(e => e.id == effect)) @@ -149,7 +89,7 @@ export class WrathAndGloryItem extends Item { effect.name = game.i18n.localize(effect.name) effect.statuses = [effect.id]; delete effect.id - return this.createEmbeddedDocuments("ActiveEffect", [effect]) + return this.createEmbeddedDocuments("ActiveEffect", [effect], {condition: true}) } } @@ -175,40 +115,10 @@ export class WrathAndGloryItem extends Item { return existing } - - /** - * Override update to account for archetype parent - */ - async update(data={}, context={}) - { - // If this item is from an archetype entry, update the diff instead of the actual item - // I would like to have done this is the item's _preCreate but the item seems to lose - // its "archetype" reference so it has to be done here - // TODO: Current Issue - changing a property, then changing back to the original value - // does not work due to `diffObject()` - - if (this.archetype) { - // Get the archetype's equipment, find the corresponding object, add to its diff - - let list = duplicate(getProperty(this.archetype, this.archetypeItemPath)) - let item = list[this.archetypeItemIndex]; - mergeObject( // Merge current diff with new diff - item.diff, - diffObject(this.toObject(), data), - { overwrite: true }) - - // If the diff includes the item's name, change the name stored in the archetype - if (item.diff.name) - item.name = item.diff.name - else - item.name = this.name - - this.archetype.update({ [`${this.archetypeItemPath}`]: list }) - data={} - } - return super.update(data, context) - } - + getTestData() + { + return this.system.test; + } // @@@@@@ FORMATTED GETTERs @@@@@@ @@ -227,21 +137,35 @@ export class WrathAndGloryItem extends Item { return this.system.traits.obj; } + hasKeyword(keyword) + { + + if (typeof keyword == "string") + { + keyword = [keyword]; + } + + let keywords = this.keywords.split(",").map(i => i.trim()); + + return keyword.some(k => keywords.includes(k)); + } + + // TODO move this to model async GetArchetypeItems() { let items = []; - let species = await game.wng.utility.findItem(this.species.id, "species") - let faction = game.wng.utility.findItem(this.faction.id, "faction") + let species = await this.system.species.document; + let faction = await this.system.faction.document; - let speciesAbilities = species.abilities.map(i => game.wng.utility.findItem(i.id, "ability")) - let archetypeAbility = game.wng.utility.findItem(this.ability.id, "ability") + let speciesAbilities = await species.system.abilities.awaitDocuments(); + let archetypeAbility = await this.system.ability.document; let keywords = this.keywords.map(WNGUtility.getKeywordItem) // Get all archetype talents/wargear, merge with diff - for (let i of this.suggested.talents.concat(this.wargear.filter(k => k.id))) + for (let i of this.suggested.talents.list.concat(this.wargear.filter(k => k.id))) { - let item = await game.wng.utility.findItem(i.id) + let item = await warhammer.utility.findItemId(i.id) if (item) { item = item.toObject(); @@ -346,43 +270,10 @@ export class WrathAndGloryItem extends Item { } journal.sheet.render(true, {pageId : page?.id}) } - - get rollable() { - if (this.type == "ability") { - if (this.abilityType == "determination") return true - if (this.hasDamage) return true - if (this.hasTest) return true - } - - } - get hasDamage() { - return (this.damage && (this.damage.base || this.damage.bonus || this.damage.rank != "none")) || (this.damage?.ed && (this.damage?.ed.base || this.damage?.ed.bonus || this.damage?.ed.rank != "none") || (this.damage?.otherDamage.shock || this.damage?.otherDamage.wounds || this.damage?.otherDamage.mortalWounds)) - } - - get damageValues() { - if (this.traitList.brutal) - return { - 1: 0, - 2: 0, - 3: 1, - 4: 1, - 5: 2, - 6: 2 - } - else - return { - 1: 0, - 2: 0, - 3: 0, - 4: 1, - 5: 1, - 6: 2 - } - } get hasTest() { - return this.test && Number.isNumeric(this.test.dn) && this.test.type + return this.test && this.test.type } // @@@@@@ TYPE GETTERS @@@@@@ diff --git a/scripts/document/mixin.js b/scripts/document/mixin.js deleted file mode 100644 index 0c907f6..0000000 --- a/scripts/document/mixin.js +++ /dev/null @@ -1,66 +0,0 @@ -import WNGUtility from "../common/utility"; - -export default WNGDocumentMixin = (cls) => class extends cls -{ - - async _preCreate(data, options, user) - { - if (data._id) - { - options.keepId = WNGUtility._keepID(data._id, this); - } - - await super._preCreate(data, options, user); - await this.system._preCreate(data, options); - } - - async _preUpdate(data, options, user) - { - await super._preUpdate(data, options, user); - await this.system._preUpdate(data, options); - } - - async _preDelete(options, user) - { - await super._preDelete(options, user); - await this.system._preDelete(options, user) - } - - async _onUpdate(data, options, user) - { - await super._onUpdate(data, options, user); - await this.system._onUpdate(data, options, user); - } - - async _onCreate(data, options, user) - { - await super._onCreate(data, options, user); - await this.system._onCreate(data, options, user); - } - - async _onDelete(options, user) - { - await super._onDelete(options, user); - await this.system._onDelete(options, user) - } - - // Assigns a property to all datamodels are their embedded models - propagateDataModels(model, name, value) - { - if (model instanceof foundry.abstract.DataModel && !model[name]) - { - Object.defineProperty(model, name, { - value, - enumerable : false - }); - } - - for(let property in model) - { - if (model[property] instanceof foundry.abstract.DataModel) - { - this.propagateDataModels(model[property], name, value); - } - } - } -}; \ No newline at end of file diff --git a/scripts/hooks/actor.js b/scripts/hooks/actor.js deleted file mode 100644 index d55e93c..0000000 --- a/scripts/hooks/actor.js +++ /dev/null @@ -1,29 +0,0 @@ -export default function() { - Hooks.on("updateActor", (actor, data, options, userId) => { - if (userId == game.user.id) - { - if (actor.type != "vehicle") - { - if (actor.combat.wounds.value && actor.getFlag("wrath-and-glory", "autoWounded")) - { - if (!actor.hasCondition("wounded")) - actor.addCondition("wounded") - } - else if (actor.hasCondition("wounded") && actor.getFlag("wrath-and-glory", "autoWounded")) - actor.removeCondition("wounded") - - - if (actor.combat.shock.value > actor.combat.shock.max && actor.getFlag("wrath-and-glory", "autoExhausted")) - { - if (!actor.hasCondition("exhausted")) - actor.addCondition("exhausted", {"wrath-and-glory.auto" : true}) // Auto flag for auto deletion - } - else if (actor.hasCondition("exhausted") && actor.getFlag("wrath-and-glory", "autoExhausted")) // If not auto added, don't auto delete - { - if (actor.hasCondition("exhausted").getFlag("wrath-and-glory", "auto")) - actor.removeCondition("exhausted") - } - } - } - }) -} \ No newline at end of file diff --git a/scripts/hooks/chat.js b/scripts/hooks/chat.js index fbf2a6c..d3d07f4 100644 --- a/scripts/hooks/chat.js +++ b/scripts/hooks/chat.js @@ -15,5 +15,17 @@ export default function() { }) } + // Remove damage breakdown if user shouldn't see details + if (message.type == "damage") + { + html.find(".report").each((i, element) => { + let actor = fromUuidSync(element.dataset?.uuid); + if (actor && !actor.isOwner) + { + element.dataset.tooltip = ""; + } + }) + } + }) } \ No newline at end of file diff --git a/scripts/hooks/effects.js b/scripts/hooks/effects.js deleted file mode 100644 index 777d0ba..0000000 --- a/scripts/hooks/effects.js +++ /dev/null @@ -1,6 +0,0 @@ -export default function() { - Hooks.on("preCreateActiveEffect", (effect, data, options, user) => { - if (["psychicPower","weaponUpgrade","weapon","ammo","faction"].includes(effect.parent?.type)) // Default effects to not transfer to actor for these item types - effect.updateSource({"transfer" : false}) - }) -} \ No newline at end of file diff --git a/scripts/hooks/entryContext.js b/scripts/hooks/entryContext.js index a2ba1b0..d8c964c 100644 --- a/scripts/hooks/entryContext.js +++ b/scripts/hooks/entryContext.js @@ -2,28 +2,31 @@ import EditTestForm from "../apps/edit-test.js"; export default function() { Hooks.on("getChatLogEntryContext", (html, options) => { - let canApply = li => li.find(".damageRoll").length && canvas.tokens.controlled.length > 0; + let canApply = li => { + let msg = game.messages.get(li.attr("data-message-id")) + return game.user.isGM && msg.type == "damage"; + } let canRerollFailed = li => { let msg = game.messages.get(li.attr("data-message-id")) - let test = msg.getTest() + let test = msg.system.test if (test) - return !test.context.rerollFailed && (msg.isAuthor || msg.isOwner) + return !test.context.rerollFailed && (msg.isAuthor || msg.isOwner) && msg.type == "test" } let canRerollSelected = li => { - return li.find(".selected").length// && !li.find(".shifted").length + return li.find(".selected").length; } let canEdit = li => { let msg = game.messages.get(li.attr("data-message-id")) - let test = msg.getTest() + let test = msg.system.test if (test) return msg.isAuthor || msg.isOwner } let canShift = li => { let msg = game.messages.get(li.attr("data-message-id")) - let test = msg.getTest() + let test = msg.system.test let selected = Array.from(li.find(".selected")).map(i => Number(i.dataset.index)) // If all selected dice are shiftable and number of selected <= shifts possible @@ -32,28 +35,29 @@ export default function() { let canShiftDamage = li => { let msg = game.messages.get(li.attr("data-message-id")) - let test = msg.getTest() + let test = msg.system.test return canShift(li) && test.doesDamage && (msg.isAuthor || msg.isOwner) } let canShiftPotency = li => { let msg = game.messages.get(li.attr("data-message-id")) - let test = msg.getTest() - return canShift(li) && test.testData.potency?.length && (msg.isAuthor || msg.isOwner) + let test = msg.system.test + return canShift(li) && test.testData?.potency?.length && (msg.isAuthor || msg.isOwner) } let canResetPotency = li => { let msg = game.messages.get(li.attr("data-message-id")) - let test = msg.getTest() + let test = msg.system.test if (!test) return; - return test.testData.potency?.length && test.testData.potency.some(p => p.allocation) && (msg.isAuthor || msg.isOwner) + return test.testData?.potency?.length && test.testData?.potency.some(p => p.allocation) && (msg.isAuthor || msg.isOwner) } let canClearReroll = li => { - let test = game.messages.get(li.attr("data-message-id")).getTest() - return test && game.user.isGM && test.testData.rerolls.length + let msg = game.messages.get(li.attr("data-message-id")) + let roll = msg.system.test || msg.system.damage; + return roll && game.user.isGM && roll.hasRerolled } let canUnshift = li => { @@ -69,7 +73,7 @@ export default function() { condition: canRerollFailed, callback: async li => { let message = game.messages.get(li.attr("data-message-id")); - let test = message.getTest(); + let test = message.system.test; let actor = test.actor; if (actor.type == "agent") { @@ -101,7 +105,7 @@ export default function() { condition: canEdit, callback: async li => { let message = game.messages.get(li.attr("data-message-id")); - let test = message.getTest(); + let test = message.system.test; new EditTestForm(test).render(true) } }, @@ -111,9 +115,9 @@ export default function() { condition: canRerollSelected, callback: async li => { let message = game.messages.get(li.attr("data-message-id")); - let test = message.getTest(); + let roll = message.system.test || message.system.damage; let selected = Array.from(li.find(".selected")).map(i => Number(i.dataset.index)) - test.reroll(selected) + roll.reroll(selected) } }, { @@ -122,8 +126,8 @@ export default function() { condition: canClearReroll, callback: async li => { let message = game.messages.get(li.attr("data-message-id")); - let test = message.getTest(); - test.clearRerolls() + let roll = message.system.test || message.system.damage; + roll.clearRerolls() } }, { @@ -132,7 +136,7 @@ export default function() { condition: canResetPotency, callback: async li => { let message = game.messages.get(li.attr("data-message-id")); - let test = message.getTest(); + let test = message.system.test; test.resetAllocation() } }, @@ -142,7 +146,7 @@ export default function() { condition: canShift, callback: async li => { let message = game.messages.get(li.attr("data-message-id")); - let test = message.getTest(); + let test = message.system.test; let shifted = Array.from(li.find(".selected")).map(i => parseInt(i.dataset.index)) test.shift(shifted, "other") } @@ -153,7 +157,7 @@ export default function() { condition: canShiftDamage, callback: async li => { let message = game.messages.get(li.attr("data-message-id")); - let test = message.getTest(); + let test = message.system.test; let shifted = Array.from(li.find(".selected")).map(i => parseInt(i.dataset.index)) test.shift(shifted, "damage") } @@ -164,7 +168,7 @@ export default function() { condition: canShift, callback: async li => { let message = game.messages.get(li.attr("data-message-id")); - let test = message.getTest(); + let test = message.system.test; let shifted = Array.from(li.find(".selected")).map(i => parseInt(i.dataset.index)) test.shift(shifted, "glory") @@ -181,7 +185,7 @@ export default function() { condition: canShiftPotency, callback: async li => { let message = game.messages.get(li.attr("data-message-id")); - let test = message.getTest(); + let test = message.system.test; let shifted = Array.from(li.find(".selected")).map(i => parseInt(i.dataset.index)) test.shift(shifted, "potency") } @@ -192,91 +196,20 @@ export default function() { condition: canUnshift, callback: async li => { let message = game.messages.get(li.attr("data-message-id")); - let test = message.getTest(); + let test = message.system.test; test.unshift() } }, { name: "BUTTON.ApplyDamage", - icon: '', + icon: '', condition: canApply, callback: li => { - let test = game.messages.get(li.attr("data-message-id")).getTest(); - canvas.tokens.controlled.forEach(t => _dealDamageToTarget(test, t.actor)); + let damage = game.messages.get(li.attr("data-message-id")).system.damage; + damage.applyToTargets(); } } - // { - // name: "BUTTON.ApplyDamageArmourAP", - // icon: '', - // condition: canApply, - // callback: li => { - // let test = game.messages.get(li.attr("data-message-id")).getTest(); - // canvas.tokens.controlled.forEach(t => _dealDamageToTarget(test, t.actor, true, true)); - // } - // } ) }); -} - -function _dealDamageToTarget(test, target) { - let ap = Math.abs(test.result.damage.ap) || 0 - let damage = test.result.damage.total + (test.result.damage.other?.wounds?.total || 0) - let res = target.combat.resilience.total || 1 - let invuln = target.combat.resilience.invulnerable - let promise - - let addWounds = 0; - let addShock = 0; - - if (game.settings.get('wrath-and-glory', 'advancedArmour')) - { - if (!invuln) - res -= (Math.min(ap, target.system.combat.resilience.armour)) - } - else - { - if (!invuln) - res -= ap - } - - if (res <= 0) - res = 1 - - if (res > damage) - ui.notifications.notify(game.i18n.format("NOTE.APPLY_DAMAGE_RESIST", {name : target.prototypeToken.name})) - - if (res == damage) - { - addShock++ - } - - if (res < damage) - { - addWounds = damage - res - if (addWounds <= 0) - addWounds = 0 - - } - addWounds += test.result.damage.other?.mortalWounds?.total || 0 - - if (test.result.damage.other?.shock?.total) - { - addShock += test.result.damage.other.shock.total - } - - let updateObj = {} - if (addShock) - { - updateObj["system.combat.shock.value"] = target.combat.shock.value + 1 - ui.notifications.notify(game.i18n.format("NOTE.APPLY_DAMAGE_SHOCK", {name : target.prototypeToken.name})); -} - if (addWounds) - { - updateObj["system.combat.wounds.value"] = target.combat.wounds.value + addWounds; - ui.notifications.notify(game.i18n.format("NOTE.APPLY_DAMAGE", {damage : addWounds, name : target.prototypeToken.name})); - } - - target.update(updateObj); - return promise -} +} \ No newline at end of file diff --git a/scripts/hooks/item.js b/scripts/hooks/item.js deleted file mode 100644 index 762ba86..0000000 --- a/scripts/hooks/item.js +++ /dev/null @@ -1,14 +0,0 @@ -export default function() { - Hooks.on("preCreateItem", (item, options, userId) => { - if (["species", "archetype", "faction"].includes(item.type) && item.isOwned) - { - - // If actor already owns species, archetype, or faction, replace it - if (item.parent[item.type]) - { - item.parent[item.type].update(item.toObject()) - return false - } - } - }) -} \ No newline at end of file diff --git a/scripts/hooks/ready.js b/scripts/hooks/ready.js index 1b0b452..1bf0575 100644 --- a/scripts/hooks/ready.js +++ b/scripts/hooks/ready.js @@ -1,25 +1,10 @@ -import {migrateWorld} from "../common/migration.js" import FoundryOverrides from "../common/overrides.js" export default function() { Hooks.once("ready", () => { - migrateWorld(); game.counter.render(true) - game.wng.tags.createTags(); - - if (game.release.generation == 12) - { - ui.notifications.warn("Please note that the Wrath & Glory system has not been made fully compatible with V12 and issues may occur.") - } - }); - - CONFIG.ChatMessage.documentClass.prototype.getTest = function () { - if (hasProperty(this, "flags.wrath-and-glory.testData")) - return game.wng.rollClasses.WNGTest.recreate(this.getFlag("wrath-and-glory", "testData")) - } - - FoundryOverrides(); - + FoundryOverrides(); + }); } diff --git a/scripts/hooks/settings.js b/scripts/hooks/settings.js index 73b996d..80ddb50 100644 --- a/scripts/hooks/settings.js +++ b/scripts/hooks/settings.js @@ -1,11 +1,11 @@ export default function () { - game.settings.register("wrath-and-glory", "worldSchemaVersion", { - name: "World Version", + game.settings.register("wrath-and-glory", "systemMigrationVersion", { + name: "System Migration Version", hint: "Used to automatically upgrade worlds data when the system is upgraded.", scope: "world", config: false, - default: 0, - type: Number, + default: "1.0.0", + type: String, }); game.settings.register('wrath-and-glory', 'initiativeRollOption', { diff --git a/scripts/hooks/sidebar.js b/scripts/hooks/sidebar.js deleted file mode 100644 index d5e2dcf..0000000 --- a/scripts/hooks/sidebar.js +++ /dev/null @@ -1,18 +0,0 @@ -import BugReportFormWNG from "../apps/bug-report.js"; - -export default function() -{ - Hooks.on("renderSidebarTab", async (app, html) => { - if (app.options.id == "settings") - { - let button = $(``) - - button.click(ev => { - new BugReportFormWNG().render(true); - }) - - button.insertAfter(html.find("#game-details")) - - } - }) -} \ No newline at end of file diff --git a/scripts/model/actor/agent.js b/scripts/model/actor/agent.js index 8d17c94..fe2f11d 100644 --- a/scripts/model/actor/agent.js +++ b/scripts/model/actor/agent.js @@ -5,6 +5,7 @@ import { AgentAttributesModel } from "./components/attributes"; const fields = foundry.data.fields; export class AgentModel extends StandardWNGActorModel { + static defineSchema() { let schema = super.defineSchema(); schema.attributes = new fields.EmbeddedDataField(AgentAttributesModel) @@ -91,10 +92,5 @@ export class AgentModel extends StandardWNGActorModel { this.experience.current = this.experience.total - this.experience.spent; - //SINGETON TYPES - this.bio.origin.value = this.bio.origin.value || this.parent.faction?.backgrounds.origin.find(b => b.active)?.description - this.bio.accomplishment.value = this.bio.accomplishment.value || this.parent.faction?.backgrounds.accomplishment.find(b => b.active)?.description - this.bio.goal.value = this.bio.goal.value || this.parent.faction?.backgrounds.goal.find(b => b.active)?.description - } } diff --git a/scripts/model/actor/components/base.js b/scripts/model/actor/components/base.js deleted file mode 100644 index ae56141..0000000 --- a/scripts/model/actor/components/base.js +++ /dev/null @@ -1,47 +0,0 @@ - -export class BaseActorModel extends foundry.abstract.DataModel { - - static defineSchema() - { - return {}; - } - - async _preCreate(data, options, user) - { - } - - async _preUpdate(data, options, user) - { - } - - async _preDelete(options, user) - { - - } - - async _onUpdate(data, options, user) - { - - } - - async _onCreate(data, options, user) - { - - } - - async _onDelete(options, user) - { - - } - - computeBase() - { - - } - - computeDerived() - { - - } - -} diff --git a/scripts/model/actor/components/combat.js b/scripts/model/actor/components/combat.js index a990cd9..7763d1e 100644 --- a/scripts/model/actor/components/combat.js +++ b/scripts/model/actor/components/combat.js @@ -11,18 +11,19 @@ export class CombatModel extends foundry.abstract.DataModel { total: new foundry.data.fields.NumberField() }), wounds: new foundry.data.fields.SchemaField({ - value: new foundry.data.fields.NumberField(), + value: new foundry.data.fields.NumberField({min : 0, initial : 0}), bonus: new foundry.data.fields.NumberField(), max: new foundry.data.fields.NumberField() }), determination: new foundry.data.fields.SchemaField({ bonus: new foundry.data.fields.NumberField(), + attribute: new foundry.data.fields.StringField({initial: "toughness"}), total: new foundry.data.fields.NumberField() }), shock: new foundry.data.fields.SchemaField({ - value: new foundry.data.fields.NumberField(), + value: new foundry.data.fields.NumberField({initial : 0, min : 0}), bonus: new foundry.data.fields.NumberField(), - max: new foundry.data.fields.NumberField() + max: new foundry.data.fields.NumberField({nullable : true, initial : 0}) }), resolve: new foundry.data.fields.SchemaField({ bonus: new foundry.data.fields.NumberField(), @@ -63,7 +64,7 @@ export class CombatModel extends foundry.abstract.DataModel { if (autoCalc.resilience) this.resilience.total = Math.max(attributes.toughness.total + 1 + this.resilience.bonus + this.resilience.armour, 1); if (autoCalc.determination) - this.determination.total = Math.max(attributes[this.determination.attribute || "toughness"].total + this.determination.bonus, 1); + this.determination.total = Math.max(attributes[this.determination.attribute].total + this.determination.bonus, 1); if (autoCalc.defence) { @@ -103,10 +104,15 @@ export class CombatModel extends foundry.abstract.DataModel { } - if (item.traitList.invulnerable) + if (item.system.invulnerable) { this.resilience.invulnerable = true } + + if (item.traitList.forceField) + { + this.resilience.forceField = true; + } } this.resilience.armour += highestRes } diff --git a/scripts/model/actor/components/crew.js b/scripts/model/actor/components/crew.js index 6187cf8..3c5ea65 100644 --- a/scripts/model/actor/components/crew.js +++ b/scripts/model/actor/components/crew.js @@ -1,10 +1,20 @@ -import ItemDialog from "../../../apps/item-dialog"; -import { DocumentListModel } from "../../shared/list"; -import { DocumentReferenceModel } from "../../shared/reference"; let fields = foundry.data.fields + +export class VehicleCrew extends DocumentReferenceModel +{ + static defineSchema() + { + let schema = super.defineSchema(); + schema.type = new fields.StringField({default: ""}); + return schema; + } +} + // List of objects that reference some embedded document on the parent -export class VehicleComplement extends DocumentListModel { +export class VehicleComplement extends DocumentReferenceListModel { + static listSchema = VehicleCrew + static defineSchema() { let schema = super.defineSchema(); @@ -15,9 +25,14 @@ export class VehicleComplement extends DocumentListModel { return schema; } + add(document, type) + { + return this._add({uuid : document.uuid, id : document.id, name : document.name, type}); + } + async choose(filter) { - let list = this.list.map(i => i.document).filter(i => i).filter(i => i.isOwner); + let list = this.documents.filter(i => i).filter(i => i.isOwner); if (filter) { list = list.filter(filter); @@ -41,14 +56,4 @@ export class VehicleComplement extends DocumentListModel { { return this.list.find(i => i.type == "pilot")?.document; } -} - -export class VehicleCrew extends DocumentReferenceModel -{ - static defineSchema() - { - let schema = super.defineSchema(); - schema.type = new fields.StringField({default: ""}); - return schema; - } } \ No newline at end of file diff --git a/scripts/model/actor/components/skills.js b/scripts/model/actor/components/skills.js index 589e662..09c7d46 100644 --- a/scripts/model/actor/components/skills.js +++ b/scripts/model/actor/components/skills.js @@ -9,7 +9,7 @@ export class SkillModel extends foundry.abstract.DataModel { return { label: new fields.StringField({ required: true, initial: "" }), attribute: new fields.StringField(), - rating: new fields.NumberField({ required: true, initial: 0 }), + rating: new fields.NumberField({ required: true, initial: 0, min: 0 }), base: new fields.NumberField({ required: true, initial: 0 }), bonus: new fields.NumberField({ required: true, initial: 0 }), cost: new fields.NumberField({ required: true, initial: 0 }), diff --git a/scripts/model/actor/components/standard.js b/scripts/model/actor/components/standard.js index 4a83e92..45ec2de 100644 --- a/scripts/model/actor/components/standard.js +++ b/scripts/model/actor/components/standard.js @@ -1,10 +1,10 @@ import { AttributesModel } from "./attributes"; -import { BaseActorModel } from "./base"; import { CombatModel } from "./combat"; import { SkillsModel } from "./skills"; -export class StandardWNGActorModel extends BaseActorModel { +export class StandardWNGActorModel extends BaseWarhammerActorModel { + static singletonItemPaths = {"species" : "species", "faction" : "faction", "archetype" : "archetype"}; async _preCreate(data, options, user) { @@ -24,8 +24,6 @@ export class StandardWNGActorModel extends BaseActorModel { "flags.wrath-and-glory.autoCalc.determination": true, "flags.wrath-and-glory.autoCalc.wounds": true, "flags.wrath-and-glory.autoCalc.conviction": true, - "flags.wrath-and-glory.autoWounded": true, - "flags.wrath-and-glory.autoExhausted": true, "flags.wrath-and-glory.generateMetaCurrencies": true }) } @@ -36,12 +34,48 @@ export class StandardWNGActorModel extends BaseActorModel { attributes: new foundry.data.fields.EmbeddedDataField(AttributesModel), skills : new foundry.data.fields.EmbeddedDataField(SkillsModel), combat : new foundry.data.fields.EmbeddedDataField(CombatModel), + + species : new foundry.data.fields.EmbeddedDataField(SingletonItemModel), + faction : new foundry.data.fields.EmbeddedDataField(SingletonItemModel), + archetype : new foundry.data.fields.EmbeddedDataField(SingletonItemModel) } } - computeBase() + async _preUpdate(data, options, user) { - + await super._preUpdate(data, options, user); + if (foundry.utils.hasProperty(options, "changed.system.combat.wounds.value")) + { + options.deltaWounds = data.system.combat.wounds.value - this.combat.wounds.value; + if (data.system.combat.wounds.value > this.combat.wounds.max) + { + data.system.combat.wounds.value = this.combat.wounds.max; + } + } + if (foundry.utils.hasProperty(options, "changed.system.combat.shock.value")) + { + options.deltaShock = data.system.combat.shock.value - this.combat.shock.value; + if (data.system.combat.shock.value > this.combat.shock.max) + { + data.system.combat.shock.value = this.combat.shock.max; + } + } + } + + async _onUpdate(data, options, user) + { + super._onUpdate(data, options, user) + if (user == game.user.id) + { + if (this.combat.wounds.value > 0) + { + this.parent.addCondition("wounded") + } + else if (this.parent.hasCondition("wounded")) + { + this.parent.removeCondition("wounded"); + } + } } computeDerived() { @@ -49,4 +83,12 @@ export class StandardWNGActorModel extends BaseActorModel { this.skills.compute(this.attributes); this.combat.compute(this.attributes, this.parent.getFlag("wrath-and-glory", "autoCalc") || {}); } + + + _addModelProperties() + { + this.species.relative = this.parent.items + this.faction.relative = this.parent.items + this.archetype.relative = this.parent.items + } } diff --git a/scripts/model/actor/threat.js b/scripts/model/actor/threat.js index 4e37166..a3ce6e1 100644 --- a/scripts/model/actor/threat.js +++ b/scripts/model/actor/threat.js @@ -3,8 +3,10 @@ import { StandardWNGActorModel } from "./components/standard"; let fields = foundry.data.fields; export class ThreatModel extends StandardWNGActorModel { + static defineSchema() { let schema = super.defineSchema(); + schema.bio = new fields.SchemaField({ species: new fields.StringField(), faction: new fields.StringField(), @@ -19,10 +21,71 @@ export class ThreatModel extends StandardWNGActorModel { }) schema.notes = new fields.StringField(), - schema.mob = new fields.NumberField(), + schema.mob = new fields.EmbeddedDataField(MobModel); + // schema.mob = new fields.SchemaField({ + // value : new fields.NumberField({min: 0}), + // abilities : new fields.EmbeddedDataField(DocumentReferenceListModel), + // }); schema.resources = new fields.SchemaField({ ruin : new fields.NumberField({min : 0}), }) return schema; } + + static migrateData(data) + { + super.migrateData(data); + if (typeof data.mob == "number") + { + data.mob = {value : data.mob, abilites : []} + } + } + + _addModelProperties() + { + this.mob.abilities.relative = this.parent.items; + } + } + +class MobModel extends foundry.abstract.DataModel +{ + static defineSchema() + { + let schema = {}; + schema.value = new fields.NumberField({min: 0}); + schema.abilities = new fields.EmbeddedDataField(MobAbilities); + return schema; + } + + isMobAbility(item) + { + return this.abilities.list.find(i => i.id == item.id); + } + + isActiveMobAbility(item) + { + return this.abilities.list.find(i => i.id == item.id)?.requiredMob <= this.value; + } +} + + +class MobAbility extends DocumentReferenceModel +{ + static defineSchema() + { + let schema = super.defineSchema(); + schema.requiredMob = new fields.NumberField({min: 0}); + return schema; + } +} + +class MobAbilities extends DocumentReferenceListModel +{ + static listSchema = MobAbility + static defineSchema() + { + let schema = super.defineSchema(); + return schema; + } +} \ No newline at end of file diff --git a/scripts/model/actor/vehicle.js b/scripts/model/actor/vehicle.js index eef5480..cd58fd1 100644 --- a/scripts/model/actor/vehicle.js +++ b/scripts/model/actor/vehicle.js @@ -1,11 +1,10 @@ import { TraitsModel } from "../item/components/traits"; -import { BaseActorModel } from "./components/base"; import { VehicleComplement } from "./components/crew"; import { VehicleCombatModel } from "./components/vehicle-combat"; const fields = foundry.data.fields; -export class VehicleModel extends BaseActorModel { +export class VehicleModel extends BaseWarhammerActorModel { static defineSchema() { let schema = super.defineSchema(); schema.complement = new fields.EmbeddedDataField(VehicleComplement); @@ -21,7 +20,6 @@ export class VehicleModel extends BaseActorModel { computeBase() { super.computeBase(); - this.complement.findDocuments(game.actors); } computeDerived() @@ -37,6 +35,7 @@ export class VehicleModel extends BaseActorModel { static migrateData(data) { + super.migrateData(data); if (data.traits instanceof Array) { data.traits = {list : data.traits}; diff --git a/scripts/model/effect/effect.js b/scripts/model/effect/effect.js new file mode 100644 index 0000000..c0faf87 --- /dev/null +++ b/scripts/model/effect/effect.js @@ -0,0 +1,22 @@ +let fields = foundry.data.fields; + +export class WrathAndGloryAvoidTestModel extends AvoidTestModel { + static defineSchema() { + let schema = super.defineSchema(); + schema.dn = new fields.StringField({}); + schema.type = new fields.StringField({}); + schema.specification = new fields.StringField({}) + + return schema; + } +} + +export class WrathAndGloryActiveEffectModel extends WarhammerActiveEffectModel { + static _avoidTestModel = WrathAndGloryAvoidTestModel; + + static defineSchema() + { + let schema = super.defineSchema(); + return schema + } +} \ No newline at end of file diff --git a/scripts/model/item/ability.js b/scripts/model/item/ability.js index e748d0d..3b2c5e9 100644 --- a/scripts/model/item/ability.js +++ b/scripts/model/item/ability.js @@ -29,4 +29,10 @@ export class AbilityModel extends StandardItemModel get AbilityType() { return game.wng.config.abilityTypes[this.abilityType] } + + getOtherEffects() + { + return super.getOtherEffects().concat(this.traits.effects); + } + } \ No newline at end of file diff --git a/scripts/model/item/ammo.js b/scripts/model/item/ammo.js index 44754d1..411e881 100644 --- a/scripts/model/item/ammo.js +++ b/scripts/model/item/ammo.js @@ -14,4 +14,9 @@ export class AmmoModel extends PhysicalItemModel return schema; } + // Ammo effects should never transfer to actors, they always append to the weapon's effects + shouldTransferEffect(effect) + { + return false; + } } \ No newline at end of file diff --git a/scripts/model/item/archetype.js b/scripts/model/item/archetype.js index 0c29e72..8c2e178 100644 --- a/scripts/model/item/archetype.js +++ b/scripts/model/item/archetype.js @@ -1,6 +1,4 @@ -import { DeferredDocumentReferenceModel, DiffDocumentReferenceModel } from "../shared/reference"; import { Attributes, BaseItemModel, Skills } from "./components/base"; -import { SingletonItemModel } from "./components/singleton"; let fields = foundry.data.fields; @@ -12,31 +10,97 @@ export class ArchetypeModel extends BaseItemModel let schema = super.defineSchema(); schema.tier = new fields.NumberField({min : 1, initial : 1}) schema.journal = new fields.StringField({}); - schema.species = new fields.EmbeddedDataField(SingletonItemModel); - schema.faction = new fields.EmbeddedDataField(SingletonItemModel); + schema.species = new fields.EmbeddedDataField(DeferredReferenceModel); + schema.faction = new fields.EmbeddedDataField(DeferredReferenceModel); schema.influence = new fields.NumberField({initial : 0}); schema.cost = new fields.NumberField({min : 0, initial : 0}); schema.keywords = new fields.ArrayField(new fields.StringField()); schema.attributes = Attributes(); schema.skills = Skills(); - schema.ability = new fields.EmbeddedDataField(DeferredDocumentReferenceModel) - schema.wargear = new fields.ArrayField(new fields.EmbeddedDataField(ArchetypeWargearModel)) - schema.groups = new fields.SchemaField({ - type : new fields.StringField({initial : "and"}), - groupId : new fields.StringField({initial : "root"}), - items : new fields.ArrayField(new fields.ObjectField()) - }) + schema.ability = new fields.EmbeddedDataField(DeferredReferenceModel) + schema.wargear = new fields.EmbeddedDataField(ChoiceModel) schema.suggested = new fields.SchemaField({ attributes : Attributes(), skills: Skills(), - talents : new fields.ArrayField(new fields.EmbeddedDataField(DiffDocumentReferenceModel)) + talents : new fields.EmbeddedDataField(DeferredReferenceListModel) }) return schema; } + + static migrateData(data) + { + super.migrateData(data); + if (data.suggested.talents instanceof Array) + { + data.suggested.talents = {list : data.suggested.talents}; + } + + + let _convertStructure = (structure, wargear) => { + structure.type = ["or", "and"].includes(structure.type) ? structure.type : "option"; + if (!isNaN(structure.index)) + { + structure.id = wargear[structure.index].groupId || wargear[structure.index].id; + } + else + { + structure.id = structure.groupId; + } + if (structure.items?.length) + { + structure.options = foundry.utils.deepClone(structure.items); + delete structure.items; + for(let opt of structure.options) + { + _convertStructure(opt, wargear); + } + } + + return structure + } + + if (data.wargear instanceof Array) + { + let oldWargear = foundry.utils.deepClone(data.wargear); + + data.wargear = { + structure : _convertStructure(data.groups, oldWargear), + + options: oldWargear.map(w => { + let type = w.type; + if (type == "generic") + { + type = w.filters?.length ? "filter" : "placeholder"; + } + return { + name : w.name, + type : type, + id : w.groupId || w.id, + diff : w.diff, + documentId : w.id, + idType : "id", + filters: (w.filters || []).map(i => { + let opMap = { + lt : "<", + le : "<=", + eq : "==", + gt : ">", + ge : ">=" + } + return { + path : i.property, + value : i.value, + operation : opMap[i.test] + } + }) + } + })} + } + } } -class ArchetypeWargearModel extends DeferredDocumentReferenceModel +class ArchetypeWargearModel extends DeferredReferenceModel { static defineSchema() { diff --git a/scripts/model/item/armour.js b/scripts/model/item/armour.js index 0757e74..138ad92 100644 --- a/scripts/model/item/armour.js +++ b/scripts/model/item/armour.js @@ -20,4 +20,9 @@ export class ArmourModel extends EquippedItemModel return schema; } + getOtherEffects() + { + return super.getOtherEffects().concat(this.traits.effects); + } + } \ No newline at end of file diff --git a/scripts/model/item/components/base.js b/scripts/model/item/components/base.js index 50c32af..3d00ab8 100644 --- a/scripts/model/item/components/base.js +++ b/scripts/model/item/components/base.js @@ -4,94 +4,34 @@ let fields = foundry.data.fields; -export class BaseItemModel extends foundry.abstract.DataModel +export class BaseItemModel extends BaseWarhammerItemModel { - get id () - { - return this.parent.id; - } - static defineSchema() { return {}; } - async _preCreate(data, options, user) - { - } - - async _preUpdate(data, options, user) - { - } - - async _preDelete(options, user) - { - - } - - async _onUpdate(data, options, user) - { - - } - - async _onCreate(data, options, user) - { - - } - - async _onDelete(options, user) + get isMobAbility() { - - } - - computeBase() - { - - } - - computeDerived() - { - - } - - computeOwned() - { - - } - - async allowCreation(data, options, user) - { - if (this.parent.actor) - { - return this.parent.actor.system.itemIsAllowed(this.parent); - } - else + if (this.parent.actor?.system.mob) { - return true; + return this.parent.actor.system.mob.isMobAbility(this.parent); } } - /** - * Get effects from other sources, like weapon modifications - * - */ - getOtherEffects() + get isActiveMobAbility() { - return []; + return !this.isMobAbility || this.parent.actor.system.mob.isActiveMobAbility(this.parent); } - /** - * - */ - effectIsApplicable(effect) + get requiredMob() { - return !effect.disabled; + return this.isMobAbility && this.parent.actor.system.mob.abilities.list.find(i => i.id == this.parent.id)?.requiredMob; } - // If an item effect is disabled it should still transfer to the actor, so that it's visibly disabled - shouldTransferEffect() + shouldTransferEffect(effect) { - return true; + return super.shouldTransferEffect(effect) && this.isActiveMobAbility; } static migrateData(data) @@ -112,29 +52,78 @@ export class BaseItemModel extends foundry.abstract.DataModel if (data.damage) { - if (!data.damage.ap && data.ap) - { + if (!data.damage.ap && data.ap) { data.damage.ap = data.ap; delete data.ap; } - if (!data.damage.ed && data.ed) - { + if (!data.damage.ed && data.ed) { data.damage.ed = data.ed; delete data.ed; } - if (!data.damage.other && data.other) - { + if (!data.damage.other && data.other) { data.damage.other = data.other; delete data.other; } - if (!data.damage.otherDamage && data.otherDamage) - { + if (!data.damage.otherDamage && data.otherDamage) { data.damage.otherDamage = data.otherDamage; delete data.otherDamage; } + + if (typeof data.damage.ed.dice == "number" && data.damageap.dice != 0) + { + data.damage.ed.dice = "1d6" + } + + if (typeof data.damage.ap.dice == "number" && data.damageap.dice != 0) + { + data.damage.ed.dice = "1d6" + } + + + + if (data.damage.otherDamage.mortalWounds) + { + data.otherDamage.mortal = data.otherDamage.mortalWounds; + delete data.otherDamage.mortalWounds; + } + if (data.damage) + { + data.damage.rank = this._convertRank(data.damage.rank); + } + if (data.damage.ed) + { + data.damage.ed.rank = this._convertRank(data.damage.ed.rank); + } + if (data.damage.ap) + { + data.damage.ap.rank = this._convertRank(data.damage.ap.rank); + } + } + + + } + + static _convertRank(str) + { + if (typeof str == "string") + { + + return { + "none": 0, + "single": 1, + "double": 2 + }[str]; + } + else if (isNaN(str)) + { + return 0; + } + else + { + return str; } } diff --git a/scripts/model/item/components/damage.js b/scripts/model/item/components/damage.js index f5fa350..f7e10c6 100644 --- a/scripts/model/item/components/damage.js +++ b/scripts/model/item/components/damage.js @@ -6,29 +6,30 @@ export class DamageModel extends foundry.abstract.DataModel static defineSchema() { let schema = {}; + schema.enabled = new fields.BooleanField({initial: false}); schema.base = new fields.NumberField({initial: 0, nullable: false}); schema.bonus = new fields.NumberField({initial: 0, nullable: false}); schema.dice = new fields.NumberField({min: 0, initial: 0, nullable: false}); - schema.rank = new fields.StringField({initial : "none"}); + schema.rank = new fields.NumberField({initial : 0, choices : {0 : "RANK.NONE", 1 : "RANK.SINGLE", 2: "RANK.DOUBLE"}}); schema.ed = new fields.SchemaField({ base: new fields.NumberField({initial: 0, nullable: false}), bonus: new fields.NumberField({initial: 0, nullable: false}), - dice : new fields.NumberField({min: 0, initial: 0, nullable: false}), - rank: new fields.StringField({initial : "none"}) + dice : new fields.StringField(), + rank: new fields.NumberField({initial : 0, choices : {0 : "RANK.NONE", 1 : "RANK.SINGLE", 2: "RANK.DOUBLE"}}) }) schema.ap = new fields.SchemaField({ base: new fields.NumberField({initial: 0, nullable: false}), bonus: new fields.NumberField({initial: 0, nullable: false}), - dice : new fields.NumberField({min: 0, initial: 0, nullable: false}), - rank: new fields.StringField({initial : "none"}) + dice : new fields.StringField(), + rank: new fields.NumberField({initial : 0, choices : {0 : "RANK.NONE", 1 : "RANK.SINGLE", 2: "RANK.DOUBLE"}}) }) schema.otherDamage = new fields.SchemaField({ - mortalWounds : new fields.StringField({}), - wounds : new fields.StringField({}), - shock : new fields.StringField({}) + mortal : new fields.StringField({initial : "0"}), + wounds : new fields.StringField({initial : "0"}), + shock : new fields.StringField({initial : "0"}) }) return schema; } @@ -46,7 +47,6 @@ export class DamageModel extends foundry.abstract.DataModel return this._dataWithRank("ap"); } - _dataWithRank(type) { let data = type != "damage" ? this[type] : this; let damage = data.base + data.bonus; @@ -55,9 +55,9 @@ export class DamageModel extends foundry.abstract.DataModel damage = damage ? damage + ` + ${data.dice}` : data.dice } let rank = ""; - if (data.rank === "single") { + if (data.rank === 1) { rank = " + R"; - } else if (data.rank === "double") { + } else if (data.rank === 2) { rank = " + DR"; } return `${damage}${rank}`; diff --git a/scripts/model/item/components/equipped.js b/scripts/model/item/components/equipped.js index 6e1957f..048a8e7 100644 --- a/scripts/model/item/components/equipped.js +++ b/scripts/model/item/components/equipped.js @@ -10,5 +10,19 @@ export class EquippedItemModel extends PhysicalItemModel schema.equipped = new fields.BooleanField({}); return schema; } + + get isEquipped() + { + return this.equipped; + } + get equippable() + { + return true; + } + + shouldTransferEffect(effect) + { + return super.shouldTransferEffect(effect) && (!effect.system.transferData.equipTransfer || this.isEquipped) + } } \ No newline at end of file diff --git a/scripts/model/item/components/physical.js b/scripts/model/item/components/physical.js index 442a3f5..fa23e7e 100644 --- a/scripts/model/item/components/physical.js +++ b/scripts/model/item/components/physical.js @@ -11,7 +11,7 @@ export class PhysicalItemModel extends StandardItemModel static defineSchema() { let schema = super.defineSchema(); - schema.quantity = new fields.NumberField({min : 0}); + schema.quantity = new fields.NumberField({initial : 0, min : 0}); schema.value = new fields.StringField(); schema.keywords = new fields.StringField(); schema.rarity = new fields.StringField({initial : "common"}); diff --git a/scripts/model/item/components/singleton.js b/scripts/model/item/components/singleton.js deleted file mode 100644 index d634e6d..0000000 --- a/scripts/model/item/components/singleton.js +++ /dev/null @@ -1,15 +0,0 @@ -import { BaseItemModel } from "./base"; -let fields = foundry.data.fields; - -export class SingletonItemModel extends foundry.abstract.DataModel -{ - - static defineSchema() - { - let schema = {}; - schema.name = new fields.StringField(); - schema.id = new fields.StringField(); - return schema; - } - -} \ No newline at end of file diff --git a/scripts/model/item/components/test.js b/scripts/model/item/components/test.js index 533e150..4117e5e 100644 --- a/scripts/model/item/components/test.js +++ b/scripts/model/item/components/test.js @@ -6,7 +6,8 @@ export class TestDataModel extends foundry.abstract.DataModel static defineSchema() { let schema = {}; - schema.dn = new fields.StringField({}), + schema.self = new fields.BooleanField(), + schema.dn = new fields.NumberField({nullable : true}), schema.type = new fields.StringField({}), schema.specification = new fields.StringField({}) return schema; diff --git a/scripts/model/item/components/traits.js b/scripts/model/item/components/traits.js index 7ca7341..a19972d 100644 --- a/scripts/model/item/components/traits.js +++ b/scripts/model/item/components/traits.js @@ -40,7 +40,7 @@ export class TraitsModel extends foundry.abstract.DataModel add(traits) { - let add = traits.filter(i => i.type == "add") + let add = traits.filter(i => i.type == "add" || !i.type) let remove = traits.filter(i => i.type == "remove") add.forEach(trait => { @@ -89,10 +89,18 @@ export class TraitsModel extends foundry.abstract.DataModel } else { + let effectData = systemConfig().traitEffects[i.name]; + if (effectData) + { + foundry.utils.setProperty(effectData, `flags.${game.system.id}.path`, `system.traitList.${i.name}.effect`); + effectData.name = game.i18n.localize(effectData.name); + effectData.img = this.parent.parent?.img; + } traits[i.name] = { name: i.name, display: this.parent.traitsAvailable[i.name], - type: i.type + type: i.type, + effect : effectData ? new ActiveEffect.implementation(effectData, {parent: this.parent.parent}) : null } if (game.wng.config.traitHasRating[i.name]) { traits[i.name].rating = i.rating; @@ -102,4 +110,9 @@ export class TraitsModel extends foundry.abstract.DataModel }) return traits } + + get effects() + { + return Object.values(this.obj).map(i => i.effect).filter(i => i); + } } \ No newline at end of file diff --git a/scripts/model/item/faction.js b/scripts/model/item/faction.js index 106a592..653125c 100644 --- a/scripts/model/item/faction.js +++ b/scripts/model/item/faction.js @@ -18,6 +18,52 @@ export class FactionModel extends StandardItemModel return schema; } + shouldTransferEffect(effect) + { + for (let bg of this.backgrounds.origin.concat(this.backgrounds.accomplishment).concat(this.backgrounds.goal)) + { + if (bg.chosen && bg.effect.id == effect.id) + { + return true; + } + } + return false + } + + _addModelProperties() + { + for (let bg of this.backgrounds.origin.concat(this.backgrounds.accomplishment).concat(this.backgrounds.goal)) + { + bg.effect.relative = this.parent.effects; + } + } + + static migrateData(data) + { + super.migrateData(data); + for(let bg of data.backgrounds.origin) + { + if (typeof bg.effect == "string") + { + bg.effect = {id : bg.effect} + } + } + for(let bg of data.backgrounds.accomplishment) + { + if (typeof bg.effect == "string") + { + bg.effect = {id : bg.effect} + } + } + for(let bg of data.backgrounds.goal) + { + if (typeof bg.effect == "string") + { + bg.effect = {id : bg.effect} + } + } + } + } function backgroundData() @@ -25,7 +71,8 @@ function backgroundData() return new fields.ArrayField(new fields.SchemaField({ name : new fields.StringField(), description : new fields.StringField(), - effect : new fields.StringField(), - active : new fields.BooleanField() + effect : new fields.EmbeddedDataField(DocumentReferenceModel), + active : new fields.BooleanField(), + chosen : new fields.BooleanField() })); } diff --git a/scripts/model/item/psychicPower.js b/scripts/model/item/psychicPower.js index 7aefa18..daeda54 100644 --- a/scripts/model/item/psychicPower.js +++ b/scripts/model/item/psychicPower.js @@ -1,6 +1,7 @@ import { DamageModel } from "./components/damage"; import { StandardItemModel } from "./components/standard"; import { TestDataModel } from "./components/test"; +import { TraitsModel } from "./components/traits"; let fields = foundry.data.fields; @@ -20,11 +21,23 @@ export class PsychicPowerModel extends StandardItemModel schema.range = new fields.StringField({}), schema.multiTarget = new fields.BooleanField({}), schema.keywords = new fields.StringField({}), + schema.traits = new fields.EmbeddedDataField(TraitsModel); schema.prerequisites = new fields.StringField({}), - schema.potency = new fields.ArrayField(new fields.ObjectField({})) + schema.potency = ListModel.createListModel(new fields.SchemaField({ + cost : new fields.NumberField(), + description : new fields.StringField(), + initial : new fields.StringField(), + property : new fields.StringField(), + single : new fields.BooleanField(), + value : new fields.StringField() + })) return schema; } + + get traitsAvailable() { + return game.wng.config.weaponTraits + } get DN() { if (!this.dn) @@ -43,4 +56,13 @@ export class PsychicPowerModel extends StandardItemModel return game.wng.config.powerActivations[this.activation] } + static migrateData(data) + { + super.migrateData(data); + if (data.potency instanceof Array) + { + data.potency = {list : data.potency}; + } + } + } \ No newline at end of file diff --git a/scripts/model/item/species.js b/scripts/model/item/species.js index f5b05e2..8dfa67d 100644 --- a/scripts/model/item/species.js +++ b/scripts/model/item/species.js @@ -13,11 +13,19 @@ export class SpeciesModel extends StandardItemModel schema.speed = new fields.NumberField({min : 1, initial : 6}) schema.size = new fields.StringField({}); schema.journal = new fields.StringField({}); - schema.abilities = new fields.ArrayField(new fields.ObjectField({})) + schema.abilities = new fields.EmbeddedDataField(DeferredReferenceListModel) schema.attributes = Attributes(); schema.skills = Skills(); schema.attributeMax = Attributes(); return schema; } + static migrateData(data) + { + super.migrateData(data); + if (data.abilities instanceof Array) + { + data.abilities = {list: data.abilities}; + } + } } \ No newline at end of file diff --git a/scripts/model/item/talent.js b/scripts/model/item/talent.js index 885ba01..a8bf57a 100644 --- a/scripts/model/item/talent.js +++ b/scripts/model/item/talent.js @@ -1,4 +1,6 @@ +import { DamageModel } from "./components/damage"; import { StandardItemModel } from "./components/standard"; +import { TestDataModel } from "./components/test"; let fields = foundry.data.fields; @@ -9,6 +11,12 @@ export class TalentModel extends StandardItemModel { { let schema = super.defineSchema(); + schema.test = new fields.EmbeddedDataField(TestDataModel), + schema.damage = new fields.EmbeddedDataField(DamageModel), + schema.uses = new fields.SchemaField({ + current : new fields.NumberField({nullable: true}), + max : new fields.StringField() + }) schema.effect = new fields.StringField({}), schema.cost = new fields.NumberField({min : 0}), schema.requirements = new fields.StringField({}), diff --git a/scripts/model/item/weapon.js b/scripts/model/item/weapon.js index 1d9035d..159bbf3 100644 --- a/scripts/model/item/weapon.js +++ b/scripts/model/item/weapon.js @@ -30,13 +30,11 @@ export class WeaponModel extends EquippedItemModel thrown : new fields.NumberField({nullable : true}), }) schema.category = new fields.StringField({initial : "melee"}); - schema.ammo = new fields.StringField({}) + schema.ammo = new fields.EmbeddedDataField(DocumentReferenceModel); schema.salvo = new fields.NumberField({}) schema.traits = new fields.EmbeddedDataField(TraitsModel); schema.upgrades = new fields.ArrayField(new fields.ObjectField()); - schema.combi = new fields.SchemaField({ - id : new fields.StringField(), - }) + schema.combi = new fields.EmbeddedDataField(DocumentReferenceModel); schema.twinned = new fields.BooleanField(); return schema; } @@ -90,6 +88,12 @@ export class WeaponModel extends EquippedItemModel return this.multiTarget ? game.i18n.localize("Yes") : game.i18n.localize("No") } + computeBase() + { + super.computeBase(); + this.damage.enabled = true; + } + computeDerived() { this.applyUpgrades(); @@ -103,15 +107,6 @@ export class WeaponModel extends EquippedItemModel if (this.isRanged && this.Ammo) { this.applyAmmo() } - - if (this.combi.id) - { - let combi = this.parent.actor?.items.get(this.combi.id); - if (combi) - { - this.combi.document = combi; - } - } } applyUpgrades() { @@ -135,6 +130,28 @@ export class WeaponModel extends EquippedItemModel this.traits.add(this.Ammo.system.traits) } + getOtherEffects() + { + let other = super.getOtherEffects().concat(this.traits.effects); + if (this.Ammo) + { + other = other.concat(this.Ammo.effects.contents); + } + for(let upg of this.upgrades) + { + other = other.concat(upg.effects.map(e => new ActiveEffect.implementation(e, {parent : this.parent}))); + } + return other; + } + + _addModelProperties() + { + if (this.parent.actor) + { + this.ammo.relative = this.parent.actor.items + this.combi.relative = this.parent.actor.items + } + } _applyEffects(effects) { let overrides = {} @@ -180,8 +197,17 @@ export class WeaponModel extends EquippedItemModel get Ammo() { - if (this.parent.isOwned) - return this.parent.actor.items.get(this.ammo) + return this.ammo.document + } + + + static migrateData(data) + { + super.migrateData(data); + if (typeof data.ammo == "string") + { + data.ammo = {id : data.ammo}; + } } } \ No newline at end of file diff --git a/scripts/model/message/message.js b/scripts/model/message/message.js new file mode 100644 index 0000000..947cf79 --- /dev/null +++ b/scripts/model/message/message.js @@ -0,0 +1,39 @@ +import { DamageRoll } from "../../common/tests/damage"; + +export class WrathAndGloryTestMessageModel extends WarhammerTestMessageModel +{ + static defineSchema() + { + let fields = foundry.data.fields; + let schema = {}; + schema.context = new fields.ObjectField(); + schema.testData = new fields.ObjectField(); + schema.result = new fields.ObjectField(); + schema.class = new fields.StringField(); + return schema; + } + + get test() + { + return game.wng.rollClasses[this.class].recreate(this); + } +} + +export class WrathAndGloryDamageMessageModel extends foundry.abstract.DataModel +{ + static defineSchema() + { + let fields = foundry.data.fields; + let schema = {}; + schema.context = new fields.ObjectField(); + schema.damageData = new fields.ObjectField(); + schema.rerollData = new fields.ObjectField(); + schema.result = new fields.ObjectField(); + return schema; + } + + get damage() + { + return new DamageRoll(this) + } +} \ No newline at end of file diff --git a/scripts/model/shared/list.js b/scripts/model/shared/list.js deleted file mode 100644 index 6687842..0000000 --- a/scripts/model/shared/list.js +++ /dev/null @@ -1,89 +0,0 @@ -import { DocumentReferenceModel } from "./reference"; - -let fields = foundry.data.fields; - - -// Generic list of objects -export class ListModel extends foundry.abstract.DataModel -{ - defaultValue = undefined; - - static defineSchema() - { - let schema = {}; - schema.list = new fields.ArrayField(new fields.ObjectField()); - return schema; - } - - add(value) - { - return this.list.concat(value || this.defaultValue); - } - - edit(index, value) - { - let list = duplicate(this.list); - if (typeof value == "object") - { - mergeObject(list[index], value, {overwrite : true}); - } - else if (typeof value == "string") - { - list[index] = value; - } - return list; - } - - remove(index) - { - index = Number(index); - return this.list.slice(0, index).concat(this.list.slice(index+1)); - } -} - -// List of objects that reference some embedded document on the parent -export class DocumentListModel extends ListModel -{ - static defineSchema() - { - let schema = super.defineSchema(); - schema.list = new fields.ArrayField(new fields.EmbeddedDataField(DocumentReferenceModel)); - return schema; - } - - addDocument(document) - { - if (document.prototypeToken?.actorLink && this.has(id)) - { - return this.list - } - else - { - return this.list.concat([{id : document.id}]) - } - } - - editId(id, value) - { - let index = this.list.findIndex(i => i.id == id); - return this.edit(index, value); - } - - has(id) - { - return !!this.list.find(i => i.id == id) - } - - removeId(id) - { - let index = this.list.findIndex(i => i.id == id); - - if (index != -1) {return this.remove(index);} - else {return this.list;} - } - - findDocuments(collection) - { - this.list.forEach(i => i.document = collection.get(i.id)); - } -} diff --git a/scripts/model/shared/reference.js b/scripts/model/shared/reference.js deleted file mode 100644 index 892e539..0000000 --- a/scripts/model/shared/reference.js +++ /dev/null @@ -1,44 +0,0 @@ -let fields = foundry.data.fields; - -export class DocumentReferenceModel extends foundry.abstract.DataModel -{ - static defineSchema() - { - let schema = {}; - schema.id = new fields.StringField(); - schema.id = new fields.StringField(); - return schema; - } - - getDocument(collection) - { - if (collection instanceof Collection) - { - this.document = collection.get(this.id); - } - else if (collection instanceof Array) - { - this.document = collection.find(i => i.id == this.id); - } - } -} - -export class DeferredDocumentReferenceModel extends DocumentReferenceModel -{ - static defineSchema() - { - let schema = super.defineSchema(); - schema.name = new fields.StringField(); - return schema; - } -} - -export class DiffDocumentReferenceModel extends DeferredDocumentReferenceModel -{ - static defineSchema() - { - let schema = super.defineSchema(); - schema.diff = new fields.ObjectField(); - return schema; - } -} \ No newline at end of file diff --git a/scripts/sheet/actor/base.js b/scripts/sheet/actor/base.js index d716690..28c38b3 100644 --- a/scripts/sheet/actor/base.js +++ b/scripts/sheet/actor/base.js @@ -1,6 +1,6 @@ import ActorConfigure from "../../apps/actor-configure.js"; -export class BaseWnGActorSheet extends ActorSheet { +export class BaseWnGActorSheet extends WarhammerActorSheet { rollData = {}; static get defaultOptions() { @@ -74,16 +74,13 @@ export class BaseWnGActorSheet extends ActorSheet { html.find(".item-dropdown").mousedown(this._dropdownLeftClick.bind(this)) html.find(".rollable").mouseenter(this._toggleDice.bind(this)) html.find(".rollable").mouseleave(this._toggleDice.bind(this)) - html.find(".rollable").mousedown(this._onRollableAbilityClick.bind(this)) + html.find(".rollable").click(this._onRollableAbilityClick.bind(this)) html.find(".item-dropdown-right").mousedown(this._dropdownRightClick.bind(this)) html.find(".item-create").mousedown(this._onItemCreate.bind(this)); html.find(".item-edit").click(this._onItemEdit.bind(this)); + html.find(".effects .source").click(this._onEditEmbeddedDoc.bind(this)); html.find(".item-delete").mousedown(this._onItemDelete.bind(this)); html.find(".item-post").mousedown(this._onItemPost.bind(this)); - html.find(".effect-create").click(this._onEffectCreate.bind(this)); - html.find(".effect-edit").click(this._onEffectEdit.bind(this)); - html.find(".effect-delete").click(this._onEffectDelete.bind(this)); - html.find(".effect-toggle").click(this._onEffectToggle.bind(this)); html.find("input").focusin(this._onFocusIn.bind(this)); html.find(".checkbox").click(this._onCheckboxClick.bind(this)) html.find(".property-edit").change(this._onSelectChange.bind(this)) @@ -127,7 +124,7 @@ export class BaseWnGActorSheet extends ActorSheet { _onItemEdit(event) { event.stopPropagation(); const div = $(event.currentTarget).parents(".item"); - const item = this.actor.items.get(div.data("itemId")); + const item = this.actor.items.get(div.data("itemId") || event.target.dataset.itemId); item.sheet.render(true); } @@ -162,60 +159,6 @@ export class BaseWnGActorSheet extends ActorSheet { item.sendToChat() } - - async _onEffectCreate(ev) { - let type = ev.currentTarget.attributes["data-type"].value - let effectData = { name: "New Effect", icon: "icons/svg/aura.svg" } - if (type == "temporary") { - effectData["duration.rounds"] = 1; - } - - let html = await renderTemplate("systems/wrath-and-glory/template/apps/quick-effect.hbs") - let dialog = new Dialog({ - title: "Quick Effect", - content: html, - buttons: { - "create": { - label: "Create", - callback: html => { - let mode = 2 - let label = html.find(".label").val() - let key = html.find(".key").val() - let value = parseInt(html.find(".modifier").val()) - effectData.name = label - effectData.changes = [{ key, mode, value }] - this.actor.createEmbeddedDocuments("ActiveEffect", [effectData]) - } - }, - "skip": { - label: "Skip", - callback: () => this.actor.createEmbeddedDocuments("ActiveEffect", [effectData]).then(effect => effect[0].sheet.render(true)) - } - } - }) - await dialog._render(true) - dialog._element.find(".label").select() - - - } - - _onEffectEdit(ev) { - let id = $(ev.currentTarget).parents(".item").attr("data-effect-id") - this.object.effects.get(id).sheet.render(true) - } - - _onEffectDelete(ev) { - let id = $(ev.currentTarget).parents(".item").attr("data-effect-id") - this.object.deleteEmbeddedDocuments("ActiveEffect", [id]) - } - - _onEffectToggle(ev) { - let id = $(ev.currentTarget).parents(".item").attr("data-effect-id") - let effect = this.object.effects.get(id) - - effect.update({ "disabled": !effect.disabled }) - } - _onFocusIn(event) { $(event.currentTarget).select(); } diff --git a/scripts/sheet/actor/standard.js b/scripts/sheet/actor/standard.js index b7774dc..5fc9801 100644 --- a/scripts/sheet/actor/standard.js +++ b/scripts/sheet/actor/standard.js @@ -44,13 +44,27 @@ export class StandardActorSheet extends BaseWnGActorSheet { items.traumaticInjuries = this.actor.itemTypes.traumaticInjury items.weaponUpgrades = this.actor.itemTypes.weaponUpgrade - items.equipped.weapons = this.actor.itemTypes.weapon.filter(i => i.equipped) - items.equipped.armour = this.actor.itemTypes.armour.filter(i => i.equipped) + items.equipped.weapons = this.actor.itemTypes.weapon.filter(i => i.equipped).filter(i => i.system.isActiveMobAbility) + items.equipped.armour = this.actor.itemTypes.armour.filter(i => i.equipped).filter(i => i.system.isActiveMobAbility) items.equipped.ammo = items.equipped.weapons.map(i => this.actor.items.get(i.ammo)).filter(i => !!i).filter((item, index, self) => self.findIndex(dup => dup.id == item.id) == index) //remove duplicate sheetData.items = items; this.constructInventory(sheetData) + + for(let type in sheetData.items) + { + if (type != "equipped") + { + sheetData.items[type] = sheetData.items[type].filter(i => i.system.isActiveMobAbility); + } + } + + for(let type in sheetData.inventory) + { + sheetData.inventory[type].items = sheetData.inventory[type].items.filter(i => i.system.isActiveMobAbility); + } + } constructInventory(sheetData) { @@ -101,19 +115,36 @@ export class StandardActorSheet extends BaseWnGActorSheet { } constructEffectLists(sheetData) { - let effects = {} + let effects = { + temporary : [], + disabled : [], + passive : [] + } + + for(let e of Array.from(sheetData.actor.allApplicableEffects())) + { + if (e.isTemporary && !e.disabled) + { + effects.temporary.push(e); + } + else if (e.disabled) + { + effects.disabled.push(e); + } + else + { + effects.passive.push(e); + } + } effects.conditions = CONFIG.statusEffects.map(i => { return { - label: i.label, + name: i.name, key: i.id, - img: i.icon, + img: i.img, existing: this.actor.hasCondition(i.id) } }) - effects.temporary = sheetData.actor.effects.filter(i => i.isTemporary && !i.disabled && !i.isCondition) - effects.disabled = sheetData.actor.effects.filter(i => i.disabled && !i.isCondition) - effects.passive = sheetData.actor.effects.filter(i => !i.isTemporary && !i.disabled && !i.isCondition) sheetData.effects = effects; } @@ -164,117 +195,59 @@ export class StandardActorSheet extends BaseWnGActorSheet { }) } - async _onRollableAbilityClick(ev) { const div = $(event.currentTarget).parents(".item"); const item = this.actor.items.get(div.data("itemId")); if (ev.button == 0) { - let test if (item.abilityType == "determination") - test = await this.actor.setupGenericTest("determination") + await this.actor.setupGenericTest("determination") else - test = await this.actor.setupAbilityRoll(item) + await this.actor.setupAbilityRoll(item) - await test.rollTest(); } else this._dropdownRightClick(ev) } - async _prepareCustomRoll() { - this._resetRollData(); - return prepareCommonRoll(this.rollData); - } - - async _prepareReroll() { - return reroll(this.rollData); - } - - async _prepareDamageRoll() { - this._resetRollData(); - this.rollData.weapon = { - damage: { - base: 0, - rank: "none", - bonus: 0 - }, - ed: { - base: 0, - rank: "none", - bonus: 0, - die: { - one: 0, - two: 0, - three: 0, - four: 1, - five: 1, - six: 2 - } - }, - ap: { - base: 0, - rank: "none", - bonus: 0 - }, - traits: "" - } - this.rollData.name = "ROLL.DAMAGE"; - return prepareDamageRoll(this.rollData); - } - async _onAttributeClick(event) { event.preventDefault(); const attribute = $(event.currentTarget).data("attribute"); - let test = await this.actor.setupAttributeTest(attribute) - await test.rollTest(); - test.sendToChat() + this.actor.setupAttributeTest(attribute) } async _onSkillClick(event) { event.preventDefault(); const skill = $(event.currentTarget).data("skill"); - let test = await this.actor.setupSkillTest(skill) - await test.rollTest(); - test.sendToChat() + this.actor.setupSkillTest(skill) } async _onDeterminationClick(event) { event.preventDefault(); - let test = await this.actor.setupGenericTest("determination") - await test.rollTest(); - test.sendToChat() + this.actor.setupGenericTest("determination") } async _onStealthClick(event) { event.preventDefault(); - let test = await this.actor.setupGenericTest("stealth") - await test.rollTest(); - test.sendToChat() + this.actor.setupGenericTest("stealth") } async _onConvictionClick(event) { event.preventDefault(); - this._resetRollData(); - new Dialog({ title: "Conviction Roll", buttons: { "corruption": { label: "Corruption", callback: async () => { - let test = await this.actor.setupGenericTest("corruption") - await test.rollTest(); - test.sendToChat() + this.actor.setupGenericTest("corruption") } }, "mutation": { label: "Mutation", callback: async () => { - let test = await this.actor.setupGenericTest("mutation") - await test.rollTest(); - test.sendToChat() + this.actor.setupGenericTest("mutation") } } } @@ -289,18 +262,14 @@ export class StandardActorSheet extends BaseWnGActorSheet { "corruption": { label: "Fear", callback: async () => { - let test = await this.actor.setupGenericTest("fear") - await test.rollTest(); - test.sendToChat() + this.actor.setupGenericTest("fear") } }, "mutation": { label: "Terror", callback: async () => { - let test = await this.actor.setupGenericTest("terror") - await test.rollTest(); - test.sendToChat() + this.actor.setupGenericTest("terror") } } } @@ -309,110 +278,18 @@ export class StandardActorSheet extends BaseWnGActorSheet { async _onInfluenceClick(event) { event.preventDefault(); - let test = await this.actor.setupGenericTest("influence") - await test.rollTest(); - test.sendToChat() + this.actor.setupGenericTest("influence") } async _onWeaponClick(event) { event.preventDefault(); const div = $(event.currentTarget).parents(".item"); - let tests = await this.actor.setupWeaponTest(div.data("itemId")) - ui.sidebar.activateTab("chat") - await Promise.all(tests.map(t => t.rollTest())); - tests.forEach(t => { - t.sendToChat() - }) + this.actor.setupWeaponTest(div.data("itemId")) } async _onPowerClick(event) { event.preventDefault(); const div = $(event.currentTarget).parents(".item"); - let test = await this.actor.setupPowerTest(div.data("itemId")) - await test.rollTest(); - test.sendToChat() - } - - async _prepareRollPsychicPower(event) { - event.preventDefault(); - this._resetRollData(); - const div = $(event.currentTarget).parents(".item"); - const psychicPower = this.actor.items.get(div.data("itemId")); - const skill = this.actor.system.skills.psychicMastery; - this.rollData.difficulty.target = psychicPower.dn; - this.rollData.name = psychicPower.name; - this.rollData.weapon = { - damage: { - base: psychicPower.damage.base, - rank: psychicPower.damage.rank, - bonus: psychicPower.damage.bonus - }, - ed: { - base: psychicPower.ed.base, - rank: psychicPower.ed.rank, - bonus: psychicPower.ed.bonus, - die: psychicPower.ed.die - }, - potency: psychicPower.potency - }; - this.rollData.wrath.isPsy = true; - this.rollData.wrath.isCommon = false; - this.rollData.pool.size = skill.total; - this.rollData.skillName = skill.label; - this.rollData.name = psychicPower.name; - return preparePsychicRoll(this.rollData); - } - - _resetRollData() { - let rank = 0; - if (this.actor.advances) { - rank = this.actor.advances.rank; - } - this.rollData = { - name: "DIALOG.CUSTOM_ROLL", - rank: rank, - difficulty: { - target: 3, - penalty: 0, - rank: "none" - }, - pool: { - size: 1, - bonus: 0, - rank: "none" - }, - wrath: { - base: 1, - isPsy: false, - isCommon: true, - isWeapon: false - }, - result: { - dice: [], - wrath: 0, - isSuccess: false, - isWrathCriticals: false, - isWrathComplications: false - }, - rolls: { - hit: [], - damage: [] - } - }; - } - - _getConvictionPenalty() { - let corruption = this.actor.corruption.current; - if (corruption > 20) { - return 4; - } else if (corruption > 15) { - return 3; - } else if (corruption > 10) { - return 2; - } else if (corruption > 5) { - return 1; - } else { - return 0; - } + await this.actor.setupPowerTest(div.data("itemId")) } } diff --git a/scripts/sheet/actor/threat.js b/scripts/sheet/actor/threat.js index 3b5747e..fc70d20 100644 --- a/scripts/sheet/actor/threat.js +++ b/scripts/sheet/actor/threat.js @@ -1,3 +1,4 @@ +import { MobConfig } from "../../apps/mob-config.js"; import { StandardActorSheet } from "./standard.js"; export class ThreatSheet extends StandardActorSheet { @@ -12,13 +13,14 @@ export class ThreatSheet extends StandardActorSheet { const sheetData = await super.getData(); sheetData.autoCalc.wounds = false; sheetData.autoCalc.shock = false; - + sheetData.mobAbilities = this.document.system.mob.abilities.documents.filter(i => !i.system.isActiveMobAbility); return sheetData; } activateListeners(html) { super.activateListeners(html); html.find(".item-cost").focusout(async (ev) => { await this._onItemCostFocusOut(ev); }); + html.find(".configure-mob").click(this._onConfigureMob.bind(this)); } @@ -32,4 +34,9 @@ export class ThreatSheet extends StandardActorSheet { this._render(true); } + + _onConfigureMob(event) + { + new MobConfig(this.document).render(true); + } } diff --git a/scripts/sheet/actor/vehicle.js b/scripts/sheet/actor/vehicle.js index 90de38e..ad93a19 100644 --- a/scripts/sheet/actor/vehicle.js +++ b/scripts/sheet/actor/vehicle.js @@ -112,11 +112,11 @@ export class VehicleSheet extends BaseWnGActorSheet { if (data.type == "Actor" && data.uuid) { let actor = await fromUuid(data.uuid); - this.actor.update({"system.complement.list" : this.actor.system.complement.add({id : actor.id, type: complementType})}) + this.actor.update(this.actor.system.complement.add(actor, complementType)) } else if (data.type == "complementDrag") { - this.actor.update({"system.complement.list" : this.actor.system.complement.edit(data.index, {type: complementType})}); + this.actor.update(this.actor.system.complement.edit(data.index, {type: complementType})); } super._onDrop(ev) } @@ -128,7 +128,7 @@ export class VehicleSheet extends BaseWnGActorSheet { html.find(".complement-delete").click(ev => { let index = $(ev.currentTarget).parents(".item").attr("data-index"); - this.actor.update({"system.complement.list" : this.actor.system.complement.remove(index)}) + this.actor.update(this.actor.system.complement.remove(index)) }) html.find(".vehicle-traits").click(ev => { @@ -142,12 +142,7 @@ export class VehicleSheet extends BaseWnGActorSheet { let weapon = this.actor.items.get(id); if (weapon) { - let tests = await actor.setupWeaponTest(weapon); - for(let test of tests) - { - await test.rollTest(); - test.sendToChat(); - } + actor.setupWeaponTest(weapon); } }); } diff --git a/scripts/sheet/item-sheet.js b/scripts/sheet/item-sheet.js index 5d55d80..af88c4b 100644 --- a/scripts/sheet/item-sheet.js +++ b/scripts/sheet/item-sheet.js @@ -1,9 +1,6 @@ -import ArchetypeGeneric from "../apps/archetype-generic.js"; -import ArchetypeGroups from "../apps/archetype-groups.js"; import ItemTraits from "../apps/item-traits.js"; -import { WrathAndGloryItem } from "../document/item.js"; -export class WrathAndGloryItemSheet extends ItemSheet { +export class WrathAndGloryItemSheet extends WarhammerItemSheet { static get defaultOptions() { return mergeObject(super.defaultOptions, { classes: ["wrath-and-glory", "sheet", "item"], @@ -59,15 +56,7 @@ export class WrathAndGloryItemSheet extends ItemSheet { const data = await super.getData(); - // If this is a temp item with an archetype parent - if (this.item.archetype) { - let list = duplicate(getProperty(this.item.archetype, this.item.archetypeItemPath)) - let wargearObj = list[this.item.archetypeItemIndex]; - mergeObject(data.system, wargearObj.diff, { overwrite: true }) // Merge archetype diff with item data - data.name = wargearObj.diff.name || data.item.name - } - else - data.name = data.item.name + data.name = data.item.name data.system = data.data.system // project system data so that handlebars has the same name and value paths @@ -75,7 +64,7 @@ export class WrathAndGloryItemSheet extends ItemSheet { return { name : i.name, key : i.id, - img : i.icon, + img : i.img, existing : this.item.hasCondition(i.id) } }) @@ -84,18 +73,13 @@ export class WrathAndGloryItemSheet extends ItemSheet { if (this.item.type == "archetype") { - let element = $(ArchetypeGroups.constructHTML(this.item, {parentheses: true, commas: true, draggable:false})) - // Remove unnecessary outside parentheses - let parentheses = Array.from(element.find(".parentheses")) - parentheses[0].remove(); - parentheses[parentheses.length - 1].remove() - data.wargearHTML = `
    ${element.html()}
    ` - - data.talents = this.item.suggested.talents.map(i => `${i.name}`).join(",") + data.wargearHTML = `
    ${this.item.system.wargear.textDisplay}
    ` + + data.talents = this.item.suggested.talents.list.map(i => `${i.name}`).join(",") } else if (this.item.type == "species") { - data.abilities = this.item.abilities.map(i => `${i.name}`).join(",") + data.abilities = this.item.abilities.list.map(i => `${i.name}`).join(",") } else if (this.item.type == "weapon" && this.item.isOwned) { @@ -181,13 +165,10 @@ async _handleEnrichment() _updatePotency(index, path, value) { - let potency = foundry.utils.deepClone(this.item.potency) if (Number.isNumeric(value) && typeof value != "boolean") // value = Number(value) - setProperty(potency[index], path, value) - - this.item.update({ "system.potency": potency }) + this.item.update(this.item.system.potency.edit(index, value, path)) } // Prevent upgrades from stacking @@ -217,39 +198,6 @@ async _handleEnrichment() new ItemTraits(this.item).render(true) }) - html.find(".effect-create").click(async ev => { - if (this.item.isOwned) - ui.notifications.error("Effects can only be added to world items or actors directly") - let effectData = { label: this.item.name, icon: this.item.img } - - let html = await renderTemplate("systems/wrath-and-glory/template/apps/quick-effect.hbs", effectData) - let dialog = new Dialog({ - title : "Quick Effect", - content : html, - buttons : { - "create" : { - label : "Create", - callback : html => { - let mode = 2 - let label = html.find(".label").val() - let key = html.find(".key").val() - let value = parseInt(html.find(".modifier").val()) - effectData.name = label - effectData.changes = [{key, mode, value}] - this.object.createEmbeddedDocuments("ActiveEffect", [effectData]) - } - }, - "skip" : { - label : "Skip", - callback : () => this.object.createEmbeddedDocuments("ActiveEffect", [effectData]).then(effect => effect[0].sheet.render(true)) - } - } - }) - await dialog._render(true) - dialog._element.find(".label").select() - - }) - html.find(".effect-edit").click(ev => { let id = $(ev.currentTarget).parents(".item").attr("data-effect-id") this.object.effects.get(id).sheet.render(true) @@ -305,16 +253,7 @@ async _handleEnrichment() }) html.find(".add-potency").click(ev => { - let potency = duplicate(this.item.potency) - potency.push({ - "description": "", - "cost": 1, - "property": "", - "initial": "", - "value": "", - "single" : false - }) - this.item.update({ "system.potency": potency }) + this.item.update(this.item.system.potency.add()) }) @@ -340,7 +279,7 @@ async _handleEnrichment() html.find(".potency-delete").click(ev => { let index = parseInt($(ev.currentTarget).parents(".potency-fields").attr("data-index")) - this.item._deleteIndex(index, "system.potency") + this.item.update(this.item.system.potency.remove(index)) }) html.find(".background-delete").click(ev => { @@ -379,27 +318,12 @@ async _handleEnrichment() }) - - html.find(".add-generic").click(async ev => { - new ArchetypeGeneric({item: this.item}).render(true) - }) - - html.find(".reset").click(ev => { - this.item.resetGroups(); - }) - - html.find(".configure-groups").click(ev => { - new ArchetypeGroups(this.item).render(true) - }) - html.find(".archetype-item,.species-item,.archetype-faction,.archetype-species").mouseup(async ev => { let id = ev.currentTarget.dataset.id; if (ev.button == 0) { - let item = await game.wng.utility.findItem(id) - if (!item) - item = await fromUuid(id) + let item = await warhammer.utility.findItemId(id) if (!item) { @@ -412,78 +336,26 @@ async _handleEnrichment() { if (ev.currentTarget.classList.contains("archetype-ability")) { - this.item.update({"system.ability" : {id: "", name: ""}}) + this.item.update(this.item.system.ability.unset()) } if (ev.currentTarget.classList.contains("archetype-faction")) { - this.item.update({"system.faction" : {id: "", name: ""}}) + this.item.update(this.item.system.faction.unset()) } if (ev.currentTarget.classList.contains("archetype-species")) { - this.item.update({"system.species" : {id: "", name: ""}}) + this.item.update(this.item.system.species.unset()) } else if (this.item.type == "archetype") // Is archetype talent { - let index = this.item.suggested.talents.findIndex(t => t.id == id) - let array = duplicate(this.item.suggested.talents) - array.splice(index, 1); - this.item.update({"system.suggested.talents" : array}) + this.item.update(this.item.system.suggested.talents.removeId(id)) } else if (this.item.type == "species") // TODO Combine these if statements { - let index = this.item.abilities.findIndex(t => t.id == id) - let array = duplicate(this.item.abilities) - array.splice(index, 1); - this.item.update({"system.abilities" : array}) + this.item.update(this.item.system.abilities.removeId(id)) } } }) - - html.find(".wargear").mouseup(async ev => { - let index = Number(ev.currentTarget.dataset.index) - let array = duplicate(this.item.wargear); - let obj = this.item.wargear[index]; - - if (obj) { - if (ev.button == 0) - { - if (obj.type == "generic") - new ArchetypeGeneric({item: this.item, index}).render(true); - else - { - let item = game.items.get(obj.id) - if (!item) - item = await fromUuid(obj.id) - - if (!item) - throw new Error("Could not find Item with ID " + obj.id) - - new Item.implementation(item.toObject(), { archetype: { item: this.item, index, path: "system.wargear" } }).sheet.render(true) - - } - } - else { - new Dialog({ - title: "Delete Item?", - content: "Do you want to remove this item from the Archetype? This will reset the groupings.", - buttons: { - yes: { - label: "Yes", - callback: async () => { - array.splice(index, 1) - await this.item.update({ "system.wargear" : array }) - this.item.resetGroups(); - } - }, - no: { - label: "No", - callback: () => { } - } - } - }).render(true) - } - } - }) } diff --git a/scripts/wrath-and-glory.js b/scripts/wrath-and-glory.js index f2a56d2..b06f45d 100644 --- a/scripts/wrath-and-glory.js +++ b/scripts/wrath-and-glory.js @@ -6,7 +6,6 @@ import WNG from "./common/config.js" import WNGUtility from "./common/utility.js" import { WNGTest, WrathDie, PoolDie } from "./common/tests/test.js"; import WeaponTest from "./common/tests/weapon-test.js"; -import WrathAndGloryEffectSheet from "./apps/active-effect-config.js"; import PowerTest from "./common/tests/power-test.js"; import CorruptionTest from "./common/tests/corruption-test.js"; import MutationTest from "./common/tests/mutation-test.js"; @@ -14,10 +13,7 @@ import ResolveTest from "./common/tests/resolve-test.js"; import DeterminationRoll from "./common/tests/determination.js"; import StealthRoll from "./common/tests/stealth.js"; import AbilityRoll from "./common/tests/ability-roll.js"; -import ModuleInitializer from "./apps/module-initialization.js" -import ModuleUpdater from "./apps/module-updater.js" -import {migrateWorld} from "./common/migration.js" -import TagManager from "./common/tag-manager.js"; +import Migration from "./common/migration.js" import { WrathAndGloryCombat, WrathAndGloryCombatant } from "./common/combat.js"; import WrathANdGloryCombatTracker from "./apps/combat-tracker.js"; import { WrathAndGloryOptionalCombat } from "./common/combat-optional.js"; @@ -51,6 +47,10 @@ import { WeaponModel } from "./model/item/weapon.js"; import { WeaponUpgradeModel } from "./model/item/weaponUpgrade.js"; import { ArchetypeModel } from "./model/item/archetype.js"; import { FactionModel } from "./model/item/faction.js"; +import { WrathAndGloryActiveEffectModel } from "./model/effect/effect.js"; +import WrathAndGloryActiveEffectConfig from "./apps/effect-config.js"; +import { WrathAndGloryDamageMessageModel, WrathAndGloryTestMessageModel } from "./model/message/message.js"; +import loadEffects from "./loadEffects.js"; Hooks.once("init", () => { @@ -58,10 +58,24 @@ Hooks.once("init", () => { CONFIG.Actor.documentClass = WrathAndGloryActor; CONFIG.Item.documentClass = WrathAndGloryItem; CONFIG.ActiveEffect.documentClass = WrathAndGloryEffect; - CONFIG.ActiveEffect.sheetClass = WrathAndGloryEffectSheet; + //CONFIG.ChatMessage.documentClass = SystemChatMessage; DocumentSheetConfig.registerSheet(JournalEntryPage, "wrath-and-glory", Level4TextPageSheet, { types : ["text"], makeDefault: true, label : "W&G Journal Sheet" }); DocumentSheetConfig.registerSheet(JournalEntryPage, "wrath-and-glory", DataslatePageSheet, { types : ["text"], makeDefault: false, label : "Data Slate" }); +CONFIG.ActiveEffect.legacyTransferral = false; +CONFIG.ActiveEffect.dataModels["base"] = WrathAndGloryActiveEffectModel +CONFIG.ChatMessage.dataModels["test"] = WrathAndGloryTestMessageModel; +CONFIG.ChatMessage.dataModels["damage"] = WrathAndGloryDamageMessageModel; + +DocumentSheetConfig.registerSheet(ActiveEffect, "system", WrathAndGloryActiveEffectConfig, {makeDefault : true}); + +Actors.unregisterSheet("core", ActorSheet); +Actors.registerSheet("wrath-and-glory", AgentSheet, { types: ["agent"], makeDefault: true }); +Actors.registerSheet("wrath-and-glory", ThreatSheet, { types: ["threat"], makeDefault: true }); +Actors.registerSheet("wrath-and-glory", VehicleSheet, { types: ["vehicle"], makeDefault: true }); +Items.unregisterSheet("core", ItemSheet); +Items.registerSheet("wrath-and-glory", WrathAndGloryItemSheet, {makeDefault : true}); + if (game.settings.get("wrath-and-glory", "initiativeRollOption")) { @@ -113,15 +127,10 @@ Hooks.once("init", () => { WrathDie, PoolDie, }, - apps: { - ModuleInitializer, - ModuleUpdater - }, ItemTraits, RuinGloryCounter, utility : WNGUtility, - migration : {migrateWorld}, - tags: new TagManager() + migration : Migration }; CONFIG.Dice.terms.w = WrathDie; @@ -130,13 +139,7 @@ Hooks.once("init", () => { game.wng.config = WNG CONFIG.Combat.initiative = { formula: "(@attributes.initiative.total)dp", decimals: 0 }; - Actors.unregisterSheet("core", ActorSheet); - Actors.registerSheet("wrath-and-glory", AgentSheet, { types: ["agent"], makeDefault: true }); - Actors.registerSheet("wrath-and-glory", ThreatSheet, { types: ["threat"], makeDefault: true }); - Actors.registerSheet("wrath-and-glory", VehicleSheet, { types: ["vehicle"], makeDefault: true }); - Items.unregisterSheet("core", ItemSheet); - Items.registerSheet("wrath-and-glory", WrathAndGloryItemSheet, {makeDefault : true}); - DocumentSheetConfig.registerSheet(ActiveEffect, "wrath-and-glory", WrathAndGloryEffectSheet, {makeDefault: true, label : "Wrath & Glory Active Effect Config"}) + initializeHandlebars(); CONFIG.fontDefinitions.Priori = {editor : true, fonts : []} @@ -147,3 +150,4 @@ Hooks.once("init", () => { hooks() +loadEffects(); \ No newline at end of file diff --git a/static/lang/en.json b/static/lang/en.json index 35eca25..4ec4c72 100644 --- a/static/lang/en.json +++ b/static/lang/en.json @@ -28,6 +28,8 @@ "ABILITY.REQUIREMENTS": "Requirements", "ABILITY.DISPLAY": "Display", "ABILITY.TYPE": "Type", + "ABILITY.DAMAGE": "Damage", + "ABILITY.TEST_SELF": "Test Self", "ABILITY_TYPE.BATTLECRY" : "Battlecry", "ABILITY_TYPE.ACTION" : "Action", @@ -160,6 +162,11 @@ "BUTTON.CONFIGURE": "Configure", "BUTTON.JOURNAL": "Journal", + "SHIFT.DAMAGE" : "Damage", + "SHIFT.GLORY" : "Glory", + "SHIFT.OTHER" : "Other", + "SHIFT.POTENCY" : "Potency", + "BUTTON.ApplyDamage" : "Apply Damage", "BUTTON.ApplyDamageInvuln" : "Apply Damage (Invulnerable armour)", "BUTTON.ApplyDamageArmourAP" : "Apply Damage (AP only on Armour)", @@ -187,8 +194,8 @@ "CHAT.WRATH_COMPLICATION_CORRUPTION": "Double Corruption points!", "CHAT.FAIL_CORRUPTION" : "Gain {corruption} Corruption, 1 Ruin added", "CHAT.FAIL_FEAR" : "Gains the @JournalEntry[Fear] Condition, 1 Ruin added", - "CHAT.DETERMINATION" : "Determination", - "CHAT.DETERMINATION_RESULT" : "{wounds} Wounds, {shock} Shock", + "CHAT.DETERMINATION_RESULT" : "Converted {shock} Wounds to Shock ({wounds} Wounds leftover)", + "CHAT.DETERMINATION_RESULT_IGNORE_SHOCK" : "Ignored {shock} Wounds ({wounds} Wounds leftover)", "CHAT.STEALTH" : "Stealth", "CHAT.STEALTH_RESULT" : "{stealth}", "CHAT.WOUNDS": "Wounds", @@ -198,6 +205,7 @@ "DIALOG.YES": "Yes", "DIALOG.NO": "No", "DIALOG.DN": "DN", + "DIALOG.DIFFICULTY": "Difficulty", "DIALOG.DICE_POOL": "Dice Pool", "DIALOG.BONUS": "Bonus", "DIALOG.PENALTY": "Penalty", @@ -212,6 +220,11 @@ "DIALOG.ITEM_DELETE" : "Item Delete", "DIALOG.ITEM_DELETE_PROMPT" : "Are you sure you want to delete this item?", "DIALOG.OUT_OF_RANGE" : "Out of Range!", + "DIALOG.ROLLMODE" : "Roll Mode", + "DIALOG.MODIFIER_BREAKDOWN" : "Modifier Breakdown", + "DIALOG.DICE_TOOLTIP" : "Number of D6s rolled to determine the value (e.g. 2 = 2D6)", + "DIALOG.POWER_LEVEL": "Power Level", + "DIALOG.IGNORE_SHOCK": "Ignore Shock", "GEAR.NAME": "Name", "GEAR.DESCRIPTION": "Description", @@ -303,7 +316,9 @@ "PSYCHIC_POWER.WOUNDS": "Wounds", "PSYCHIC_POWER.SHOCK": "Shock", "PSYCHIC_POWER.MORTAL": "Mortal", - + "PSYCHIC_POWER.BOUND" : "Bound", + "PSYCHIC_POWER.UNBOUND" : "Unbound", + "PSYCHIC_POWER.TRANSCENDENT" : "Transcendent", "POWER.POTENCY_COST" : "Cost", "POWER.POTENCY_INITIAL" : "Initial", @@ -398,7 +413,7 @@ "TALENT.NAME": "Name", "TALENT.EFFECT": "Effect", - "TALENT.COST": "Cost", + "TALENT.COST": "XP Cost", "TALENT.DESCRIPTION": "Description", "TALENT.REQUIREMENTS": "Requirements", "TALENT.DISPLAY": "Display", @@ -424,6 +439,8 @@ "TITLE.ADVANCES": "ADVANCES", "TITLE.DETAILS": "DETAILS", "TITLE.COMPLEMENT": "COMPLEMENT", + "TITLE.MOB_ABILITIES" : "MOB ABILITIES", + "TITLE.INACTIVE_MOB_ABILITIES" : "INACTIVE MOB ABILITIES", "TRAUMATIC_INJURY.NAME": "Name", "TRAUMATIC_INJURY.DESCRIPTION": "Description", @@ -446,6 +463,7 @@ "WEAPON.QUANTITY": "Quantity", "WEAPON.AIM": "Aim", "WEAPON.AIMED": "Aimed", + "WEAPON.CHARGING": "Charging", "WEAPON.COMBIWEAPON": "Combi-Weapon", "WEAPON.TWINNED": "Twinned", "WEAPON_UPGRADE.TRAITS_ADD" : "Traits Added", @@ -476,9 +494,12 @@ "ERROR.ID": "ID for {name} already exists in collection. This Document has been given a unique ID.", "ERROR.NoMoreRuin" : "No more Ruin", "NOTE.RuinSubtracted" : "Ruin Subtracted", - "NOTE.APPLY_DAMAGE_RESIST" : "{name} resisted the Damage", - "NOTE.APPLY_DAMAGE_SHOCK" : "{name} sustained 1 Shock", - "NOTE.APPLY_DAMAGE" : "{damage} Wounds applied to {name}", + "NOTE.APPLY_DAMAGE_REDUCED" : "{damage} Damage reduced by {res} Resilience", + "NOTE.APPLY_DAMAGE_DETERMINATION" : "Determination converted {shock} Wounds to Shock", + "NOTE.APPLY_DAMAGE_RESIST" : "{name} resisted the Damage", + "NOTE.APPLY_DAMAGE_SHOCK" : "{name} sustained {shock} Shock", + "NOTE.APPLY_DAMAGE" : "{wounds} Wounds and {shock} Shock applied to {name}", + "NOTE.APPLY_DAMAGE_MORTAL" : "{mortal} Mortal Wounds converted to normal", "ERROR.MacroItemMissing" : "Your controlled Actor does not have an item named", "ROLL.CannotFindTable" : "Cannot find table. Either import it from the Compendium or create your own. It must have the name {name}", "ERROR.NoAvailableActors" : "No available owned Actors to choose!", @@ -559,6 +580,8 @@ "EFFECT.HalfCover" : "Half Cover", "EFFECT.FullCover" : "Full Cover", "THREAT.MOB" : "Mob", + "THREAT.MobAbilities" : "Mob Abilities", + "THREAT.MobValue" : "Mob Value", "UpdaterTitle1": "Update", "UpdaterTitle2": "Content", @@ -596,5 +619,25 @@ "BugReport.ErrorForm": "Please fill out the form", "BugReport.ErrorName1": "Please include your Discord tag or email in the Name section.", "BugReport.ErrorName2": "Discord Tag or email is required in the name section.", - "BUTTON.PostBug" : "W&G Bug Report" + "BUTTON.PostBug" : "W&G Bug Report", + + "WH" : { + "Trigger" : { + "preTakeDamage" : "Pre-Take Damage", + "preApplyDamage" : "Pre-Apply Damage", + + "computeDamage" : "Compute Damage", + "preComputeDamage" : "Pre-Compute Damage", + + "takeDamage" : "Take Damage", + "applyDamage" : "Apply Damage", + + "rollTest" : "Roll Test", + "preRollTest" : "Pre-Roll Test", + "preRollWeaponTest" : "Pre-Roll Weapon Test", + "rollWeaponTest" : "Roll Weapon Test", + "preRollPowerTest" : "Pre-Roll Power Test", + "rollPowerTest" : "Roll Power Test" + } + } } diff --git a/static/template/actor/agent.hbs b/static/template/actor/agent.hbs index 458ada1..ac3f15c 100644 --- a/static/template/actor/agent.hbs +++ b/static/template/actor/agent.hbs @@ -10,18 +10,33 @@
    - - + + {{#if actor.system.species.document}} + + + {{else}} + + {{/if}}
    - - + + {{#if actor.system.faction.document}} + + + {{else}} + + {{/if}}
    - - + + {{#if actor.system.archetype.document}} + + + {{else}} + + {{/if}}
    diff --git a/static/template/actor/tab/combat.hbs b/static/template/actor/tab/combat.hbs index 043aa66..240a0a8 100644 --- a/static/template/actor/tab/combat.hbs +++ b/static/template/actor/tab/combat.hbs @@ -135,6 +135,8 @@
    {{/if}} + +

    {{localize "TITLE.WARGEAR"}}

    @@ -162,7 +164,7 @@
    {{#if item.isRanged}} + {{#if actor.system.species.document}} + + + {{else}} + + {{/if}}
    - + {{#if actor.system.faction.document}} + + + {{else}} + + {{/if}}
    - + {{#if actor.system.archetype.document}} + + + {{else}} + + {{/if}}
    @@ -31,14 +46,15 @@ +
    - - + +
    diff --git a/static/template/apps/actor-configure.hbs b/static/template/apps/actor-configure.hbs index 808b6d3..a96bbf7 100644 --- a/static/template/apps/actor-configure.hbs +++ b/static/template/apps/actor-configure.hbs @@ -47,18 +47,6 @@
    -
    - - -
    - -
    - - -
    - -
    -
    diff --git a/static/template/apps/archetype-generic.hbs b/static/template/apps/archetype-generic.hbs deleted file mode 100644 index e2b4e93..0000000 --- a/static/template/apps/archetype-generic.hbs +++ /dev/null @@ -1,44 +0,0 @@ -
    - -
    - - -
    - - - - - {{#each filters as |filter f|}} -
    -
    - - -
    - -
    - - -
    - -
    - - -
    - - {{/each}} - - {{#if object.index}} - Add Filter - {{/if}} - - -
    \ No newline at end of file diff --git a/static/template/apps/bug-report.hbs b/static/template/apps/bug-report.hbs deleted file mode 100644 index 270d342..0000000 --- a/static/template/apps/bug-report.hbs +++ /dev/null @@ -1,45 +0,0 @@ -
    -
    - - -
    - -
    - - -
    - -
    - - -
    -

    {{localize "GRIEVANCE.Warning1"}}{{localize "GRIEVANCE.Warning2"}} - -

    - - -
    - -
    - -
    - - -

    {{localize "GRIEVANCE.Warning3"}} {{localize "GRIEVANCE.Warning4"}}

    - -
    diff --git a/static/template/apps/character-creation.hbs b/static/template/apps/character-creation.hbs index 0a25ae1..751e5da 100644 --- a/static/template/apps/character-creation.hbs +++ b/static/template/apps/character-creation.hbs @@ -117,7 +117,7 @@

    {{localize "FACTION.ORIGIN"}}

      {{#each faction.backgrounds.origin as |bg index|}} -
    1. {{bg.name}}: {{bg.description}}
    2. +
    3. {{bg.name}}: {{bg.description}}
    4. {{/each}}
    {{/if}} @@ -125,7 +125,7 @@

    {{localize "FACTION.ACCOMPLISHMENT"}}

      {{#each faction.backgrounds.accomplishment as |bg index|}} -
    1. {{bg.name}}: {{bg.description}}
    2. +
    3. {{bg.name}}: {{bg.description}}
    4. {{/each}}
    {{/if}} @@ -133,7 +133,7 @@

    {{localize "FACTION.GOAL"}}

      {{#each faction.backgrounds.goal as |bg index|}} -
    1. {{bg.name}}: {{bg.description}}
    2. +
    3. {{bg.name}}: {{bg.description}}
    4. {{/each}}
    {{/if}} diff --git a/static/template/apps/document-resolver.hbs b/static/template/apps/document-resolver.hbs deleted file mode 100644 index 2fd0511..0000000 --- a/static/template/apps/document-resolver.hbs +++ /dev/null @@ -1,15 +0,0 @@ -
    -
    -

    {{{localize "UPDATER.ResolveDuplicatesNote"}}}

    - {{#each object}} -
    - - {{this.name}} - -
    - {{/each}} -
    - - -
    - \ No newline at end of file diff --git a/static/template/apps/effect-avoid-test.hbs b/static/template/apps/effect-avoid-test.hbs new file mode 100644 index 0000000..73ed4d3 --- /dev/null +++ b/static/template/apps/effect-avoid-test.hbs @@ -0,0 +1,60 @@ +
    + +
    + +
    +
    +
    +
    + + {{#if (eq system.transferData.avoidTest.type "attribute")}} + + {{/if}} + {{#if (eq system.transferData.avoidTest.type "skill")}} + + {{/if}} + {{#if (eq system.transferData.avoidTest.type "resolve")}} + + {{/if}} + {{#if (eq system.transferData.avoidTest.type "conviction")}} + + {{/if}} +
    +
    +
    \ No newline at end of file diff --git a/static/template/apps/effect-key-options.hbs b/static/template/apps/effect-key-options.hbs new file mode 100644 index 0000000..b0adf12 --- /dev/null +++ b/static/template/apps/effect-key-options.hbs @@ -0,0 +1,48 @@ + \ No newline at end of file diff --git a/static/template/apps/effect-script.hbs b/static/template/apps/effect-script.hbs deleted file mode 100644 index ca09ba1..0000000 --- a/static/template/apps/effect-script.hbs +++ /dev/null @@ -1,24 +0,0 @@ -
    -
    - -
    - -
    -
    - - - {{#if aceActive}} -

    Activate if

    -
    -

    Hide if

    -
    - {{else}} -

    Activate if

    - -

    Hide if

    - - {{/if}} - - - -
    \ No newline at end of file diff --git a/static/template/apps/filter-results.hbs b/static/template/apps/filter-results.hbs deleted file mode 100644 index dd73641..0000000 --- a/static/template/apps/filter-results.hbs +++ /dev/null @@ -1,15 +0,0 @@ -
    -
    -
    -
      - {{#each items as |item i|}} -
    1. - -

      {{item.name}}

      -
    2. - {{/each}} -
    -
    -
    - -
    \ No newline at end of file diff --git a/static/template/apps/item-dialog.hbs b/static/template/apps/item-dialog.hbs deleted file mode 100644 index cb8f434..0000000 --- a/static/template/apps/item-dialog.hbs +++ /dev/null @@ -1,10 +0,0 @@ -
    -
      - {{#each items as |item i|}} -
    1. - -

      {{item.name}}

      -
    2. - {{/each}} -
    -
    \ No newline at end of file diff --git a/static/template/apps/mob-config.hbs b/static/template/apps/mob-config.hbs new file mode 100644 index 0000000..91097e0 --- /dev/null +++ b/static/template/apps/mob-config.hbs @@ -0,0 +1,28 @@ +
    +

    Drag and Drop Items from the Actor to designate them as Mob Abilities.

    +
    +
    +
    {{localize "THREAT.MobAbilities"}}
    +
    {{localize "THREAT.MobValue"}}
    +
    +
    +
    + {{#each abilities.list}} +
    +
    + +
    + +
    +
    + +
    +
    +
    + {{/each}} +
    +
    +
    \ No newline at end of file diff --git a/static/template/apps/module-updater.hbs b/static/template/apps/module-updater.hbs deleted file mode 100644 index 2eb1651..0000000 --- a/static/template/apps/module-updater.hbs +++ /dev/null @@ -1,16 +0,0 @@ -
    - -

    {{localize "Updater1"}} {{localize "Updater2"}}

    -

    {{localize "Updater3"}}

    - - - -
    diff --git a/static/template/apps/quick-effect.hbs b/static/template/apps/quick-effect.hbs deleted file mode 100644 index 840b1b9..0000000 --- a/static/template/apps/quick-effect.hbs +++ /dev/null @@ -1,67 +0,0 @@ -
    -
    -
    - -
    - -
    -
    - -
    - - -
    - -
    - -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/static/template/chat/roll/ability/ability-use.hbs b/static/template/chat/roll/ability/ability-use.hbs new file mode 100644 index 0000000..f661995 --- /dev/null +++ b/static/template/chat/roll/ability/ability-use.hbs @@ -0,0 +1,25 @@ +
    +
    +

    {{context.title}}

    +
    + +
    + {{{result.enrichedDescription}}} +
    + +{{#if result.damage}} +
    + +
    +{{/if}} + +{{#if showTest}} +
    + +
    +{{/if}} + +
    +{{> effectButtons}} +
    +
    \ No newline at end of file diff --git a/static/template/chat/roll/base/base-buttons.hbs b/static/template/chat/roll/base/base-buttons.hbs index b34db7c..2aacb36 100644 --- a/static/template/chat/roll/base/base-buttons.hbs +++ b/static/template/chat/roll/base/base-buttons.hbs @@ -4,18 +4,12 @@ {{/if}} -{{#if showEffects}} -
    +{{#if showTest}}
    - {{#each testEffects as |effect e|}} - - {{/each}} +
    {{/if}} -{{#if showTest}} -
    - -
    -{{/if}} \ No newline at end of file +{{> effectButtons}} + \ No newline at end of file diff --git a/static/template/chat/roll/base/base-result.hbs b/static/template/chat/roll/base/base-result.hbs index 8e38340..79e81df 100644 --- a/static/template/chat/roll/base/base-result.hbs +++ b/static/template/chat/roll/base/base-result.hbs @@ -3,20 +3,15 @@ {{else}}
    {{/if}} - - {{#if context.targets.length}} -
    - - - - {{#each context.targets as |target t|}} - {{target.name}} - {{/each}} -
    - {{/if}} + {{> chatTargets targets=targetTokens}} +

    {{localize "CHAT.SUCCESS"}}: {{result.success}}

    {{localize "CHAT.FAILURE"}}: {{result.failure}}

    {{#if isShiftable}}

    {{localize "CHAT.SHIFTING"}}: {{result.shiftsPossible}}

    - {{/if}} \ No newline at end of file + {{/if}} + + {{#each result.text}} +

    {{this.label}}: {{this.description}}

    + {{/each}} \ No newline at end of file diff --git a/static/template/chat/roll/base/base-roll.hbs b/static/template/chat/roll/base/base-roll.hbs index e40ff83..a5da9ff 100644 --- a/static/template/chat/roll/base/base-roll.hbs +++ b/static/template/chat/roll/base/base-roll.hbs @@ -1,6 +1,5 @@
    -

    {{context.title}}

    - +

    {{context.title}}

    {{#if result.dn}}

    {{localize "CHAT.DN"}} {{result.dn}}

    {{/if}} diff --git a/static/template/chat/roll/base/dice-container.hbs b/static/template/chat/roll/base/dice-container.hbs index 4b5dc66..6e2a4b7 100644 --- a/static/template/chat/roll/base/dice-container.hbs +++ b/static/template/chat/roll/base/dice-container.hbs @@ -12,19 +12,10 @@

    Shifted

    {{#each result.shifted as |die|}} + {{log this}} - {{log die}} - {{#if (eq die.shift "glory")}} - G - {{/if}} - {{#if (eq die.shift "damage")}} - D - {{/if}} - {{#if (eq die.shift "potency")}} - P - {{/if}} - {{#if (eq die.shift "other")}} - ? + {{#if die.shifted}} + {{die.shifted.letter}} {{/if}} {{#if die.rerolled}} diff --git a/static/template/chat/roll/damage/ability-roll.hbs b/static/template/chat/roll/damage/ability-roll.hbs deleted file mode 100644 index b69e71d..0000000 --- a/static/template/chat/roll/damage/ability-roll.hbs +++ /dev/null @@ -1,53 +0,0 @@ -
    -
    -

    {{context.title}}

    - -
    - {{#each result.damage.dice as |die|}} - - {{/each}} -
    -
    - - {{#if result.damage}} -
    - {{#if result.damage.total}} -

    {{localize "WEAPON.DAMAGE"}}: {{result.damage.total}}

    - {{/if}} - {{#if result.damage.ap}} -

    {{localize "WEAPON.AP"}}: {{result.damage.ap}}

    - {{/if}} - - {{#if result.damage.other.mortalWounds.total}} -

    {{localize "CHAT.MORTAL"}}: {{result.damage.other.mortalWounds.total}}

    - {{/if}} - - {{#if result.damage.other.wounds.total}} -

    {{localize "CHAT.WOUNDS"}}: {{result.damage.other.wounds.total}}

    - {{/if}} - - {{#if result.damage.other.shock.total}} -

    {{localize "CHAT.SHOCK"}}: {{result.damage.other.shock.total}}

    - {{/if}} - - {{#if item.system.traits}} -

    {{localize "WEAPON.TRAITS"}}: {{item.system.traits.formatted}}

    - {{/if}} -
    - {{/if}} - {{#if showEffects}} -
    -
    - {{#each testEffects as |effect e|}} - - {{/each}} -
    - {{/if}} - - {{#if showTest}} -
    -
    - -
    - {{/if}} -
    diff --git a/static/template/chat/roll/damage/damage-roll.hbs b/static/template/chat/roll/damage/damage-roll.hbs index 84e85ee..0afe216 100644 --- a/static/template/chat/roll/damage/damage-roll.hbs +++ b/static/template/chat/roll/damage/damage-roll.hbs @@ -1,49 +1,61 @@
    -

    {{localize "WEAPON.DAMAGE"}}: {{result.damage.total}}

    - {{#if result.damage.ap}} -

    {{localize "WEAPON.AP"}}: {{result.damage.ap}}

    + {{#if targetTokens.length}} +
    + + + + {{#each targetTokens as |target t|}} + {{target.name}} + {{/each}} +
    + {{/if}} +

    {{localize "WEAPON.DAMAGE"}}: {{result.total}}

    + {{#if result.ap}} +

    {{localize "WEAPON.AP"}}: {{result.ap}}

    {{/if}} - {{#if result.damage.other.mortalWounds.total}} -

    {{localize "CHAT.MORTAL"}}: {{result.damage.other.mortalWounds.total}}

    + {{#if result.other.mortal}} +

    {{localize "CHAT.MORTAL"}}: {{result.other.mortal}}

    {{/if}} - {{#if result.damage.other.wounds.total}} -

    {{localize "CHAT.WOUNDS"}}: {{result.damage.other.wounds.total}}

    + {{#if result.other.wounds}} +

    {{localize "CHAT.WOUNDS"}}: {{result.other.wounds}}

    {{/if}} - {{#if result.damage.other.shock.total}} -

    {{localize "CHAT.SHOCK"}}: {{result.damage.other.shock.total}}

    + {{#if result.other.shock}} +

    {{localize "CHAT.SHOCK"}}: {{result.other.shock}}

    {{/if}} - {{#if item.system.traits}} -

    {{localize "WEAPON.TRAITS"}}: {{item.system.traits.formatted}}

    + {{#if item.system.traits.list.length}} +

    {{localize "WEAPON.TRAITS"}}: {{item.system.traits.formatted}}

    {{/if}}
    - {{#if showEffects}} -
    -
    - {{#each testEffects as |effect e|}} - - {{/each}} -
    - {{/if}} - {{#if showTest}} -
    -
    - + {{#if context.appliedReport}} +

    Applied Damage

    +
    + {{{context.appliedReport}}}
    {{/if}}
    + +{{#unless context.appliedReport}} +
    + +
    +{{/unless}} \ No newline at end of file diff --git a/static/template/chat/roll/determination/determination-result.hbs b/static/template/chat/roll/determination/determination-result.hbs index 239169b..f41f99c 100644 --- a/static/template/chat/roll/determination/determination-result.hbs +++ b/static/template/chat/roll/determination/determination-result.hbs @@ -1 +1,5 @@ -

    {{localize "CHAT.DETERMINATION"}}: {{localize "CHAT.DETERMINATION_RESULT" wounds=result.wounds shock=result.shock}}

    +{{#if result.shockIgnored}} +

    {{localize "CHAT.DETERMINATION_RESULT_IGNORE_SHOCK" wounds=result.wounds shock=result.converted}}

    +{{else}} +

    {{localize "CHAT.DETERMINATION_RESULT" wounds=result.wounds shock=result.converted}}

    +{{/if}} diff --git a/static/template/dialog/attack-roll.hbs b/static/template/dialog/attack-roll.hbs new file mode 100644 index 0000000..506b537 --- /dev/null +++ b/static/template/dialog/attack-roll.hbs @@ -0,0 +1,67 @@ +
    +{{log this}} +
    + + +
    +
    + +
    Value
    +
    Dice
    +
    +
    + + + +
    +
    + + + +
    +{{#if data.weapon.isRanged}} +
    + + + + + +
    +{{else if data.weapon.isMelee}} +
    + + +
    +{{/if}} +
    +
    +
    + +
    1
    +
    2
    +
    3
    +
    4
    +
    5
    +
    6
    +
    +
    + + + + + + + +
    +
    +
    + + +
    \ No newline at end of file diff --git a/static/template/dialog/common-roll.hbs b/static/template/dialog/common-roll.hbs index 1c64ffc..9c2b42b 100644 --- a/static/template/dialog/common-roll.hbs +++ b/static/template/dialog/common-roll.hbs @@ -1,66 +1,44 @@ -
    +

    {{title}}

    - {{#if targets.length}} -
    - - - - {{#each targets as |target t|}} - {{target.name}} - {{/each}} -
    - {{/if}} -
    - - {{localize "DIALOG.BASE"}} - {{localize "DIALOG.RANK"}} - {{localize "DIALOG.BONUS"}} -
    + {{log this}} +
    +
    + {{#if data.targets.length}} +
    + + + + {{#each data.targets as |target t|}} + {{target.name}} + {{/each}} +
    + {{/if}} +
    + + +
    +
    + + +
    + {{#unless noWrath}} +
    + + +
    + {{/unless}} +
    + + +
    -
    - - - - -
    -
    - - - - -
    - {{#if determination}} -
    - - -
    - {{else}} -
    - - -
    - {{/if}} + {{{subTemplate}}} +
    -
    -
    -
    -

    {{localize "HEADER.EFFECTS"}}

    - + {{> dialogModifiers}}
    -
    + + +
    \ No newline at end of file diff --git a/static/template/dialog/damage-roll.hbs b/static/template/dialog/damage-roll.hbs deleted file mode 100644 index e03ee0c..0000000 --- a/static/template/dialog/damage-roll.hbs +++ /dev/null @@ -1,54 +0,0 @@ -
    -

    {{title}} - Damage

    -
    - - {{localize "DIALOG.BASE"}} - {{localize "DIALOG.RANK"}} - {{localize "DIALOG.BONUS"}} -
    -
    - - - - -
    -
    - - - - -
    -
    - - - - -
    -
    - - -
    -
    - \ No newline at end of file diff --git a/static/template/dialog/determination-roll.hbs b/static/template/dialog/determination-roll.hbs new file mode 100644 index 0000000..7539b2f --- /dev/null +++ b/static/template/dialog/determination-roll.hbs @@ -0,0 +1,9 @@ +
    + + +
    + +
    + + +
    \ No newline at end of file diff --git a/static/template/dialog/power-roll.hbs b/static/template/dialog/power-roll.hbs new file mode 100644 index 0000000..e8743df --- /dev/null +++ b/static/template/dialog/power-roll.hbs @@ -0,0 +1,6 @@ +
    + + +
    \ No newline at end of file diff --git a/static/template/dialog/psychic-roll.hbs b/static/template/dialog/psychic-roll.hbs deleted file mode 100644 index b8a39c1..0000000 --- a/static/template/dialog/psychic-roll.hbs +++ /dev/null @@ -1,110 +0,0 @@ -
    -
    -

    {{localize "SKILL.PSYCHIC_MASTERY"}}

    - {{#if targets.length}} -
    - - - - {{#each targets as |target t|}} - {{target.name}} - {{/each}} -
    - {{/if}} -
    - - {{localize "DIALOG.BASE"}} - {{localize "DIALOG.RANK"}} - {{localize "DIALOG.BONUS"}} -
    -
    - - - - -
    -
    - - - - -
    -
    - - -
    -
    -
    -

    {{power.name}}

    -
    - - {{localize "DIALOG.BASE"}} - {{localize "DIALOG.RANK"}} - {{localize "DIALOG.BONUS"}} -
    -
    - - - - -
    -
    - - - - -
    -
    - - 1 - 2 - 3 - 4 - 5 - 6 -
    -
    - - - - - - - -
    -
    -
    -
    -
    -

    {{localize "HEADER.EFFECTS"}}

    - -
    -
    \ No newline at end of file diff --git a/static/template/dialog/weapon-roll.hbs b/static/template/dialog/weapon-roll.hbs deleted file mode 100644 index 54bf229..0000000 --- a/static/template/dialog/weapon-roll.hbs +++ /dev/null @@ -1,143 +0,0 @@ -
    -
    -

    {{localize skill.label}}

    - {{#if targets.length}} -
    - - - - {{#each targets as |target t|}} - {{target.name}} - {{/each}} -
    - {{/if}} -
    - - {{localize "DIALOG.BASE"}} - {{localize "DIALOG.RANK"}} - {{localize "DIALOG.BONUS"}} -
    -
    - - - - -
    -
    - - - - -
    -
    -
    - - -
    -
    -

    {{weapon.name}}

    -
    - - {{localize "DIALOG.BASE"}} - {{localize "DIALOG.RANK"}} - {{localize "DIALOG.BONUS"}} - {{localize "DIALOG.DICE"}} -
    -
    - - - - - -
    -
    - - - - - -
    -
    - - - - - -
    - {{#if weapon.isRanged}} -
    - - - - -
    -
    - {{/if}} - -
    - - 1 - 2 - 3 - 4 - 5 - 6 -
    -
    - - - - - - - -
    -
    -
    -
    -
    -

    {{localize "HEADER.EFFECTS"}}

    - -
    -
    \ No newline at end of file diff --git a/static/template/item/ability.hbs b/static/template/item/ability.hbs index 85922ee..214eb2f 100644 --- a/static/template/item/ability.hbs +++ b/static/template/item/ability.hbs @@ -33,7 +33,7 @@
    - +
    @@ -68,145 +68,8 @@
    {{#unless (eq system.abilityType "determination")}} -
    -
    - -
    - -
    -
    - - {{#if (eq system.test.type "attribute")}} - - {{/if}} - {{#if (eq system.test.type "skill")}} - - {{/if}} - {{#if (eq system.test.type "resolve")}} - - {{/if}} - {{#if (eq system.test.type "conviction")}} - - {{/if}} -
    -
    -
    -
    -
    - - {{localize "DIALOG.BASE"}} - {{localize "DIALOG.RANK"}} - {{localize "DIALOG.BONUS"}} -
    -
    - -
    - - - -
    -
    -
    -
    -
    - -
    - - - -
    -
    -
    -
    -
    - -
    - - - -
    -
    -
    - -
    -
    - - {{localize "PSYCHIC_POWER.SHOCK"}} - {{localize "PSYCHIC_POWER.WOUNDS"}} - {{localize "PSYCHIC_POWER.MORTAL"}} -
    -
    - -
    - - - -
    -
    -
    - -
    -
    - -
    - -
    -
    -
    + {{> test}} + {{> damage}} {{/unless}}
    diff --git a/static/template/item/archetype.hbs b/static/template/item/archetype.hbs index 645e590..c5a17dc 100644 --- a/static/template/item/archetype.hbs +++ b/static/template/item/archetype.hbs @@ -98,16 +98,8 @@
    -

    {{localize "ARCHETYPE.WARGEAR"}} - - - -

    -
    - {{{wargearHTML}}} -
    + {{> choiceDisplay label="ARCHETYPE.WARGEAR" path="wargear" display=item.system.wargear.textDisplay}}
    -

    {{localize "ARCHETYPE.SUGGESTED_ATTRIBUTES"}}

    diff --git a/static/template/item/faction.hbs b/static/template/item/faction.hbs index e9d3874..1e8c9c3 100644 --- a/static/template/item/faction.hbs +++ b/static/template/item/faction.hbs @@ -38,8 +38,8 @@ {{#each system.backgrounds.origin as |background bg|}}
    {{#if ../item.isOwned}} -
    - {{#if background.active}} +
    + {{#if background.chosen}} {{/if}}
    @@ -47,7 +47,7 @@ +
    @@ -89,7 +89,7 @@
    - +
    - - -
    -
    -
    -
    -
    - -
    - - - -
    -
    -
    - -
    -
    - - {{localize "PSYCHIC_POWER.SHOCK"}} - {{localize "PSYCHIC_POWER.WOUNDS"}} - {{localize "PSYCHIC_POWER.MORTAL"}} -
    -
    - -
    - - - -
    -
    -
    - + {{> damage hideAP=true}}
    @@ -212,7 +156,7 @@

    {{localize "TITLE.POTENCY"}}

    - {{#if system.potency.length}} + {{#if system.potency.list.length}}
    {{localize "POWER.POTENCY_COST"}}
    @@ -221,12 +165,12 @@
    {{localize "POWER.POTENCY_VALUE"}}
    {{localize "POWER.POTENCY_SINGLE"}}
    - {{#each system.potency as |potency index|}} + {{#each system.potency.list as |potency index|}}
    - + diff --git a/static/template/item/talent.hbs b/static/template/item/talent.hbs index 2f8334c..af96496 100644 --- a/static/template/item/talent.hbs +++ b/static/template/item/talent.hbs @@ -55,6 +55,9 @@
    + {{> test}} + {{> damage}} +
    diff --git a/static/template/item/weapon.hbs b/static/template/item/weapon.hbs index 589a219..89ee544 100644 --- a/static/template/item/weapon.hbs +++ b/static/template/item/weapon.hbs @@ -43,7 +43,7 @@
    - +
    @@ -52,15 +52,15 @@
    - +
    - - - + + +
    @@ -69,87 +69,18 @@
    - +
    - +
    {{/if}} -
    -
    - - {{localize "DIALOG.BASE"}} - {{localize "DIALOG.RANK"}} - {{localize "DIALOG.BONUS"}} - {{localize "DIALOG.DICE"}} -
    -
    - -
    - - - - -
    -
    -
    -
    -
    - -
    - - - - -
    -
    -
    -
    -
    - -
    - - - - -
    -
    -
    - -
    -
    - -
    - -
    -
    -
    + {{> damage hideEnabled=true hideOther=true}}
    {{#if (eq item.actor.type "vehicle")}} @@ -185,7 +116,7 @@
    - +
    @@ -233,6 +164,7 @@ {{/each}}
    +
    {{> systems/wrath-and-glory/template/item/tab/effects.hbs}} diff --git a/static/template/item/weaponUpgrade.hbs b/static/template/item/weaponUpgrade.hbs index eff7bfa..bce745d 100644 --- a/static/template/item/weaponUpgrade.hbs +++ b/static/template/item/weaponUpgrade.hbs @@ -74,7 +74,6 @@
    - Note: These effects will be applied to the weapon. {{> systems/wrath-and-glory/template/item/tab/effects.hbs}}
    diff --git a/static/template/partials/chat-targets.hbs b/static/template/partials/chat-targets.hbs new file mode 100644 index 0000000..c1b9e8a --- /dev/null +++ b/static/template/partials/chat-targets.hbs @@ -0,0 +1,10 @@ +{{#if targets}} +
    + + + + {{#each targets as |target t|}} + {{target.name}} + {{/each}} +
    +{{/if}} \ No newline at end of file diff --git a/static/template/partials/damage.hbs b/static/template/partials/damage.hbs new file mode 100644 index 0000000..f39db15 --- /dev/null +++ b/static/template/partials/damage.hbs @@ -0,0 +1,102 @@ +
    + + +{{#unless hideEnabled}} +
    + +
    +
    + {{#if system.damage.enabled}} + + {{/if}} +
    +
    +
    +
    + +{{/unless}} +{{#if system.damage.enabled}} +
    +
    + + {{localize "DIALOG.BASE"}} + {{localize "DIALOG.BONUS"}} + {{localize "DIALOG.RANK"}} + {{localize "DIALOG.DICE"}} +
    + {{#unless hideDamage}} +
    + +
    + + + + +
    +
    + {{/unless}} +
    +{{#unless hideED}} +
    +
    + +
    + + + + +
    +
    +
    +{{/unless}} +{{#unless hideAP}} +
    +
    + +
    + + + + +
    +
    +
    +{{/unless}} + + +{{#unless hideOther}} +
    +
    + + {{localize "PSYCHIC_POWER.SHOCK"}} + {{localize "PSYCHIC_POWER.WOUNDS"}} + {{localize "PSYCHIC_POWER.MORTAL"}} +
    +
    + +
    + + + +
    +
    +
    +{{/unless}} + +{{#unless hideTraits}} +
    +
    + +
    + +
    +
    +
    +{{/unless}} +{{/if}} \ No newline at end of file diff --git a/static/template/partials/test.hbs b/static/template/partials/test.hbs new file mode 100644 index 0000000..2971871 --- /dev/null +++ b/static/template/partials/test.hbs @@ -0,0 +1,70 @@ +
    + {{#unless hideSelfTest}} +
    + +
    +
    + {{#if system.test.self}} + + {{/if}} +
    +
    +
    + {{/unless}} +
    + +
    + +
    +
    + + {{#if (eq system.test.type "attribute")}} + + {{/if}} + {{#if (eq system.test.type "skill")}} + + {{/if}} + {{#if (eq system.test.type "resolve")}} + + {{/if}} + {{#if (eq system.test.type "conviction")}} + + {{/if}} +
    +
    +
    \ No newline at end of file diff --git a/style/chat/_chat.scss b/style/chat/_chat.scss index 13437ce..ebd01dd 100644 --- a/style/chat/_chat.scss +++ b/style/chat/_chat.scss @@ -29,6 +29,24 @@ margin: 0px 5px; } +.wrath-and-glory.chat { + .test-buttons { + button { + display: flex; + align-items: center; + text-align: center; + line-height: unset; + p { + flex: 1; + margin: 5px 0px; + } + .test-label { + font-size: 12px; + } + } + } +} + .wrath-and-glory.chat strong { font-family: var(--font-text); @@ -146,10 +164,10 @@ } -.wrath-and-glory.chat .die.shifted .die-icon.potency, -.wrath-and-glory.chat .die.shifted .die-icon.other, -.wrath-and-glory.chat .die.shifted .die-icon.damage, - .wrath-and-glory.chat .die.shifted .die-icon.glory{ +.wrath-and-glory.chat .die.shifted .die-icon.shift-icon, +.wrath-and-glory.chat .die.shifted .die-icon.shift-icon, +.wrath-and-glory.chat .die.shifted .die-icon.shift-icon, + .wrath-and-glory.chat .die.shifted .die-icon.shift-icon{ left: -5px; } diff --git a/style/common/_apps.scss b/style/common/_apps.scss index 5736a6e..7d1c9eb 100644 --- a/style/common/_apps.scss +++ b/style/common/_apps.scss @@ -66,30 +66,6 @@ } -.active-effect-sheet .change-flags { - display: flex; - align-items: center; - justify-content: center; -} - - -.active-effect-sheet .change-flags .conditional-code { - flex: 0 0 20px; - margin: 5px; -} - -#effect-script-config .ace-editor, - #effect-script-config textarea { - width: 100%; - height: calc(50% - 65px); - resize: none; -} - -#effect-script-config .ace-editor > *{ - height: 95% -} - - #character-creation .character-creation{ height: calc(100% - 30px); width: 100%; diff --git a/style/common/_sheet.scss b/style/common/_sheet.scss index 699a7d9..be17553 100644 --- a/style/common/_sheet.scss +++ b/style/common/_sheet.scss @@ -399,4 +399,11 @@ display: none .wrath-and-glory.sheet .item-label:hover{ background: var(--color-imperium); cursor: pointer; +} + +.warhammer.active-effect-sheet { + + .script { + background-color: var(--color-imperium); + } } \ No newline at end of file diff --git a/style/dialog/_dialog.scss b/style/dialog/_dialog.scss index 4bcfb8a..f6b3289 100644 --- a/style/dialog/_dialog.scss +++ b/style/dialog/_dialog.scss @@ -1,139 +1,152 @@ -.dialog .quick-effect > * { - margin: 5px;display: flex; align-items: center; justify-content: space-between; -} - -.roll-dialog h1, - .wrath-and-glory.dialog h1 { - margin: 0; - border-style: solid; - border-width: 0px 8px 8px 8px; - border-image: var(--image-title-border) 8 8 8 8 repeat repeat; - color: var(--color-red); - font-weight: bold; - font-size: 18px; - text-align: center; - padding: 0 30px; -} - -.wrath-and-glory.dialog label { - text-align: center; - background-color: var(--color-black); - color: var(--color-white); - padding: 5px; - font-family: var(--font-text); - font-size: 14px; - border: 1px solid var(--color-black); - height: 31px; -} - -.wrath-and-glory.dialog .hover:hover { - background-color: var(--color-red); -} - -.wrath-and-glory.dialog input[type="text"], -.wrath-and-glory.dialog input[type="number"], -.wrath-and-glory.dialog select { - background: var(--image-input) repeat; - border: 1px solid var(--color-black); - border-radius: 0; - border-left: 0; - box-shadow: inset 0 0 6px var(--color-dark-grey); - color: var(--color-black); - font-size: 14px; - font-family: var(--font-text); - text-align: center; - height: 31px; -} - -.wrath-and-glory.dialog { +.wrath-and-glory.roll-dialog +{ + form { + display: flex; + flex-direction: column; + } display: flex; flex-direction: column; - margin-bottom: 8px; - flex: 0 0 380px -} - -.dialog .dialog-buttons{ - flex: 0 0 35px; - -} - -.roll-dialog .dialog-content { - display: flex; -} - -.roll-dialog .dialog-effects { - flex: 1 -} - -.roll-dialog .dialog-effects option { - white-space: normal; -} - - .roll-dialog .dialog-effects .wrapper{ - height: 100% -} - -.roll-dialog .effect-select { - margin: 10px; - width: calc(100% - 15px); - height: calc(100% - 50px) -} - -/* .wrath-and-glory.dialog.power-dialog, - .wrath-and-glory.dialog.weapon-dialog { - flex-direction: column; -} */ - -.wrath-and-glory.dialog .wrapper-left { - display: flex; - flex-direction: column; - flex-basis: calc(50% - 4px); - margin-right: 4px; - justify-content: center; - align-self: baseline; -} - -.wrath-and-glory.dialog .wrapper-right { - display: flex; - flex-direction: column; - flex-wrap: wrap; - flex-basis: calc(50% - 4px); - margin-left: 4px; - justify-content: center; - align-self: baseline; -} - -.wrath-and-glory.dialog .wrapper { - display: flex; - flex-direction: row; - margin-top: 8px; - text-align: center; -} - -.wrath-and-glory.dialog .wrapper .name { - flex-basis: 30%; -} - -.wrath-and-glory.dialog .wrapper .dice, - .wrath-and-glory.dialog .wrapper .rank { - width: 95px; -} - -.wrath-and-glory.dialog .wrapper .distance, -.wrath-and-glory.dialog .wrapper .bonus , - .wrath-and-glory.dialog .wrapper .base { - flex-basis: 15%; -} - -.wrath-and-glory.dialog .wrapper .full { - flex-basis: 70%; -} - - -.wrath-and-glory.dialog .wrapper .range { - flex: 0.5 -} -.wrath-and-glory.dialog .wrapper .die { - flex-basis: calc(70% / 6); + .dialog-content { + display: flex; + flex: 1; + } + + .dialog-fields { + flex: 0 0 300px; + } + + h1 { + margin: 0; + border-style: solid; + border-width: 0px 8px 8px 8px; + border-image: var(--image-title-border) 8 8 8 8 repeat repeat; + color: var(--color-red); + font-weight: bold; + font-size: 18px; + text-align: center; + padding: 0 30px; + } + + label { + text-align: center; + background-color: var(--color-black); + color: var(--color-white); + padding: 5px; + font-family: var(--font-text); + font-size: 14px; + border: 1px solid var(--color-black); + height: 31px; + flex: 0 0 5rem; + text-wrap: nowrap; + } + + input[type="text"], + input[type="number"], + select { + &:disabled { + opacity : 0.5; + } + background: var(--image-input) repeat; + border: 1px solid var(--color-black); + border-radius: 0; + border-left: 0; + box-shadow: inset 0 0 6px var(--color-dark-grey); + color: var(--color-black); + font-size: 14px; + font-family: var(--font-text); + text-align: center; + height: 31px; + } + + input[type="checkbox"] + { + &:before { + content: ""; + font-family: "Font Awesome 5 Free"; + font-weight: bold; + display: block; + height: 31px; + background: white; + border: 1px solid black; + cursor: pointer; + width: 31px; + box-sizing: border-box; + } + + &:checked{ + &::before { + content : "\f00c"; + background: var(--color-imperium); + text-align: center; + font-size: 24px; + color: white + } + } + height: 31px; + margin: 0px; + border-radius: 0px; + } + + .wrapper { + display : flex; + margin: 5px; + } + + + .ed-values { + display: flex; + flex-direction: column; + .dice-inputs { + display: flex; + } + } + + .input-labels { + display: flex; + justify-content: right; + text-align: center; + line-height: 1rem; + height: 1rem; + > * { + flex: 1; + } + label { + flex: 0 0 5rem; + padding: 0px; + } + } + + .dialog-modifiers + { + margin: 5px; + label { + width: 100%; + display: block; + } + flex: 1; + ul { + list-style: none; + margin: 0px; + li { + margin: 0px; + line-height: 1.5rem; + padding-left: 5px; + } + li.active { + background-color: var(--color-imperium); + color: #CCC; + } + } + + background: var(--image-input) repeat; + border: 1px solid var(--color-black); + border-radius: 0; + border-left: 0; + box-shadow: inset 0 0 6px var(--color-dark-grey); + color: var(--color-black); + font-size: 14px; + font-family: var(--font-text); + } + } \ No newline at end of file diff --git a/style/sheet/_actor.scss b/style/sheet/_actor.scss index ce51f70..6dba26d 100644 --- a/style/sheet/_actor.scss +++ b/style/sheet/_actor.scss @@ -45,6 +45,7 @@ flex-direction: row; flex-basis: calc(50% - 1px); margin-right: 1px; + align-items: center; } } @@ -57,6 +58,12 @@ input { flex-grow: 1; } + + a { + font-size: var(--font-size-20); + margin-left: -20px; + margin-right: 5px; + } } } @@ -86,4 +93,21 @@ } } + .sheet-effect-buttons { + justify-content: left; + display: flex; + align-self:flex-start; + button { + background-color: var(--color-imperium); + font-size: var(--font-size-12); + line-height: 16px; + border-radius: 0px; + height: 1rem; + padding: 0px 3px; + &:hover { + box-shadow: none; + } + } + } + } \ No newline at end of file diff --git a/style/sheet/_item.scss b/style/sheet/_item.scss index 10df2fe..f9637fc 100644 --- a/style/sheet/_item.scss +++ b/style/sheet/_item.scss @@ -448,14 +448,30 @@ justify-content: center; .wrath-and-glory.sheet.item .archetype .wrapper.wargear { flex-direction: column; -} -.wrath-and-glory.sheet.item .archetype .wrapper.wargear .wargear-list { - background: url(./asset/image/input.webp) repeat; - min-height: 200px; - box-shadow: inset 0 0 20px var(--color-dark-grey); - margin: 5px; - width:unset; + label { + margin: 0; + border-style: solid; + border-width: 0px 8px 8px 8px; + border-image: var(--image-title-border) 8 8 8 8 repeat repeat; + color: var(--color-red); + font-weight: bold; + font-size: 18px; + text-align: center; + padding: 0 30px; + background: none; + width: 100%; + display: block; + } + + .choice-text { + background: url(./asset/image/input.webp) repeat; + min-height: 200px; + box-shadow: inset 0 0 20px var(--color-dark-grey); + margin: 5px; + width: unset; + padding: 10px; + } } .wrath-and-glory.sheet.item .species .species-item-list, diff --git a/style/sheet/_threat.scss b/style/sheet/_threat.scss index c8600a8..78429c9 100644 --- a/style/sheet/_threat.scss +++ b/style/sheet/_threat.scss @@ -12,9 +12,15 @@ flex-basis: calc(20% - 1px); margin-left: 1px; - label { - min-width: 50px; - max-width: 50px; + label { + display: flex; + justify-content: center; + a { + text-wrap: nowrap; + text-align: center; + font-size: 14px; + margin:0px; + } } } } diff --git a/style/sheet/tab/_combat.scss b/style/sheet/tab/_combat.scss index 1271729..9e71fcb 100644 --- a/style/sheet/tab/_combat.scss +++ b/style/sheet/tab/_combat.scss @@ -6,7 +6,6 @@ .wrath-and-glory.sheet.actor .combat { display: flex; flex-direction: row; - flex-wrap: wrap; flex-basis: 100%; } diff --git a/style/sheet/tab/_effects.scss b/style/sheet/tab/_effects.scss index b69de4b..372e4df 100644 --- a/style/sheet/tab/_effects.scss +++ b/style/sheet/tab/_effects.scss @@ -22,7 +22,15 @@ display: flex; flex-direction: column; height: 90%; - overflow: auto + overflow: auto; + + .source { + display: flex; + justify-content: center; + i { + margin: 0px 5px; + } + } } .wrath-and-glory.sheet .tab .system-effects label{ diff --git a/system.json b/system.json index 27235b5..df2d8d0 100644 --- a/system.json +++ b/system.json @@ -3,11 +3,10 @@ "name" : "wrath-and-glory", "title": "Warhammer 40,000: Wrath & Glory", "description": "The official system for Warhammer 40,000 Roleplay: Wrath & Glory! Time is running out in the 41st Millennium. This accursed age needs heroes more than ever before. Will you answer the call?", - "version": "5.1.7", - "minimumCoreVersion" : 11, + "version": "6.0.0", "compatibility" : { - "minimum" : 11, - "verified" : 11, + "minimum" : 12, + "verified" : 12, "maximum" : 12 }, "templateVersion": 2, @@ -47,6 +46,17 @@ "extensions": ["css", "hbs", "json"] } }, + "relationships": { + "requires": [{ + "id": "warhammer-lib", + "type": "module", + "manifest": "https://raw.githubusercontent.com/moo-man/WarhammerLibrary-FVTT/1.4.0/module.json", + "compatibility": { + "minimum" : "1.4.0", + "verified": "1.4.0" + } + }] + }, "background" : "/modules/wng-core/assets/images/cover.webp", "gridDistance": 1, "gridUnits": "m", @@ -55,7 +65,7 @@ "url": "https://github.com/moo-man/WrathAndGlory-FoundryVTT", "socket": true, "manifest": "https://github.com/moo-man/WrathAndGlory-FoundryVTT/releases/latest/download/system.json", - "download": "https://github.com/moo-man/WrathAndGlory-FoundryVTT/releases/download/5.1.7/wrath-and-glory.zip", + "download": "https://github.com/moo-man/WrathAndGlory-FoundryVTT/releases/download/6.0.0/wrath-and-glory.zip", "license": "LICENSE.txt" } diff --git a/template.json b/template.json index 286ecc3..e77c041 100644 --- a/template.json +++ b/template.json @@ -440,7 +440,7 @@ "rank": "none" }, "otherDamage" : { - "mortalWounds" : "", + "mortal" : "", "wounds" : "", "shock" : "" } @@ -509,7 +509,7 @@ "rank": "none" }, "other" : { - "mortalWounds" : "", + "mortal" : "", "wounds" : "", "shock" : "" } @@ -619,15 +619,8 @@ "id" : "" }, "wargear" : [ - [{ - "name" : "", - "id" : "" - }] ], "groups" : { - "type": "and", - "groupId" : "root", - "items" : [] }, "suggested" : { "attributes" : { @@ -728,5 +721,12 @@ }, "objectives" : [] } + }, + "ChatMessage" : { + "types" : [ + "base", + "test", + "damage" + ] } }