forked from GitHub-Mirrors/foundry-sw5e

DND5e Core 1.3.5 modded to SW5e System Combining with DND5e Core 1.3.2 to see one big commit since last core update DND5e Core 1.3.2 modded to SW5e System
133 lines
4.4 KiB
JavaScript
133 lines
4.4 KiB
JavaScript
import { SW5E } from "../config.js";
|
|
|
|
/**
|
|
* A helper class for building MeasuredTemplates for 5e powers and abilities
|
|
* @extends {MeasuredTemplate}
|
|
*/
|
|
export default class AbilityTemplate extends MeasuredTemplate {
|
|
|
|
/**
|
|
* A factory method to create an AbilityTemplate instance using provided data from an Item5e instance
|
|
* @param {Item5e} item The Item object for which to construct the template
|
|
* @return {AbilityTemplate|null} The template object, or null if the item does not produce a template
|
|
*/
|
|
static fromItem(item) {
|
|
const target = getProperty(item.data, "data.target") || {};
|
|
const templateShape = SW5E.areaTargetTypes[target.type];
|
|
if ( !templateShape ) return null;
|
|
|
|
// Prepare template data
|
|
const templateData = {
|
|
t: templateShape,
|
|
user: game.user._id,
|
|
distance: target.value,
|
|
direction: 0,
|
|
x: 0,
|
|
y: 0,
|
|
fillColor: game.user.color
|
|
};
|
|
|
|
// Additional type-specific data
|
|
switch ( templateShape ) {
|
|
case "cone":
|
|
templateData.angle = CONFIG.MeasuredTemplate.defaults.angle;
|
|
break;
|
|
case "rect": // 5e rectangular AoEs are always cubes
|
|
templateData.distance = Math.hypot(target.value, target.value);
|
|
templateData.width = target.value;
|
|
templateData.direction = 45;
|
|
break;
|
|
case "ray": // 5e rays are most commonly 1 square (5 ft) in width
|
|
templateData.width = target.width ?? canvas.dimensions.distance;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Return the template constructed from the item data
|
|
const cls = CONFIG.MeasuredTemplate.documentClass;
|
|
const template = new cls(templateData, {parent: canvas.scene});
|
|
const object = new this(template);
|
|
object.item = item;
|
|
object.actorSheet = item.actor?.sheet || null;
|
|
return object;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Creates a preview of the power template
|
|
*/
|
|
drawPreview() {
|
|
const initialLayer = canvas.activeLayer;
|
|
|
|
// Draw the template and switch to the template layer
|
|
this.draw();
|
|
this.layer.activate();
|
|
this.layer.preview.addChild(this);
|
|
|
|
// Hide the sheet that originated the preview
|
|
if ( this.actorSheet ) this.actorSheet.minimize();
|
|
|
|
// Activate interactivity
|
|
this.activatePreviewListeners(initialLayer);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Activate listeners for the template preview
|
|
* @param {CanvasLayer} initialLayer The initially active CanvasLayer to re-activate after the workflow is complete
|
|
*/
|
|
activatePreviewListeners(initialLayer) {
|
|
const handlers = {};
|
|
let moveTime = 0;
|
|
|
|
// Update placement (mouse-move)
|
|
handlers.mm = event => {
|
|
event.stopPropagation();
|
|
let now = Date.now(); // Apply a 20ms throttle
|
|
if ( now - moveTime <= 20 ) return;
|
|
const center = event.data.getLocalPosition(this.layer);
|
|
const snapped = canvas.grid.getSnappedPosition(center.x, center.y, 2);
|
|
this.data.update({x: snapped.x, y: snapped.y});
|
|
this.refresh();
|
|
moveTime = now;
|
|
};
|
|
|
|
// Cancel the workflow (right-click)
|
|
handlers.rc = event => {
|
|
this.layer.preview.removeChildren();
|
|
canvas.stage.off("mousemove", handlers.mm);
|
|
canvas.stage.off("mousedown", handlers.lc);
|
|
canvas.app.view.oncontextmenu = null;
|
|
canvas.app.view.onwheel = null;
|
|
initialLayer.activate();
|
|
this.actorSheet.maximize();
|
|
};
|
|
|
|
// Confirm the workflow (left-click)
|
|
handlers.lc = event => {
|
|
handlers.rc(event);
|
|
const destination = canvas.grid.getSnappedPosition(this.data.x, this.data.y, 2);
|
|
this.data.update(destination);
|
|
canvas.scene.createEmbeddedDocuments("MeasuredTemplate", [this.data]);
|
|
};
|
|
|
|
// Rotate the template by 3 degree increments (mouse-wheel)
|
|
handlers.mw = event => {
|
|
if ( event.ctrlKey ) event.preventDefault(); // Avoid zooming the browser window
|
|
event.stopPropagation();
|
|
let delta = canvas.grid.type > CONST.GRID_TYPES.SQUARE ? 30 : 15;
|
|
let snap = event.shiftKey ? delta : 5;
|
|
this.data.update({direction: this.data.direction + (snap * Math.sign(event.deltaY))});
|
|
this.refresh();
|
|
};
|
|
|
|
// Activate listeners
|
|
canvas.stage.on("mousemove", handlers.mm);
|
|
canvas.stage.on("mousedown", handlers.lc);
|
|
canvas.app.view.oncontextmenu = handlers.rc;
|
|
canvas.app.view.onwheel = handlers.mw;
|
|
}
|
|
}
|