MrVauxs / pf2e-graphics

@vauxs
https://mrvauxs.net
GNU General Public License v3.0
13 stars 11 forks source link

Full Kineticist Elemental Blast support #45

Closed ThiefMaster closed 4 weeks ago

ThiefMaster commented 4 weeks ago

Not sure if this within the scope of this module or not, also because of how complex it is when done properly. But in case it is, I thought having this issue to keep track of it might be useful:

ThiefMaster commented 4 weeks ago

FWIW, this is what I currently use with AA. It's based on different scripts that I found online and it's far from complete (I'm just adding stuff needed for the character I'm playing myself), but maybe a useful reference:

```js /* - Make a new macro and paste this in - Add a 'On Token' AA Animation named 'Elemental Blast' - Click Add Macro, set when to play to Macro only - Select the macro in the text field below that */ const sourceToken = args[0].token; const target = args[0].target?.token; const impulse = args[0].item; if (!sourceToken || !target || !impulse) { return; } const flags = args[0].flags.pf2e.context; const options = flags.options; const isTwoAction = options.includes('action:cost:2'); const isWeaponInfusion = options.includes('self:effect:weapon-infusion'); const roll = args[0].rolls.find((r) => r.options.action === 'elemental-blast'); const data = roll?.options.identifier; if (!data) { return; } const isMiss = roll.degreeOfSuccess < 2; const isHit = roll.degreeOfSuccess >= 2; const isCrit = roll.degreeOfSuccess >= 3; const [element, damageType, meleeOrRanged, actionCost] = data.split('.'); const isMelee = meleeOrRanged === 'melee'; let projectile = null; let onSource = null; let onTarget = null; if (isWeaponInfusion) { weaponInfusionAnim(damageType, options, isMelee, sourceToken, target, isMiss).play(); return; } const TABLE = { fire: { default: isTwoAction ? 'jb2a.energy_strands.range.multiple.orange' : 'jb2a.energy_strands.range.standard.orange', cold: isTwoAction ? 'jb2a.energy_strands.range.multiple.blue' : 'jb2a.energy_strands.range.standard.blue', }, fireMelee: { default: 'jb2a.scorching_ray.orange', cold: 'jb2a.scorching_ray.blue', }, water: { default: isTwoAction ? 'jb2a.energy_strands.range.multiple.grey' : 'jb2a.energy_strands.range.standard.grey', cold: isTwoAction ? 'jb2a.energy_strands.range.multiple.blue' : 'jb2a.energy_strands.range.standard.blue', acid: isTwoAction ? 'jb2a.energy_strands.range.multiple.dark_green.01' : 'jb2a.energy_strands.range.standard.dark_green', }, waterMelee: { default: { projectile: 'jb2a.scorching_ray.purple.02', projectileFilter: ['ColorMatrix', {hue: 150, saturate: 0, brightness: 1}], }, cold: 'jb2a.scorching_ray.blue.02', acid: 'jb2a.scorching_ray.green.02', } // air: { // default: 'jb2a.eldritch_blast.lightblue', // }, // earth: { // poison: 'jb2a.spell_projectile.poison.greenyellow', // default: 'jb2a.boulder.siege.02', // }, // metal: { // default: { // projectile: 'jb2a.energy_strands.range.standard.grey.03', // //target: 'jb2a.explosion.shrapnel.bomb.01.grey', // target: 'jb2a.ice_spikes.radial.burst.grey', // }, // electricity: 'jb2a.chain_lightning.primary.blue', // }, // wood: { // default: 'jb2a.eldritch_blast.lightgreen', // positive: { // projectile: 'jb2a.bullet.02.green', // target: 'jb2a.divine_smite.caster.greenorange', // }, // poison: 'jb2a.spell_projectile.poison.greenyellow', // }, }; const xElement = isMelee ? `${element}Melee` : element; let anim = TABLE[xElement][damageType] ?? TABLE[element][damageType] ?? TABLE[xElement]['default']; if (!anim) { console.error('No animation for elemental blast ' + data); return; } if (typeof anim === 'string') { anim = { projectile: anim, }; } const seq = new Sequence({ inModuleName: 'PF2e Animations', softFail: true, }); seq .effect() .atLocation(sourceToken) .file(anim.source) .scaleToObject() .playIf(!!anim.source) .waitUntilFinished(-350) .effect() .atLocation(sourceToken) .stretchTo(target) .missed(isMiss) .file(anim.projectile) [anim.projectileFilter ? 'filter' : 'rotate'](...(anim.projectileFilter || [0])) .playIf(!!anim.projectile) .waitUntilFinished(-350) .effect() .atLocation(target) .file(anim.target) .scaleToObject(2) .playIf(!!anim.target && !isMiss) .play(); function weaponInfusionAnim(damageType, options, isMelee, source, target, isMiss) { const seq = new Sequence(); if (isMelee) { seq.effect().atLocation(source).stretchTo(target).missed(isMiss); if (options.includes('weapon-infusion:melee:agile')) { switch (damageType) { case 'fire': seq.file('jb2a.dagger.melee.fire.orange'); break; case 'cold': seq.file('jb2a.dagger.melee.fire.blue'); break; case 'acid': seq.file('jb2a.dagger.melee.fire.green'); break; default: // physical seq .file('jb2a.dagger.melee.fire.pink') .filter('ColorMatrix', { saturate: -1 }); break; } } else if (options.includes('weapon-infusion:melee:backswing')) { switch (damageType) { case 'fire': seq.file('jb2a.glaive.melee.02.orange'); break; case 'cold': seq.file('jb2a.glaive.melee.02.blue'); break; case 'acid': seq.file('jb2a.glaive.melee.02.white').tint('#19ff30'); // XXX missing green version break; default: // physical seq .file('jb2a.glaive.melee.02.blue') .filter('ColorMatrix', { saturate: -1 }); break; } } else if (options.includes('weapon-infusion:melee:forceful')) { switch (damageType) { case 'fire': seq.file('jb2a.glaive.melee.01.orange'); break; case 'cold': seq.file('jb2a.glaive.melee.01.blue'); break; case 'acid': seq.file('jb2a.glaive.melee.01.white').tint('#19ff30'); // XXX missing green version break; default: // physical seq .file('jb2a.glaive.melee.01.blue') .filter('ColorMatrix', { saturate: -1 }); break; } } else if (options.includes('weapon-infusion:melee:reach')) { switch (damageType) { case 'fire': seq.file('jb2a.spear.melee.fire.orange'); break; case 'cold': seq.file('jb2a.spear.melee.fire.blue'); break; case 'acid': seq.file('jb2a.spear.melee.fire.green'); break; default: // physical seq .file('jb2a.spear.melee.fire.pink') .filter('ColorMatrix', { saturate: -1 }); break; } } else if (options.includes('weapon-infusion:melee:sweep')) { switch (damageType) { case 'fire': seq.file('jb2a.greataxe.melee.fire.orange'); break; case 'cold': seq.file('jb2a.greataxe.melee.fire.blue'); break; case 'acid': seq.file('jb2a.greataxe.melee.fire.green'); break; default: // physical seq.file('jb2a.greataxe.melee.fire.white') break; } } } else { seq.effect().atLocation(source).stretchTo(target).missed(isMiss); if (options.includes('weapon-infusion:ranged:volley-30')) { switch (damageType) { case 'fire': seq.file('jb2a.arrow.fire.orange'); break; case 'cold': seq.file('jb2a.arrow.cold.blue'); break; case 'acid': seq.file('jb2a.arrow.poison.green'); break; default: // physical seq.file('jb2a.arrow.physical.white') break; } } else if (options.includes('weapon-infusion:ranged:propulsive')) { switch (damageType) { case 'fire': seq.file('jb2a.bolt.fire.orange'); break; case 'cold': seq.file('jb2a.bolt.cold.blue'); break; case 'acid': seq.file('jb2a.bolt.poison.green'); break; default: seq.file('jb2a.bolt.physical.white02') break; } } else if (options.includes('weapon-infusion:ranged:thrown')) { switch (damageType) { case 'fire': seq.file('jb2a.dagger.throw.01.red').tint('#ffb940'); break; case 'cold': seq.file('jb2a.dagger.throw.01.blue'); break; case 'acid': seq.file('jb2a.dagger.throw.01.white').tint('#5eff6e'); break; default: seq.file('jb2a.dagger.throw.01.white'); break; } } } return seq; } ```
MrVauxs commented 4 weeks ago

Every single one of these points is already possible through predicates.