Migrating UI for new data structures
This commit is contained in:
@@ -3,6 +3,7 @@ import adjustment from './applyPropertyByType/applyAdjustment.js';
|
||||
import branch from './applyPropertyByType/applyBranch.js';
|
||||
import buff from './applyPropertyByType/applyBuff.js';
|
||||
import damage from './applyPropertyByType/applyDamage.js';
|
||||
import note from './applyPropertyByType/applyNote.js';
|
||||
import roll from './applyPropertyByType/applyRoll.js';
|
||||
import savingThrow from './applyPropertyByType/applySavingThrow.js';
|
||||
import toggle from './applyPropertyByType/applyToggle.js';
|
||||
@@ -13,6 +14,7 @@ const applyPropertyByType = {
|
||||
branch,
|
||||
buff,
|
||||
damage,
|
||||
note,
|
||||
roll,
|
||||
savingThrow,
|
||||
spell: action,
|
||||
|
||||
@@ -9,15 +9,22 @@ import { damagePropertyWork } from '/imports/api/creature/creatureProperties/met
|
||||
export default function applyAction(node, {creature, targets, scope, log}){
|
||||
const prop = node.node;
|
||||
if (prop.target === 'self') targets = [creature];
|
||||
|
||||
// Log the name and description
|
||||
let content = { name: prop.name };
|
||||
if (prop.description?.text){
|
||||
recalculateInlineCalculations(prop.description, scope, log);
|
||||
content.value = prop.description.value;
|
||||
}
|
||||
if (content.name || content.value){
|
||||
log.content.push(content);
|
||||
}
|
||||
|
||||
// Spend the resources
|
||||
const failed = spendResources({prop, log, scope});
|
||||
if (failed) return;
|
||||
|
||||
let content = { name: prop.name };
|
||||
if (prop.summary?.text){
|
||||
recalculateInlineCalculations(prop.summary, scope, log);
|
||||
content.value = prop.summary.value;
|
||||
}
|
||||
log.content.push(content);
|
||||
// Attack if there is an attack roll
|
||||
if (prop.attackRoll && prop.attackRoll.calculation){
|
||||
if (targets.length){
|
||||
targets.forEach(target => {
|
||||
@@ -29,6 +36,8 @@ export default function applyAction(node, {creature, targets, scope, log}){
|
||||
applyAttackWithoutTarget({prop, scope, log});
|
||||
applyChildren(node, {creature, targets, scope, log});
|
||||
}
|
||||
} else {
|
||||
applyChildren(node, {creature, targets, scope, log});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,18 +48,18 @@ function applyAttackWithoutTarget({prop, scope, log}){
|
||||
delete scope['$criticalMiss'];
|
||||
delete scope['$attackRoll'];
|
||||
|
||||
recalculateCalculation(prop.rollBonus, scope, log);
|
||||
recalculateCalculation(prop.attackRoll, scope, log);
|
||||
|
||||
let value = rollDice(1, 20)[0];
|
||||
scope['$attackRoll'] = {value};
|
||||
let criticalHitTarget = scope.criticalHitTarget?.value || 20;
|
||||
let criticalHit = value >= criticalHitTarget;
|
||||
if (criticalHit) scope['$criticalHit'] = {value: true};
|
||||
let result = value + prop.rollBonus.value;
|
||||
let result = value + prop.attackRoll.value;
|
||||
scope['$toHit'] = {value: result};
|
||||
log.content.push({
|
||||
name: criticalHit ? 'Critical Hit!' : 'To Hit',
|
||||
value: `1d20 {${value}} + ${prop.rollBonus.value} = ` + result,
|
||||
value: `1d20 [${value}] + ${prop.attackRoll.value} = ` + result,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -62,7 +71,7 @@ function applyAttackToTarget({prop, target, scope, log}){
|
||||
delete scope['$attackDiceRoll'];
|
||||
delete scope['$attackRoll'];
|
||||
|
||||
recalculateCalculation(prop.rollBonus, scope, log);
|
||||
recalculateCalculation(prop.attackRoll, scope, log);
|
||||
|
||||
const value = rollDice(1, 20)[0];
|
||||
scope['$attackDiceRoll'] = {value};
|
||||
@@ -71,7 +80,7 @@ function applyAttackToTarget({prop, target, scope, log}){
|
||||
const criticalMiss = value === 1;
|
||||
if (criticalHit) scope['$criticalHit'] = {value: true};
|
||||
if (criticalMiss) scope['$criticalMiss'] = {value: true};
|
||||
const result = value + prop.rollBonus.value;
|
||||
const result = value + prop.attackRoll.value;
|
||||
scope['$attackRoll'] = {value: result};
|
||||
if (target.variables.armor){
|
||||
const armor = target.variables.armor.value;
|
||||
@@ -81,7 +90,7 @@ function applyAttackToTarget({prop, target, scope, log}){
|
||||
'Miss!'
|
||||
log.content.push({
|
||||
name,
|
||||
value: `1d20 {${value}} + ${prop.rollBonus.value} = ` + result,
|
||||
value: `1d20 {${value}} + ${prop.attackRoll.value} = ` + result,
|
||||
});
|
||||
if ((result > armor) || (criticalHit)){
|
||||
scope['$attackHit'] = true;
|
||||
@@ -95,7 +104,7 @@ function applyAttackToTarget({prop, target, scope, log}){
|
||||
});
|
||||
log.content.push({
|
||||
name: criticalHit ? 'Critical Hit!' : criticalMiss ? 'Critical miss!' : 'To Hit',
|
||||
value: `1d20 {${value}} + ${prop.rollBonus.value} = ` + result,
|
||||
value: `1d20 {${value}} + ${prop.attackRoll.value} = ` + result,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -106,7 +115,7 @@ function applyChildren(node, args){
|
||||
|
||||
function spendResources({prop, log, scope}){
|
||||
// Check Uses
|
||||
if (prop.usesUsed >= prop.uses?.value){
|
||||
if (prop.usesLeft < 0){
|
||||
log.content.push({
|
||||
name: 'Error',
|
||||
value: `${prop.name || 'action'} does not have enough uses left`,
|
||||
@@ -127,6 +136,7 @@ function spendResources({prop, log, scope}){
|
||||
let gainLog = [];
|
||||
try {
|
||||
prop.resources.itemsConsumed.forEach(itemConsumed => {
|
||||
recalculateCalculation(itemConsumed.quantity, scope, log);
|
||||
if (!itemConsumed.itemId){
|
||||
throw 'No ammo was selected for this prop';
|
||||
}
|
||||
@@ -166,7 +176,7 @@ function spendResources({prop, log, scope}){
|
||||
itemQuantityAdjustments.forEach(adjustQuantityWork);
|
||||
|
||||
// Use uses
|
||||
if (prop.usesResult){
|
||||
if (prop.usesLeft){
|
||||
CreatureProperties.update(prop._id, {
|
||||
$inc: {usesUsed: 1}
|
||||
}, {
|
||||
@@ -174,24 +184,29 @@ function spendResources({prop, log, scope}){
|
||||
});
|
||||
log.content.push({
|
||||
name: 'Uses left',
|
||||
value: prop.usesResult - (prop.usesUsed || 0) - 1,
|
||||
value: prop.usesLeft - (prop.usesUsed || 0) - 1,
|
||||
});
|
||||
}
|
||||
|
||||
// Damage stats
|
||||
prop.resources.attributesConsumed.forEach(attConsumed => {
|
||||
if (!attConsumed.quantity) return;
|
||||
recalculateCalculation(attConsumed.quantity, scope, log);
|
||||
|
||||
if (!attConsumed.quantity?.value) return;
|
||||
let stat = scope[attConsumed.variableName];
|
||||
if (!stat) return;
|
||||
if (!stat){
|
||||
spendLog.push(stat.name + ': ' + ' not found');
|
||||
return;
|
||||
}
|
||||
damagePropertyWork({
|
||||
property: stat,
|
||||
operation: 'increment',
|
||||
value: attConsumed.quantity,
|
||||
value: attConsumed.quantity.value,
|
||||
});
|
||||
if (attConsumed.quantity > 0){
|
||||
spendLog.push(stat.name + ': ' + attConsumed.quantity);
|
||||
} else if (attConsumed.quantity < 0){
|
||||
gainLog.push(stat.name + ': ' + -attConsumed.quantity);
|
||||
if (attConsumed.quantity.value > 0){
|
||||
spendLog.push(stat.name + ': ' + attConsumed.quantity.value);
|
||||
} else if (attConsumed.quantity.value < 0){
|
||||
gainLog.push(stat.name + ': ' + -attConsumed.quantity.value);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -14,12 +14,8 @@ export default function applyAdjustment(node, {
|
||||
|
||||
// Evaluate the amount
|
||||
recalculateCalculation(prop.amount, scope, log);
|
||||
prop.amount.errors?.forEach(error => {
|
||||
if (error.type !== 'info'){
|
||||
log.content.push({name: 'Error', value: error.message});
|
||||
}
|
||||
});
|
||||
const value = prop.amount.value;
|
||||
|
||||
const value = +prop.amount.value;
|
||||
if (!isFinite(value)) {
|
||||
return applyChildren(node, {creature, targets, scope, log});
|
||||
}
|
||||
@@ -32,12 +28,12 @@ export default function applyAdjustment(node, {
|
||||
name: 'Error',
|
||||
value: `Could not apply attribute damage, creature does not have \`${prop.stat}\` set`
|
||||
});
|
||||
return;
|
||||
return applyChildren(node, {creature, targets, scope, log});
|
||||
}
|
||||
damagePropertyWork({
|
||||
property: stat,
|
||||
operation: prop.operation,
|
||||
value,
|
||||
value: value,
|
||||
});
|
||||
log.content.push({
|
||||
name: 'Attribute damage',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import dealDamage from '/imports/api/creature/creatureProperties/methods/dealDamage.js';
|
||||
import { dealDamageWork } from '/imports/api/creature/creatureProperties/methods/dealDamage.js';
|
||||
import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import { Context } from '/imports/parser/resolve.js';
|
||||
import resolve, { Context, toString } from '/imports/parser/resolve.js';
|
||||
import logErrors from './shared/logErrors.js';
|
||||
|
||||
export default function applyDamage(node, {
|
||||
creature, targets, scope, log
|
||||
@@ -14,6 +14,12 @@ export default function applyDamage(node, {
|
||||
};
|
||||
|
||||
const prop = node.node;
|
||||
|
||||
// Skip if there is no parse node to work with
|
||||
if (!prop.amount.parseNode) return;
|
||||
|
||||
// Choose target
|
||||
|
||||
let damageTargets = prop.target === 'self' ? [creature] : targets;
|
||||
// Determine if the hit is critical
|
||||
let criticalHit = scope['$criticalHit']?.value &&
|
||||
@@ -23,40 +29,66 @@ export default function applyDamage(node, {
|
||||
let context = new Context({
|
||||
options: {doubleRolls: criticalHit},
|
||||
});
|
||||
recalculateCalculation(prop.amount, scope, log, context);
|
||||
|
||||
// If we didn't end up with a finite amount, give up
|
||||
if (!isFinite(prop.amount?.value)) return applyChildren();
|
||||
// Gather all the lines we need to log into an array
|
||||
const logValue = [];
|
||||
const logName = prop.damageType === 'healing' ? 'Healing' : 'Damage';
|
||||
|
||||
// Compile the dice roll and store that string first
|
||||
const {result: compiled} = resolve('compiled', prop.amount.parseNode, scope, context);
|
||||
logValue.push(toString(compiled));
|
||||
logErrors(context.errors, log);
|
||||
|
||||
// roll the dice only and store that string
|
||||
const {result: rolled} = resolve('roll', prop.amount.parseNode, scope, context);
|
||||
logValue.push(toString(rolled));
|
||||
logErrors(context.errors, log);
|
||||
|
||||
// Reset the errors so we don't log the same errors twice
|
||||
context.errors = [];
|
||||
|
||||
// Resolve the roll to a final value
|
||||
const {result: reduced} = resolve('reduce', rolled, scope, context);
|
||||
logErrors(context.errors, log);
|
||||
|
||||
// Store the result
|
||||
if (reduced.parseType === 'constant'){
|
||||
prop.amount.value = reduced.value;
|
||||
} else if (reduced.parseType === 'error'){
|
||||
prop.amount.value = null;
|
||||
} else {
|
||||
prop.amount.value = toString(reduced);
|
||||
}
|
||||
|
||||
const damage = +reduced.value;
|
||||
|
||||
// If we didn't end up with a constant of finite amount, give up
|
||||
if (reduced?.parseType !== 'constant' && !isFinite(reduced.value)){
|
||||
return applyChildren();
|
||||
}
|
||||
|
||||
// Memoise the damage suffix for the log
|
||||
let suffix = (criticalHit ? ' critical ' : ' ') +
|
||||
prop.damageType +
|
||||
(prop.damageType !== ' healing ' ? ' damage ': '');
|
||||
(prop.damageType !== 'healing' ? ' damage ': '');
|
||||
|
||||
if (damageTargets && damageTargets.length) {
|
||||
// Iterate through all the targets
|
||||
damageTargets.forEach(target => {
|
||||
let name = prop.damageType === 'healing' ? 'Healing' : 'Damage';
|
||||
|
||||
// Deal the damage to the target
|
||||
let damageDealt = dealDamage.call({
|
||||
creatureId: target._id,
|
||||
let damageDealt = dealDamageWork({
|
||||
creature: target,
|
||||
damageType: prop.damageType,
|
||||
amount: prop.amount.value,
|
||||
amount: damage,
|
||||
});
|
||||
|
||||
// Log the damage done
|
||||
if (target._id === creature._id){
|
||||
// Target is same as self, log damage as such
|
||||
log.content.push({
|
||||
name,
|
||||
value: damageDealt + suffix + ' to self',
|
||||
});
|
||||
logValue.push(damageDealt + suffix + ' to self');
|
||||
} else {
|
||||
log.content.push({
|
||||
name,
|
||||
value: 'Dealt ' + damageDealt + suffix + ` ${target.name && ' to '}${target.name}`,
|
||||
});
|
||||
logValue.push('Dealt ' + damageDealt + suffix + ` ${target.name && ' to '}${target.name}`);
|
||||
// Log the damage received on that creature's log as well
|
||||
insertCreatureLog.call({
|
||||
log: {
|
||||
@@ -71,10 +103,11 @@ export default function applyDamage(node, {
|
||||
});
|
||||
} else {
|
||||
// There are no targets, just log the result
|
||||
log.content.push({
|
||||
name: prop.damageType === 'healing' ? 'Healing' : 'Damage',
|
||||
value: prop.amount.value + suffix,
|
||||
});
|
||||
logValue.push(damage + suffix);
|
||||
}
|
||||
log.content.push({
|
||||
name: logName,
|
||||
value: logValue.join('\n'),
|
||||
});
|
||||
return applyChildren();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
|
||||
export default function applyNote(node, {creature, targets, scope, log}){
|
||||
const prop = node.node;
|
||||
|
||||
// Log Name, summary
|
||||
let content = { name: prop.name };
|
||||
if (prop.summary?.text){
|
||||
recalculateInlineCalculations(prop.summary, scope, log);
|
||||
content.value = prop.summary.value;
|
||||
}
|
||||
if (content.name || content.value){
|
||||
log.content.push(content);
|
||||
}
|
||||
// Log description
|
||||
if (prop.description?.text){
|
||||
recalculateInlineCalculations(prop.description, scope, log);
|
||||
log.content.push({value: prop.description.value});
|
||||
}
|
||||
// Apply children
|
||||
node.children.forEach(child => applyProperty(child, {
|
||||
creature, targets, scope, log
|
||||
}));
|
||||
}
|
||||
@@ -5,14 +5,14 @@ export default function applyRoll(node, {creature, targets, scope, log}){
|
||||
const prop = node.node;
|
||||
|
||||
if (prop.roll?.calculation){
|
||||
recalculateCalculation(prop.roll, scope, log, context);
|
||||
recalculateCalculation(prop.roll, scope, log);
|
||||
|
||||
if (isFinite(prop.roll.value)){
|
||||
scope[prop.variableName] = prop.roll.value;
|
||||
}
|
||||
log.content.push({
|
||||
name: prop.name,
|
||||
value: prop.variableName + ' = ' + prop.roll + ' = ' + prop.roll.value,
|
||||
value: prop.variableName + ' = ' + prop.roll.calculation + ' = ' + prop.roll.value,
|
||||
});
|
||||
}
|
||||
return node.children.forEach(child => applyProperty(child, {
|
||||
|
||||
@@ -3,11 +3,11 @@ import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
|
||||
export default function applySavingThrow(node, {creature, targets, scope, log}){
|
||||
let saveTargets = prop.target === 'self' ? [creature] : targets;
|
||||
|
||||
const prop = node.node;
|
||||
|
||||
recalculateCalculation(prop.dc, scope, log, context);
|
||||
let saveTargets = prop.target === 'self' ? [creature] : targets;
|
||||
|
||||
recalculateCalculation(prop.dc, scope, log);
|
||||
|
||||
const dc = (prop.dc?.value);
|
||||
if (!isFinite(dc)){
|
||||
|
||||
@@ -2,7 +2,7 @@ import evaluateCalculation from '/imports/api/engine/computation/utility/evaluat
|
||||
import logErrors from './logErrors.js';
|
||||
|
||||
export default function recalculateCalculation(calc, scope, log, context){
|
||||
if (!calc.parseNode) return;
|
||||
if (!calc?.parseNode) return;
|
||||
calc._parseLevel = 'reduce';
|
||||
evaluateCalculation(calc, scope, context);
|
||||
logErrors(calc.errors, log);
|
||||
|
||||
@@ -72,9 +72,9 @@ const doAction = new ValidatedMethod({
|
||||
doActionWork({creature, targets, properties, ancestors, method: this});
|
||||
|
||||
// Recompute all involved creatures
|
||||
Meteor.defer(() => computeCreature(creature._id));
|
||||
computeCreature(creature._id);
|
||||
targets.forEach(target => {
|
||||
Meteor.defer(() => computeCreature(target._id));
|
||||
computeCreature(target._id);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
11
app/imports/api/engine/actions/doAction.test.js
Normal file
11
app/imports/api/engine/actions/doAction.test.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import '/imports/api/simpleSchemaConfig.js';
|
||||
//import testTypes from './testTypes/index.js';
|
||||
import { doActionWork } from './doAction.js';
|
||||
import createAction from './tests/createAction.testFn.js';
|
||||
|
||||
describe('Do Action', function(){
|
||||
it('Does an empty action', function(){
|
||||
doActionWork(createAction({properties: [{type: 'action'}]}));
|
||||
});
|
||||
//testTypes.forEach(test => it(test.text, test.fn));
|
||||
});
|
||||
26
app/imports/api/engine/actions/tests/createAction.testFn.js
Normal file
26
app/imports/api/engine/actions/tests/createAction.testFn.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
|
||||
export default function createAction({
|
||||
creature = {_id: 'creatureId'},
|
||||
targets = [],
|
||||
properties = [],
|
||||
ancestors = [],
|
||||
method
|
||||
} = {}){
|
||||
properties = properties.map(cleanProp);
|
||||
ancestors = ancestors.map(cleanProp);
|
||||
creature = cleanCreature(creature);
|
||||
ancestors = ancestors.map(cleanCreature);
|
||||
return {creature, targets, properties, ancestors, method};
|
||||
}
|
||||
|
||||
function cleanProp(prop){
|
||||
let schema = CreatureProperties.simpleSchema(prop);
|
||||
return schema.clean(prop);
|
||||
}
|
||||
|
||||
function cleanCreature(creature){
|
||||
let schema = Creatures.simpleSchema(creature);
|
||||
return schema.clean(creature);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import applyAction from './applyAction.testFn.js';
|
||||
|
||||
export default [{
|
||||
text: 'Applies actions',
|
||||
fn: applyAction,
|
||||
},];
|
||||
Reference in New Issue
Block a user