Split Force and Tech Power Limits

Split force and tech power limits.  Didn't test, may be broken.

Needs separate tab for force vs tech.  Still need to tally number of powers known
This commit is contained in:
supervj 2021-02-10 15:41:02 -05:00
parent fa5dc07869
commit fb73cdfc08
5 changed files with 154 additions and 77 deletions

View file

@ -506,7 +506,6 @@ export default class Actor5e extends Actor {
} }
// Look up the number of slots per level from the powerLimit table // Look up the number of slots per level from the powerLimit table
// TODO: Add Tech progression afterwards
let forcePowerLimit = SW5E.powerLimit['none']; let forcePowerLimit = SW5E.powerLimit['none'];
for (let i = 0; i < (forceProgression.maxClassPowerLevel); i++) { for (let i = 0; i < (forceProgression.maxClassPowerLevel); i++) {
forcePowerLimit[i] = SW5E.powerLimit[forceProgression.maxClass][i]; forcePowerLimit[i] = SW5E.powerLimit[forceProgression.maxClass][i];
@ -515,9 +514,22 @@ export default class Actor5e extends Actor {
for ( let [n, lvl] of Object.entries(powers) ) { for ( let [n, lvl] of Object.entries(powers) ) {
let i = parseInt(n.slice(-1)); let i = parseInt(n.slice(-1));
if ( Number.isNaN(i) ) continue; if ( Number.isNaN(i) ) continue;
if ( Number.isNumeric(lvl.override) ) lvl.max = Math.max(parseInt(lvl.override), 0); if ( Number.isNumeric(lvl.foverride) ) lvl.fmax = Math.max(parseInt(lvl.foverride), 0);
else lvl.max = forcePowerLimit[i-1] || 0; else lvl.fmax = forcePowerLimit[i-1] || 0;
lvl.value = parseInt(lvl.value); lvl.fvalue = parseInt(lvl.fvalue);
}
let techPowerLimit = SW5E.powerLimit['none'];
for (let i = 0; i < (techProgression.maxClassPowerLevel); i++) {
techPowerLimit[i] = SW5E.powerLimit[techProgression.maxClass][i];
}
for ( let [n, lvl] of Object.entries(powers) ) {
let i = parseInt(n.slice(-1));
if ( Number.isNaN(i) ) continue;
if ( Number.isNumeric(lvl.toverride) ) lvl.tmax = Math.max(parseInt(lvl.toverride), 0);
else lvl.tmax = techPowerLimit[i-1] || 0;
lvl.tvalue = parseInt(lvl.tvalue);
} }
// Set Force and tech power // Set Force and tech power

View file

@ -101,33 +101,61 @@ export default class AbilityUseDialog extends Dialog {
// Determine the levels which are feasible // Determine the levels which are feasible
let lmax = 0; let lmax = 0;
let points; let points;
let powerType;
switch (itemData.school){ switch (itemData.school){
case "lgt": case "lgt":
case "uni": case "uni":
case "drk": { case "drk": {
powerType = "force"
points = actorData.attributes.force.points.value + actorData.attributes.force.points.temp; points = actorData.attributes.force.points.value + actorData.attributes.force.points.temp;
break; break;
} }
case "tec": { case "tec": {
powerType = "tech"
points = actorData.attributes.tech.points.value + actorData.attributes.tech.points.temp; points = actorData.attributes.tech.points.value + actorData.attributes.tech.points.temp;
break; break;
} }
} }
const powerLevels = Array.fromRange(10).reduce((arr, i) => { let powerLevels
if ( i < lvl ) return arr; if (powerType === "force"){
const label = CONFIG.SW5E.powerLevels[i]; powerLevels = Array.fromRange(10).reduce((arr, i) => {
const l = actorData.powers["power"+i] || {max: 0, override: null}; if ( i < lvl ) return arr;
let max = parseInt(l.override || l.max || 0); const label = CONFIG.SW5E.powerLevels[i];
let slots = Math.clamped(parseInt(l.value || 0), 0, max); const l = actorData.powers["power"+i] || {fmax: 0, foverride: null};
if ( max > 0 ) lmax = i; let max = parseInt(l.foverride || l.fmax || 0);
arr.push({ let slots = Math.clamped(parseInt(l.fvalue || 0), 0, max);
level: i, if ( max > 0 ) lmax = i;
label: i > 0 ? game.i18n.format('SW5E.PowerLevelSlot', {level: label, n: slots}) : label, if ((max > 0) && (slots > 0) && (points > i)){
canCast: max > 0, arr.push({
hasSlots: ((slots > 0) && (points > i)) level: i,
}); label: i > 0 ? game.i18n.format('SW5E.PowerLevelSlot', {level: label, n: slots}) : label,
return arr; canCast: max > 0,
}, []).filter(sl => sl.level <= lmax); hasSlots: slots > 0
});
}
return arr;
}, []).filter(sl => sl.level <= lmax);
}else if (powerType === "tech"){
powerLevels = Array.fromRange(10).reduce((arr, i) => {
if ( i < lvl ) return arr;
const label = CONFIG.SW5E.powerLevels[i];
const l = actorData.powers["power"+i] || {tmax: 0, toverride: null};
let max = parseInt(l.override || l.tmax || 0);
let slots = Math.clamped(parseInt(l.tvalue || 0), 0, max);
if ( max > 0 ) lmax = i;
if ((max > 0) && (slots > 0) && (points > i)){
arr.push({
level: i,
label: i > 0 ? game.i18n.format('SW5E.PowerLevelSlot', {level: label, n: slots}) : label,
canCast: max > 0,
hasSlots: slots > 0
});
}
return arr;
}, []).filter(sl => sl.level <= lmax);
}
const canCast = powerLevels.some(l => l.hasSlots); const canCast = powerLevels.some(l => l.hasSlots);
if ( !canCast ) data.errors.push(game.i18n.format("SW5E.PowerCastNoSlots", { if ( !canCast ) data.errors.push(game.i18n.format("SW5E.PowerCastNoSlots", {

View file

@ -431,12 +431,12 @@ export default class Item5e extends Item {
let consumeUsage = !!uses.per; // Consume limited uses let consumeUsage = !!uses.per; // Consume limited uses
let consumeQuantity = uses.autoDestroy; // Consume quantity of the item in lieu of uses let consumeQuantity = uses.autoDestroy; // Consume quantity of the item in lieu of uses
//set rollback values in case you pick a spell level you can't cast anymore /*//set rollback values in case you pick a spell level you can't cast anymore
let forcePointValueRollback = actor.data.data.attributes.force.points.value; let forcePointValueRollback = actor.data.data.attributes.force.points.value;
let forcePointTempRollback = actor.data.data.attributes.force.points.temp; let forcePointTempRollback = actor.data.data.attributes.force.points.temp;
let techPointValueRollback = actor.data.data.attributes.tech.points.value; let techPointValueRollback = actor.data.data.attributes.tech.points.value;
let techPointTempRollback = actor.data.data.attributes.tech.points.temp; let techPointTempRollback = actor.data.data.attributes.tech.points.temp;
*/
// Display a configuration dialog to customize the usage // Display a configuration dialog to customize the usage
const needsConfiguration = createMeasuredTemplate || consumeRecharge || consumeResource || consumePowerSlot || consumeUsage; const needsConfiguration = createMeasuredTemplate || consumeRecharge || consumeResource || consumePowerSlot || consumeUsage;
if (configureDialog && needsConfiguration) { if (configureDialog && needsConfiguration) {
@ -455,31 +455,7 @@ export default class Item5e extends Item {
if ( requirePowerSlot ) { if ( requirePowerSlot ) {
const slotLevel = configuration.level; const slotLevel = configuration.level;
const powerLevel = parseInt(slotLevel); const powerLevel = parseInt(slotLevel);
const fp = actor.data.data.attributes.force.points;
const tp = actor.data.data.attributes.tech.points;
const powerCost = powerLevel + 1;
switch (id.school){
case "lgt":
case "uni":
case "drk": {
if (fp.temp >= powerCost) {
actor.update({"data.attributes.force.points.temp": fp.temp - powerCost});
}else{
actor.update({"data.attributes.force.points.value": fp.value + fp.temp - powerCost});
actor.update({"data.attributes.force.points.temp": 0});
}
break;
}
case "tec": {
if (tp.temp >= powerCost) {
actor.update({"data.attributes.tech.points.temp": tp.temp - powerCost});
}else{
actor.update({"data.attributes.tech.points.value": tp.value + tp.temp - powerCost});
actor.update({"data.attributes.tech.points.temp": 0});
break;
}
}
}
if (powerLevel !== id.level) { if (powerLevel !== id.level) {
const upcastData = mergeObject(this.data, {"data.level": powerLevel}, {inplace: false}); const upcastData = mergeObject(this.data, {"data.level": powerLevel}, {inplace: false});
item = this.constructor.createOwned(upcastData, actor); // Replace the item with an upcast version item = this.constructor.createOwned(upcastData, actor); // Replace the item with an upcast version
@ -490,13 +466,8 @@ export default class Item5e extends Item {
// Determine whether the item can be used by testing for resource consumption // Determine whether the item can be used by testing for resource consumption
const usage = item._getUsageUpdates({consumeRecharge, consumeResource, consumePowerSlot, consumeUsage, consumeQuantity}); const usage = item._getUsageUpdates({consumeRecharge, consumeResource, consumePowerSlot, consumeUsage, consumeQuantity});
if ( !usage ) { if ( !usage ) return;
actor.update({"data.attributes.force.points.value": forcePointValueRollback});
actor.update({"data.attributes.force.points.temp": forcePointTempRollback});
actor.update({"data.attributes.tech.points.value": techPointValueRollback});
actor.update({"data.attributes.tech.points.temp": techPointTempRollback});
return;
}
const {actorUpdates, itemUpdates, resourceUpdates} = usage; const {actorUpdates, itemUpdates, resourceUpdates} = usage;
// Commit pending data updates // Commit pending data updates
@ -558,14 +529,47 @@ export default class Item5e extends Item {
// Consume Power Slots // Consume Power Slots
if ( consumePowerSlot ) { if ( consumePowerSlot ) {
const level = this.actor?.data.data.powers[consumePowerSlot]; const level = this.actor?.data.data.powers[consumePowerSlot];
const powers = Number(level?.value ?? 0); const fp = this.actor.data.data.attributes.force.points;
if ( powers === 0 ) { const tp = this.actor.data.data.attributes.tech.points;
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`); const powerCost = level + 1;
ui.notifications.warn(game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label})); switch (id.school){
return false; case "lgt":
case "uni":
case "drk": {
const powers = Number(level?.fvalue ?? 0);
if ( powers === 0 ) {
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`);
ui.notifications.warn(game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label}));
return false;
}
actorUpdates[`data.powers.${consumePowerSlot}.fvalue`] = Math.max(powers - 1, 0);
if (fp.temp >= powerCost) {
actorUpdates["data.attributes.force.points.temp"] = fp.temp - powerCost;
}else{
actorUpdates["data.attributes.force.points.value"] = fp.value + fp.temp - powerCost;
actorUpdates["data.attributes.force.points.temp"] = 0;
}
break;
}
case "tec": {
const powers = Number(level?.tvalue ?? 0);
if ( powers === 0 ) {
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`);
ui.notifications.warn(game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label}));
return false;
}
actorUpdates[`data.powers.${consumePowerSlot}.tvalue`] = Math.max(powers - 1, 0);
if (tp.temp >= powerCost) {
actorUpdates["data.attributes.force.points.temp"] = tp.temp - powerCost;
}else{
actorUpdates["data.attributes.force.points.value"] = tp.value + tp.temp - powerCost;
actorUpdates["data.attributes.force.points.temp"] = 0;
}
break;
}
} }
actorUpdates[`data.powers.${consumePowerSlot}.value`] = Math.max(powers - 1, 0);
} }
// Consume Limited Usage // Consume Limited Usage
if ( consumeUsage ) { if ( consumeUsage ) {

View file

@ -286,6 +286,21 @@ function _migrateActorPowers(actorData, updateData) {
updateData["data.attributes.tech.points.tempmax"] = 0; updateData["data.attributes.tech.points.tempmax"] = 0;
updateData["data.attributes.tech.level"] = 0; updateData["data.attributes.tech.level"] = 0;
} }
// If new Power F/T split data is not present, create it
const hasNewBonus = ad?.powers?.power1?.fvalue !== undefined;
if ( !hasNewBonus ) {
for (let i = 1; i <= 9; i++) {
// add new
updateData["data.powers.power" + i + ".fvalue"] = 0;
updateData["data.powers.power" + i + ".foverride"] = null;
updateData["data.powers.power" + i + ".tvalue"] = 0;
updateData["data.powers.power" + i + ".toverride"] = null;
//remove old
updateData["data.powers.power" + i + ".-=value"] = null;
updateData["data.powers.power" + i + ".-=override"] = null;
}
}
// If new Bonus Power DC data is not present, create it // If new Bonus Power DC data is not present, create it
const hasNewBonus = ad?.bonuses?.power?.forceLightDC !== undefined; const hasNewBonus = ad?.bonuses?.power?.forceLightDC !== undefined;
if ( !hasNewBonus ) { if ( !hasNewBonus ) {

View file

@ -211,40 +211,58 @@
}, },
"powers": { "powers": {
"power1": { "power1": {
"value": 0, "fvalue": 0,
"override": null "foverride": null,
"tvalue": 0,
"toverride": null
}, },
"power2": { "power2": {
"value": 0, "fvalue": 0,
"override": null "foverride": null,
"tvalue": 0,
"toverride": null
}, },
"power3": { "power3": {
"value": 0, "fvalue": 0,
"override": null "foverride": null,
"tvalue": 0,
"toverride": null
}, },
"power4": { "power4": {
"value": 0, "fvalue": 0,
"override": null "foverride": null,
"tvalue": 0,
"toverride": null
}, },
"power5": { "power5": {
"value": 0, "fvalue": 0,
"override": null "foverride": null,
"tvalue": 0,
"toverride": null
}, },
"power6": { "power6": {
"value": 0, "fvalue": 0,
"override": null "foverride": null,
"tvalue": 0,
"toverride": null
}, },
"power7": { "power7": {
"value": 0, "fvalue": 0,
"override": null "foverride": null,
"tvalue": 0,
"toverride": null
}, },
"power8": { "power8": {
"value": 0, "fvalue": 0,
"override": null "foverride": null,
"tvalue": 0,
"toverride": null
}, },
"power9": { "power9": {
"value": 0, "fvalue": 0,
"override": null "foverride": null,
"tvalue": 0,
"toverride": null
} }
}, },
"bonuses": { "bonuses": {