diff --git a/README.md b/README.md index 31126df..499e245 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,15 @@ You can also set blocks of Terrain to be shown or not shown, so if the difficult For those who are developing Rulers based on the Enhanced Terrain Layer, to get access to the difficulty cost of terrain grid you call the cost function. `canvas.terrrain.cost(pts, options);` pts can be a single object {x: 0, y:0}, or an array of point objects. -options {elevation: 0, ignore:[]} lets the terrain layer know certain things about what you're asking for. -Passing in 'elevation' for the tokens elevation will ignore any difficult terrain caused by other tokens if the elevation is not the same. -Passing in an array of environments to ignore will cause the Terrain Layer to ignore any difficult terrain that has that environment label. +options {elevation: 0, reduce:[], tokenId: token.id, token:token} lets the terrain layer know certain things about what you're asking for. + +- elevation: adding a value for elevation will ignore all terrain that is a ground type if the elevation is greater than 0 and ignore any air terrain if the elevation is less than or equal to 0. It will also ignore any tokens that aren't at the same elevation. +- reduce: [{id:'arctic',value:1}] will result in any calculation essentially ignoring arctic terrain. [{id:'arctic',value:'-1',stop:1}] will result in any calculation reducing the difficulty by 1 and stopping at 1. You can also use '+1' to add to the difficulty. stop is an optional parameter. And you can use the id 'token' to have these settings applied when calculating cost through another token's space. +- tokenId - pass in the token id to avoid having the result use the token's own space as difficult terrain. +- token - pass in the token, will use both the id and elevation of that token. passing in elevation:false will result in the the function ignoring the token's elevation. +- calculate - this is how you'd like the cost to be calculated. default is 'maximum', which returns the highest value found while looking through all terrains. you can also passin 'additive' if you want all costs to be added together. + +if you'd like all the details of how the cost was acheived, call costDetails(pts, options). It will return the cost, and an array of details that combined to make the cost. A list of Terrain Environments can be found by calling canvas.terrain.environment(); and can be overridden if the environments in your game differ. diff --git a/classes/terrainlayer.js b/classes/terrainlayer.js index 5193bff..76bc65c 100644 --- a/classes/terrainlayer.js +++ b/classes/terrainlayer.js @@ -77,14 +77,45 @@ export class TerrainLayer extends PlaceablesLayer { } cost(pts, options = {}) { + let details = this.costDetails(pts, options); + return details.cost; + } + + costDetails(pts, options = {}) { + let reduceFn = function (cost, reduce) { + let value = parseFloat(reduce.value); + + if (typeof reduce.value == 'string' && (reduce.value.startsWith('+') || reduce.value.startsWith('-'))) { + value = cost + value; + if (reduce.stop) { + if (reduce.value.startsWith('+')) + value = Math.min(value, reduce.stop); + else + value = Math.max(value, reduce.stop); + } + } + + return Math.max(value, 0); + } + + let details = []; let total = 0; pts = pts instanceof Array ? pts : [pts]; const hx = canvas.grid.w / 2; const hy = canvas.grid.h / 2; + let calculate = options.calculate || 'maximum'; + let calculateFn = function (cost, total) { return cost; }; + switch (calculate) { + case 'maximum': + calculateFn = function (cost, total) { return Math.max(cost, total); }; break; + case 'additive': + calculateFn = function (cost, total) { return cost + total; }; break; + } + for (let pt of pts) { - let cost = 0; + let cost = null; let [gx, gy] = canvas.grid.grid.getPixelsFromGridPosition(pt.y, pt.x); let elevation = (options.elevation === false ? null : (options.elevation != undefined ? options.elevation : options?.token?.data?.elevation)); @@ -98,8 +129,20 @@ export class TerrainLayer extends PlaceablesLayer { !options.ignore?.includes(terrain.environment) && !((terrain.data.terraintype == 'ground' && elevation > 0) || (terrain.data.terraintype == 'air' && elevation <= 0)) && terrain.shape.contains(testX, testY)) { - cost = Math.max(terrain.cost(options), cost); - if (cost > 1 && options.reduce?.includes(terrain.environment)) {cost -= 1}; + let detail = {object:terrain}; + let terraincost = terrain.cost(options); + detail.cost = terraincost; + + let reduce = options.reduce?.find(e => e.id == terrain.environment); + if (reduce) { + detail.reduce = reduce; + terraincost = reduceFn(terraincost, reduce); + } + cost = calculateFn(terraincost, cost); + detail.total = cost; + + details.push(detail); + } } @@ -107,15 +150,26 @@ export class TerrainLayer extends PlaceablesLayer { for (let measure of canvas.templates.placeables) { const testX = (gx + hx) - measure.data.x; const testY = (gy + hy) - measure.data.y; - let measMult = measure.getFlag('enhanced-terrain-layer', 'multiple'); + let terraincost = measure.getFlag('enhanced-terrain-layer', 'multiple'); let measType = measure.getFlag('enhanced-terrain-layer', 'terraintype') || 'ground'; let measEnv = measure.getFlag('enhanced-terrain-layer', 'environment') || ''; - if (measMult && + if (terraincost && !options.ignore?.includes(measEnv) && !((measType == 'ground' && elevation > 0) || (measType == 'air' && elevation <= 0)) && measure.shape.contains(testX, testY)) { - cost = Math.max(measMult, cost); - if (cost > 1 && options.reduce?.includes(measEnv)) {cost -= 1}; + + let detail = { object: measure, cost: terraincost }; + let reduce = options.reduce?.find(e => e.id == measEnv); + if (reduce) { + detail.reduce = reduce; + terraincost = reduceFn(terraincost, reduce); + } + + cost = calculateFn(terraincost, cost); + detail.total = cost; + + details.push(detail); + } } @@ -126,16 +180,28 @@ export class TerrainLayer extends PlaceablesLayer { const testX = (gx + hx); const testY = (gy + hy); if (!(testX < token.data.x || testX > token.data.x + (token.data.width * canvas.grid.w) || testY < token.data.y || testY > token.data.y + (token.data.height * canvas.grid.h))) { - cost = Math.max(2, cost); + let terraincost = 2; + let detail = { object: token, cost: terraincost }; + + let reduce = options.reduce?.find(e => e.id == 'token'); + if (reduce) { + detail.reduce = reduce; + terraincost = reduceFn(terraincost, reduce); + } + + cost = calculateFn(terraincost, cost); + detail.total = cost; + + details.push(detail); } } } } - total += cost || 1; + total += (cost != undefined ? cost : 1); } - return total; + return { cost: total, details: details, calculate: calculate }; } terrainAt(x, y) {