Compare commits
17 Commits
2.0-beta.4
...
2.0-beta.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6ed9ffb74 | ||
|
|
a84da7d8a5 | ||
|
|
249aebea0f | ||
|
|
11a527481e | ||
|
|
8d729216b5 | ||
|
|
1677e8c424 | ||
|
|
987aacbb67 | ||
|
|
2714d0b9d5 | ||
|
|
1d98c41168 | ||
|
|
e42ec4b862 | ||
|
|
59fc5ab851 | ||
|
|
5d14c392e8 | ||
|
|
c6ca8c1fa4 | ||
|
|
28307e26c3 | ||
|
|
6d42eb62f0 | ||
|
|
877c9ca099 | ||
|
|
9b652fc133 |
@@ -24,7 +24,7 @@ const damageProperty = new ValidatedMethod({
|
|||||||
run({ _id, operation, value }) {
|
run({ _id, operation, value }) {
|
||||||
|
|
||||||
// Get action context
|
// Get action context
|
||||||
const prop = CreatureProperties.findOne(_id);
|
let prop = CreatureProperties.findOne(_id);
|
||||||
if (!prop) throw new Meteor.Error(
|
if (!prop) throw new Meteor.Error(
|
||||||
'Damage property failed', 'Property doesn\'t exist'
|
'Damage property failed', 'Property doesn\'t exist'
|
||||||
);
|
);
|
||||||
@@ -43,6 +43,14 @@ const damageProperty = new ValidatedMethod({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace the prop by its actionContext counterpart if possible
|
||||||
|
if (prop.variableName) {
|
||||||
|
const actionContextProp = actionContext.scope[prop.variableName];
|
||||||
|
if (actionContextProp?._id === prop._id) {
|
||||||
|
prop = actionContextProp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const result = damagePropertyWork({ prop, operation, value, actionContext });
|
const result = damagePropertyWork({ prop, operation, value, actionContext });
|
||||||
|
|
||||||
// Insert the log
|
// Insert the log
|
||||||
@@ -94,6 +102,9 @@ export function damagePropertyWork({ prop, operation, value, actionContext }) {
|
|||||||
}, {
|
}, {
|
||||||
selector: prop
|
selector: prop
|
||||||
});
|
});
|
||||||
|
// Also write it straight to the prop so that it is updated in the actionContext
|
||||||
|
prop.damage = damage;
|
||||||
|
prop.value = newValue;
|
||||||
} else if (operation === 'increment'){
|
} else if (operation === 'increment'){
|
||||||
let currentValue = prop.value || 0;
|
let currentValue = prop.value || 0;
|
||||||
let currentDamage = prop.damage || 0;
|
let currentDamage = prop.damage || 0;
|
||||||
@@ -111,6 +122,9 @@ export function damagePropertyWork({ prop, operation, value, actionContext }) {
|
|||||||
}, {
|
}, {
|
||||||
selector: prop
|
selector: prop
|
||||||
});
|
});
|
||||||
|
// Also write it straight to the prop so that it is updated in the actionContext
|
||||||
|
prop.damage += increment;
|
||||||
|
prop.value -= increment;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyTriggers(actionContext.triggers?.damageProperty?.after, prop, actionContext);
|
applyTriggers(actionContext.triggers?.damageProperty?.after, prop, actionContext);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default function applyAction(node, actionContext) {
|
|||||||
recalculateInlineCalculations(prop.summary, actionContext);
|
recalculateInlineCalculations(prop.summary, actionContext);
|
||||||
content.value = prop.summary.value;
|
content.value = prop.summary.value;
|
||||||
}
|
}
|
||||||
actionContext.addLog(content);
|
if (!prop.silent) actionContext.addLog(content);
|
||||||
|
|
||||||
// Spend the resources
|
// Spend the resources
|
||||||
const failed = spendResources(prop, actionContext);
|
const failed = spendResources(prop, actionContext);
|
||||||
@@ -188,7 +188,7 @@ function applyChildren(node, actionContext) {
|
|||||||
function spendResources(prop, actionContext){
|
function spendResources(prop, actionContext){
|
||||||
// Check Uses
|
// Check Uses
|
||||||
if (prop.usesLeft <= 0){
|
if (prop.usesLeft <= 0){
|
||||||
actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: 'Error',
|
name: 'Error',
|
||||||
value: `${prop.name || 'action'} does not have enough uses left`,
|
value: `${prop.name || 'action'} does not have enough uses left`,
|
||||||
});
|
});
|
||||||
@@ -196,7 +196,7 @@ function spendResources(prop, actionContext){
|
|||||||
}
|
}
|
||||||
// Resources
|
// Resources
|
||||||
if (prop.insufficientResources){
|
if (prop.insufficientResources){
|
||||||
actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: 'Error',
|
name: 'Error',
|
||||||
value: 'This creature doesn\'t have sufficient resources to perform this action',
|
value: 'This creature doesn\'t have sufficient resources to perform this action',
|
||||||
});
|
});
|
||||||
@@ -257,7 +257,7 @@ function spendResources(prop, actionContext){
|
|||||||
}, {
|
}, {
|
||||||
selector: prop
|
selector: prop
|
||||||
});
|
});
|
||||||
actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: 'Uses left',
|
name: 'Uses left',
|
||||||
value: prop.usesLeft - 1,
|
value: prop.usesLeft - 1,
|
||||||
inline: true,
|
inline: true,
|
||||||
@@ -288,12 +288,12 @@ function spendResources(prop, actionContext){
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Log all the spending
|
// Log all the spending
|
||||||
if (gainLog.length) actionContext.addLog({
|
if (gainLog.length && !prop.silent) actionContext.addLog({
|
||||||
name: 'Gained',
|
name: 'Gained',
|
||||||
value: gainLog.join('\n'),
|
value: gainLog.join('\n'),
|
||||||
inline: true,
|
inline: true,
|
||||||
});
|
});
|
||||||
if (spendLog.length) actionContext.addLog({
|
if (spendLog.length && !prop.silent) actionContext.addLog({
|
||||||
name: 'Spent',
|
name: 'Spent',
|
||||||
value: spendLog.join('\n'),
|
value: spendLog.join('\n'),
|
||||||
inline: true,
|
inline: true,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default function applyAdjustment(node, actionContext){
|
|||||||
damageTargets.forEach(target => {
|
damageTargets.forEach(target => {
|
||||||
let stat = target.variables[prop.stat];
|
let stat = target.variables[prop.stat];
|
||||||
if (!stat?.type) {
|
if (!stat?.type) {
|
||||||
actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: 'Error',
|
name: 'Error',
|
||||||
value: `Could not apply attribute damage, creature does not have \`${prop.stat}\` set`
|
value: `Could not apply attribute damage, creature does not have \`${prop.stat}\` set`
|
||||||
});
|
});
|
||||||
@@ -36,7 +36,7 @@ export default function applyAdjustment(node, actionContext){
|
|||||||
value,
|
value,
|
||||||
actionContext,
|
actionContext,
|
||||||
});
|
});
|
||||||
actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: 'Attribute damage',
|
name: 'Attribute damage',
|
||||||
value: `${prop.stat}${prop.operation === 'set' ? ' set to' : ''}` +
|
value: `${prop.stat}${prop.operation === 'set' ? ' set to' : ''}` +
|
||||||
` ${value}`,
|
` ${value}`,
|
||||||
@@ -44,7 +44,7 @@ export default function applyAdjustment(node, actionContext){
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: 'Attribute damage',
|
name: 'Attribute damage',
|
||||||
value: `${prop.stat}${prop.operation === 'set' ? ' set to' : ''}` +
|
value: `${prop.stat}${prop.operation === 'set' ? ' set to' : ''}` +
|
||||||
` ${value}`,
|
` ${value}`,
|
||||||
|
|||||||
@@ -36,25 +36,25 @@ export default function applyBranch(node, actionContext){
|
|||||||
break;
|
break;
|
||||||
case 'hit':
|
case 'hit':
|
||||||
if (scope['$attackHit']?.value){
|
if (scope['$attackHit']?.value){
|
||||||
if (!targets.length) actionContext.addLog({value: '**On hit**'});
|
if (!targets.length && !prop.silent) actionContext.addLog({value: '**On hit**'});
|
||||||
applyChildren();
|
applyChildren();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'miss':
|
case 'miss':
|
||||||
if (scope['$attackMiss']?.value){
|
if (scope['$attackMiss']?.value){
|
||||||
if (!targets.length) actionContext.addLog({value: '**On miss**'});
|
if (!targets.length && !prop.silent) actionContext.addLog({value: '**On miss**'});
|
||||||
applyChildren();
|
applyChildren();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'failedSave':
|
case 'failedSave':
|
||||||
if (scope['$saveFailed']?.value){
|
if (scope['$saveFailed']?.value){
|
||||||
if (!targets.length) actionContext.addLog({value: '**On failed save**'});
|
if (!targets.length && !prop.silent) actionContext.addLog({value: '**On failed save**'});
|
||||||
applyChildren();
|
applyChildren();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'successfulSave':
|
case 'successfulSave':
|
||||||
if (scope['$saveSucceeded']?.value){
|
if (scope['$saveSucceeded']?.value){
|
||||||
if (!targets.length) actionContext.addLog({value: '**On save**',});
|
if (!targets.length && !prop.silent) actionContext.addLog({value: '**On save**',});
|
||||||
applyChildren();
|
applyChildren();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import logErrors from './shared/logErrors.js';
|
|||||||
import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs.js';
|
import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs.js';
|
||||||
import cyrb53 from '/imports/api/engine/computation/utility/cyrb53.js';
|
import cyrb53 from '/imports/api/engine/computation/utility/cyrb53.js';
|
||||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||||
|
import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX.js';
|
||||||
|
|
||||||
export default function applyBuff(node, actionContext){
|
export default function applyBuff(node, actionContext){
|
||||||
applyNodeTriggers(node, 'before', actionContext);
|
applyNodeTriggers(node, 'before', actionContext);
|
||||||
@@ -32,7 +33,9 @@ export default function applyBuff(node, actionContext){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
addChildrenToPropList(node.children);
|
addChildrenToPropList(node.children);
|
||||||
crystalizeVariables({propList, actionContext});
|
if (!prop.skipCrystalization) {
|
||||||
|
crystalizeVariables({propList, actionContext});
|
||||||
|
}
|
||||||
|
|
||||||
let oldParent = {
|
let oldParent = {
|
||||||
id: prop.parent.id,
|
id: prop.parent.id,
|
||||||
@@ -43,7 +46,7 @@ export default function applyBuff(node, actionContext){
|
|||||||
copyNodeListToTarget(propList, target, oldParent);
|
copyNodeListToTarget(propList, target, oldParent);
|
||||||
|
|
||||||
//Log the buff
|
//Log the buff
|
||||||
if (prop.name || prop.description?.value){
|
if ((prop.name || prop.description?.value) && !prop.silent){
|
||||||
if (target._id === actionContext.creature._id){
|
if (target._id === actionContext.creature._id){
|
||||||
// Targeting self
|
// Targeting self
|
||||||
actionContext.addLog({
|
actionContext.addLog({
|
||||||
@@ -96,6 +99,7 @@ function crystalizeVariables({propList, actionContext}){
|
|||||||
delete prop._skipCrystalize;
|
delete prop._skipCrystalize;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Iterate through all the calculations and crystalize them
|
||||||
computedSchemas[prop.type].computedFields().forEach( calcKey => {
|
computedSchemas[prop.type].computedFields().forEach( calcKey => {
|
||||||
applyFnToKey(prop, calcKey, (prop, key) => {
|
applyFnToKey(prop, calcKey, (prop, key) => {
|
||||||
const calcObj = get(prop, key);
|
const calcObj = get(prop, key);
|
||||||
@@ -132,5 +136,36 @@ function crystalizeVariables({propList, actionContext}){
|
|||||||
calcObj.hash = cyrb53(calcObj.calculation);
|
calcObj.hash = cyrb53(calcObj.calculation);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// For each key in the schema
|
||||||
|
computedSchemas[prop.type].inlineCalculationFields().forEach( calcKey => {
|
||||||
|
// That ends in .inlineCalculations
|
||||||
|
applyFnToKey(prop, calcKey, (prop, key) => {
|
||||||
|
const inlineCalcObj = get(prop, key);
|
||||||
|
if (!inlineCalcObj) return;
|
||||||
|
|
||||||
|
// If there is no text, skip
|
||||||
|
if (!inlineCalcObj.text){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace all the existing calculations
|
||||||
|
let index = -1;
|
||||||
|
inlineCalcObj.text = inlineCalcObj.text.replace(INLINE_CALCULATION_REGEX, () => {
|
||||||
|
index += 1;
|
||||||
|
return `{${inlineCalcObj.inlineCalculations[index].calculation}}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the value to the uncomputed string
|
||||||
|
inlineCalcObj.value = inlineCalcObj.text;
|
||||||
|
|
||||||
|
// Write a new hash
|
||||||
|
const inlineCalcHash = cyrb53(inlineCalcObj.text);
|
||||||
|
if (inlineCalcHash === inlineCalcObj.hash) {
|
||||||
|
// Skip if nothing changed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inlineCalcObj.hash = inlineCalcHash;
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default function applyBuffRemover(node, actionContext) {
|
|||||||
const prop = node.node;
|
const prop = node.node;
|
||||||
|
|
||||||
// Log Name
|
// Log Name
|
||||||
if (prop.name){
|
if (prop.name && !prop.silent){
|
||||||
actionContext.addLog({ name: prop.name });
|
actionContext.addLog({ name: prop.name });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export default function applyBuffRemover(node, actionContext) {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
removeBuff(nearestBuff, actionContext);
|
removeBuff(nearestBuff, actionContext, prop);
|
||||||
} else {
|
} else {
|
||||||
// Get all the buffs targeted by tags
|
// Get all the buffs targeted by tags
|
||||||
const allBuffs = getPropertiesOfType(actionContext.creature._id, 'buff');
|
const allBuffs = getPropertiesOfType(actionContext.creature._id, 'buff');
|
||||||
@@ -41,7 +41,7 @@ export default function applyBuffRemover(node, actionContext) {
|
|||||||
if (prop.removeAll) {
|
if (prop.removeAll) {
|
||||||
// Remove all matching buffs
|
// Remove all matching buffs
|
||||||
targetedBuffs.forEach(buff => {
|
targetedBuffs.forEach(buff => {
|
||||||
removeBuff(buff, actionContext);
|
removeBuff(buff, actionContext, prop);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Sort in reverse order
|
// Sort in reverse order
|
||||||
@@ -49,7 +49,7 @@ export default function applyBuffRemover(node, actionContext) {
|
|||||||
// Remove the one with the highest order
|
// Remove the one with the highest order
|
||||||
const buff = targetedBuffs[0];
|
const buff = targetedBuffs[0];
|
||||||
if (buff) {
|
if (buff) {
|
||||||
removeBuff(buff, actionContext);
|
removeBuff(buff, actionContext, prop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,8 +60,8 @@ export default function applyBuffRemover(node, actionContext) {
|
|||||||
node.children.forEach(child => applyProperty(child, actionContext));
|
node.children.forEach(child => applyProperty(child, actionContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeBuff(buff, actionContext) {
|
function removeBuff(buff, actionContext, prop) {
|
||||||
actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: 'Removed',
|
name: 'Removed',
|
||||||
value: `${buff.name || 'Buff'}`
|
value: `${buff.name || 'Buff'}`
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export default function applyDamage(node, actionContext){
|
|||||||
// There are no targets, just log the result
|
// There are no targets, just log the result
|
||||||
logValue.push(`**${damage}** ${suffix}`);
|
logValue.push(`**${damage}** ${suffix}`);
|
||||||
}
|
}
|
||||||
actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: logName,
|
name: logName,
|
||||||
value: logValue.join('\n'),
|
value: logValue.join('\n'),
|
||||||
inline: true,
|
inline: true,
|
||||||
@@ -219,6 +219,16 @@ function dealDamage({target, damageType, amount, actionContext}){
|
|||||||
if (damageType === 'healing') damageLeft = -totalDamage;
|
if (damageType === 'healing') damageLeft = -totalDamage;
|
||||||
healthBars.forEach(healthBar => {
|
healthBars.forEach(healthBar => {
|
||||||
if (damageLeft === 0) return;
|
if (damageLeft === 0) return;
|
||||||
|
// Replace the healthbar by the one in the action context if we can
|
||||||
|
// The damagePropertyWork function bashes the prop with the damage
|
||||||
|
// So we can use the new value in later action properties
|
||||||
|
if (healthBar.variableName) {
|
||||||
|
const targetHealthBar = target.variables[healthBar.variableName];
|
||||||
|
if (targetHealthBar?._id === healthBar._id) {
|
||||||
|
healthBar = targetHealthBar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Do the damage
|
||||||
let damageAdded = damagePropertyWork({
|
let damageAdded = damagePropertyWork({
|
||||||
prop: healthBar,
|
prop: healthBar,
|
||||||
operation: 'increment',
|
operation: 'increment',
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default function applySavingThrow(node, actionContext){
|
|||||||
});
|
});
|
||||||
return node.children.forEach(child => applyProperty(child, actionContext));
|
return node.children.forEach(child => applyProperty(child, actionContext));
|
||||||
}
|
}
|
||||||
actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: prop.name,
|
name: prop.name,
|
||||||
value: `DC **${dc}**`,
|
value: `DC **${dc}**`,
|
||||||
inline: true,
|
inline: true,
|
||||||
@@ -94,7 +94,7 @@ export default function applySavingThrow(node, actionContext){
|
|||||||
} else {
|
} else {
|
||||||
scope['$saveFailed'] = {value: true};
|
scope['$saveFailed'] = {value: true};
|
||||||
}
|
}
|
||||||
actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: saveSuccess ? 'Successful save' : 'Failed save',
|
name: saveSuccess ? 'Successful save' : 'Failed save',
|
||||||
value: resultPrefix + '\n**' + result + '**',
|
value: resultPrefix + '\n**' + result + '**',
|
||||||
inline: true,
|
inline: true,
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export function applyTrigger(trigger, prop, actionContext) {
|
|||||||
recalculateInlineCalculations(trigger.description, actionContext);
|
recalculateInlineCalculations(trigger.description, actionContext);
|
||||||
content.value = trigger.description.value;
|
content.value = trigger.description.value;
|
||||||
}
|
}
|
||||||
actionContext.addLog(content);
|
if(!trigger.silent) actionContext.addLog(content);
|
||||||
|
|
||||||
// Get all the trigger's properties and apply them
|
// Get all the trigger's properties and apply them
|
||||||
const properties = getPropertyDecendants(actionContext.creature._id, trigger._id);
|
const properties = getPropertyDecendants(actionContext.creature._id, trigger._id);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
|
|||||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||||
import { doActionWork } from '/imports/api/engine/actions/doAction.js';
|
import { doActionWork } from '/imports/api/engine/actions/doAction.js';
|
||||||
import { CreatureLogSchema } from '/imports/api/creature/log/CreatureLogs.js';
|
|
||||||
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
||||||
|
|
||||||
const doAction = new ValidatedMethod({
|
const doAction = new ValidatedMethod({
|
||||||
@@ -65,8 +64,6 @@ const doAction = new ValidatedMethod({
|
|||||||
let slotLevel = spell.level || 0;
|
let slotLevel = spell.level || 0;
|
||||||
let slot;
|
let slot;
|
||||||
|
|
||||||
actionContext.scope['slotLevel'] = slotLevel;
|
|
||||||
|
|
||||||
if (slotId && !spell.castWithoutSpellSlots){
|
if (slotId && !spell.castWithoutSpellSlots){
|
||||||
slot = CreatureProperties.findOne(slotId);
|
slot = CreatureProperties.findOne(slotId);
|
||||||
if (!slot){
|
if (!slot){
|
||||||
@@ -109,6 +106,8 @@ const doAction = new ValidatedMethod({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actionContext.scope['slotLevel'] = slotLevel;
|
||||||
|
|
||||||
// Do the action
|
// Do the action
|
||||||
doActionWork({
|
doActionWork({
|
||||||
properties, ancestors, actionContext, methodScope: scope,
|
properties, ancestors, actionContext, methodScope: scope,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const linkDependenciesByType = {
|
|||||||
effect: linkEffects,
|
effect: linkEffects,
|
||||||
proficiency: linkProficiencies,
|
proficiency: linkProficiencies,
|
||||||
roll: linkRoll,
|
roll: linkRoll,
|
||||||
|
pointBuy: linkPointBuy,
|
||||||
propertySlot: linkSlot,
|
propertySlot: linkSlot,
|
||||||
skill: linkSkill,
|
skill: linkSkill,
|
||||||
spell: linkAction,
|
spell: linkAction,
|
||||||
@@ -242,6 +243,28 @@ function linkDamageMultiplier(dependencyGraph, prop) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function linkPointBuy(dependencyGraph, prop){
|
||||||
|
dependOnCalc({ dependencyGraph, prop, key: 'min' });
|
||||||
|
dependOnCalc({ dependencyGraph, prop, key: 'max' });
|
||||||
|
dependOnCalc({ dependencyGraph, prop, key: 'cost' });
|
||||||
|
dependOnCalc({ dependencyGraph, prop, key: 'total' });
|
||||||
|
prop.values?.forEach(row => {
|
||||||
|
// Wrap the document in a new object so we don't bash it unintentionally
|
||||||
|
const pointBuyRow = {
|
||||||
|
...row,
|
||||||
|
type: 'pointBuyRow',
|
||||||
|
tableName: prop.name,
|
||||||
|
tableId: prop._id,
|
||||||
|
}
|
||||||
|
dependencyGraph.addNode(row._id, pointBuyRow);
|
||||||
|
linkVariableName(dependencyGraph, pointBuyRow);
|
||||||
|
dependOnCalc({ dependencyGraph, pointBuyRow, key: 'row.min' });
|
||||||
|
dependOnCalc({ dependencyGraph, pointBuyRow, key: 'row.max' });
|
||||||
|
dependOnCalc({ dependencyGraph, pointBuyRow, key: 'row.cost' });
|
||||||
|
});
|
||||||
|
if (prop.inactive) return;
|
||||||
|
}
|
||||||
|
|
||||||
function linkProficiencies(dependencyGraph, prop){
|
function linkProficiencies(dependencyGraph, prop){
|
||||||
// The stats depend on the proficiency
|
// The stats depend on the proficiency
|
||||||
if (prop.inactive) return;
|
if (prop.inactive) return;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import _variable from './computeByType/computeVariable.js';
|
|||||||
import action from './computeByType/computeAction.js';
|
import action from './computeByType/computeAction.js';
|
||||||
import attribute from './computeByType/computeAttribute.js';
|
import attribute from './computeByType/computeAttribute.js';
|
||||||
import skill from './computeByType/computeSkill.js';
|
import skill from './computeByType/computeSkill.js';
|
||||||
|
import pointBuy from './computeByType/computePointBuy.js';
|
||||||
import propertySlot from './computeByType/computeSlot.js';
|
import propertySlot from './computeByType/computeSlot.js';
|
||||||
import container from './computeByType/computeContainer.js';
|
import container from './computeByType/computeContainer.js';
|
||||||
import _calculation from './computeByType/computeCalculation.js';
|
import _calculation from './computeByType/computeCalculation.js';
|
||||||
@@ -13,6 +14,7 @@ export default Object.freeze({
|
|||||||
attribute,
|
attribute,
|
||||||
container,
|
container,
|
||||||
skill,
|
skill,
|
||||||
|
pointBuy,
|
||||||
propertySlot,
|
propertySlot,
|
||||||
spell: action,
|
spell: action,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { has } from 'lodash';
|
||||||
|
import evaluateCalculation from '../../utility/evaluateCalculation.js';
|
||||||
|
|
||||||
|
export default function computePointBuy(computation, node) {
|
||||||
|
const prop = node.data;
|
||||||
|
const tableMin = prop.min?.value || null;
|
||||||
|
const tableMax = prop.max?.value || null;
|
||||||
|
prop.spent = 0;
|
||||||
|
prop.values?.forEach(row => {
|
||||||
|
// Clean up added properties
|
||||||
|
// delete row.tableId;
|
||||||
|
// delete row.tableName;
|
||||||
|
// delete row.type;
|
||||||
|
|
||||||
|
row.spent = 0;
|
||||||
|
if (row.value === undefined) return;
|
||||||
|
const min = has(row, 'min.value') ? row.min.value : tableMin;
|
||||||
|
const max = has(row, 'max.value') ? row.max.value : tableMax;
|
||||||
|
const costFunction = EJSON.clone(row.cost || prop.cost);
|
||||||
|
if (costFunction) costFunction.parseLevel = 'reduce';
|
||||||
|
|
||||||
|
// Check min and max
|
||||||
|
if (min !== null && row.value < min) {
|
||||||
|
row.value = min;
|
||||||
|
}
|
||||||
|
if (max !== null && row.value > max) {
|
||||||
|
row.value = max;
|
||||||
|
}
|
||||||
|
// Evaluate the cost function
|
||||||
|
if (!costFunction) return;
|
||||||
|
evaluateCalculation(costFunction, { ...computation.scope, value: row.value });
|
||||||
|
// Write calculation errors
|
||||||
|
costFunction.errors?.forEach(error => {
|
||||||
|
if (error?.message) {
|
||||||
|
row.errors = row.errors || [];
|
||||||
|
error.message = 'Cost calculation error.\n' + error.message;
|
||||||
|
row.errors.push(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (Number.isFinite(costFunction.value)) {
|
||||||
|
row.spent = costFunction.value;
|
||||||
|
prop.spent += costFunction.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
prop.pointsLeft = (prop.total?.value || 0) - (prop.spent || 0);
|
||||||
|
if (prop.spent > prop.total?.value) {
|
||||||
|
prop.errors = prop.errors || [];
|
||||||
|
prop.errors.push({
|
||||||
|
type: 'pointBuyError',
|
||||||
|
message: 'Spent more than total points available',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
// by computeVariableAsSkill
|
// by computeVariableAsSkill
|
||||||
export default function computeSkill(computation, node){
|
export default function computeSkill(computation, node){
|
||||||
const prop = node.data;
|
const prop = node.data;
|
||||||
prop.proficiency = prop.baseProficiency;
|
prop.proficiency = prop.baseProficiency || 0;
|
||||||
let profBonus = computation.scope['proficiencyBonus']?.value || 0;
|
let profBonus = computation.scope['proficiencyBonus']?.value || 0;
|
||||||
// Multiply the proficiency bonus by the actual proficiency
|
// Multiply the proficiency bonus by the actual proficiency
|
||||||
if(prop.proficiency === 0.49){
|
if(prop.proficiency === 0.49){
|
||||||
|
|||||||
@@ -8,7 +8,13 @@ export default function aggregateDefinition({node, linkedNode, link}){
|
|||||||
// get current defining prop
|
// get current defining prop
|
||||||
const definingProp = node.data.definingProp;
|
const definingProp = node.data.definingProp;
|
||||||
// Find the last defining prop
|
// Find the last defining prop
|
||||||
if (!definingProp || prop.order > definingProp.order){
|
if (
|
||||||
|
!definingProp ||
|
||||||
|
prop.type !== 'pointBuyRow' && (
|
||||||
|
definingProp.type === 'pointBuyRow' ||
|
||||||
|
prop.order > definingProp.order
|
||||||
|
)
|
||||||
|
) {
|
||||||
// override the current defining prop
|
// override the current defining prop
|
||||||
overrideProp(definingProp, node);
|
overrideProp(definingProp, node);
|
||||||
// set this prop as the new defining prop
|
// set this prop as the new defining prop
|
||||||
@@ -18,9 +24,32 @@ export default function aggregateDefinition({node, linkedNode, link}){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Aggregate the base value due to the defining properties
|
// Aggregate the base value due to the defining properties
|
||||||
const propBaseValue = prop.baseValue?.value;
|
let propBaseValue = prop.baseValue?.value;
|
||||||
|
// Point buy rows use prop.value instead of prop.baseValue
|
||||||
|
if (prop.type === 'pointBuyRow') {
|
||||||
|
propBaseValue = prop.value;
|
||||||
|
}
|
||||||
|
|
||||||
if (propBaseValue === undefined) return;
|
if (propBaseValue === undefined) return;
|
||||||
|
// Store a summary of the definition as a base value effect
|
||||||
|
node.data.effects = node.data.effects || [];
|
||||||
|
if (prop.type === 'pointBuyRow') {
|
||||||
|
node.data.effects.push({
|
||||||
|
_id: prop.tableId,
|
||||||
|
name: prop.tableName,
|
||||||
|
operation: 'base',
|
||||||
|
amount: { value: propBaseValue },
|
||||||
|
type: 'pointBuy',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
node.data.effects.push({
|
||||||
|
_id: prop._id,
|
||||||
|
name: prop.name,
|
||||||
|
operation: 'base',
|
||||||
|
amount: { value: propBaseValue },
|
||||||
|
type: prop.type,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (node.data.baseValue === undefined || propBaseValue > node.data.baseValue){
|
if (node.data.baseValue === undefined || propBaseValue > node.data.baseValue){
|
||||||
node.data.baseValue = propBaseValue;
|
node.data.baseValue = propBaseValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export default function aggregateEffect({node, linkedNode, link}){
|
|||||||
name: linkedNode.data.name,
|
name: linkedNode.data.name,
|
||||||
operation: linkedNode.data.operation,
|
operation: linkedNode.data.operation,
|
||||||
amount: linkedNode.data.amount && {value: linkedNode.data.amount.value},
|
amount: linkedNode.data.amount && {value: linkedNode.data.amount.value},
|
||||||
|
type: linkedNode.data.type,
|
||||||
// ancestors: linkedNode.data.ancestors,
|
// ancestors: linkedNode.data.ancestors,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ export default function computeVariableAsSkill(computation, node, prop){
|
|||||||
const aggregator = node.data.effectAggregator;
|
const aggregator = node.data.effectAggregator;
|
||||||
const aggregatorBase = aggregator?.base || 0;
|
const aggregatorBase = aggregator?.base || 0;
|
||||||
|
|
||||||
|
// Store effects
|
||||||
|
prop.effects = node.data.effects;
|
||||||
|
|
||||||
// If there is no aggregator, determine if the prop can hide, then exit
|
// If there is no aggregator, determine if the prop can hide, then exit
|
||||||
if (!aggregator){
|
if (!aggregator){
|
||||||
prop.hide = statBase === undefined &&
|
prop.hide = statBase === undefined &&
|
||||||
@@ -71,8 +74,6 @@ export default function computeVariableAsSkill(computation, node, prop){
|
|||||||
prop.fail = aggregator.fail;
|
prop.fail = aggregator.fail;
|
||||||
// Rollbonus
|
// Rollbonus
|
||||||
prop.rollBonuses = aggregator.rollBonus;
|
prop.rollBonuses = aggregator.rollBonus;
|
||||||
// Store effects
|
|
||||||
prop.effects = node.data.effects;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function aggregateAbilityEffects({computation, skillNode, abilityNode}){
|
function aggregateAbilityEffects({computation, skillNode, abilityNode}){
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export default function getEffectivePropTags(prop) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tags for some string properties
|
// Tags for some string properties
|
||||||
|
if (prop.variableName) tags.push(prop.variableName);
|
||||||
if (prop.damageType) tags.push(prop.damageType);
|
if (prop.damageType) tags.push(prop.damageType);
|
||||||
if (prop.skillType) tags.push(prop.skillType);
|
if (prop.skillType) tags.push(prop.skillType);
|
||||||
if (prop.attributeType) tags.push(prop.attributeType);
|
if (prop.attributeType) tags.push(prop.attributeType);
|
||||||
|
|||||||
@@ -97,7 +97,10 @@ export function getCreature(creatureId) {
|
|||||||
if (loadedCreatures.has(creatureId)) {
|
if (loadedCreatures.has(creatureId)) {
|
||||||
const loadedCreature = loadedCreatures.get(creatureId);
|
const loadedCreature = loadedCreatures.get(creatureId);
|
||||||
const creature = loadedCreature.creature;
|
const creature = loadedCreature.creature;
|
||||||
if (creature) return creature;
|
if (creature) {
|
||||||
|
const cloneCreature = EJSON.clone(creature);
|
||||||
|
return cloneCreature;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// console.time(`Cache miss on Creature: ${creatureId}`);
|
// console.time(`Cache miss on Creature: ${creatureId}`);
|
||||||
const creature = Creatures.findOne(creatureId);
|
const creature = Creatures.findOne(creatureId);
|
||||||
@@ -109,7 +112,10 @@ export function getVariables(creatureId) {
|
|||||||
if (loadedCreatures.has(creatureId)) {
|
if (loadedCreatures.has(creatureId)) {
|
||||||
const loadedCreature = loadedCreatures.get(creatureId);
|
const loadedCreature = loadedCreatures.get(creatureId);
|
||||||
const variables = loadedCreature.variables;
|
const variables = loadedCreature.variables;
|
||||||
if (variables) return variables;
|
if (variables) {
|
||||||
|
const cloneVarables = EJSON.clone(variables);
|
||||||
|
return cloneVarables;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// console.time(`Cache miss on variables: ${creatureId}`);
|
// console.time(`Cache miss on variables: ${creatureId}`);
|
||||||
const variables = CreatureVariables.findOne({_creatureId: creatureId});
|
const variables = CreatureVariables.findOne({_creatureId: creatureId});
|
||||||
|
|||||||
@@ -114,6 +114,11 @@ let ActionSchema = createPropertySchema({
|
|||||||
type: 'fieldToCompute',
|
type: 'fieldToCompute',
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
|
// Prevent the property from showing up in the log
|
||||||
|
silent: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ComputedOnlyActionSchema = createPropertySchema({
|
const ComputedOnlyActionSchema = createPropertySchema({
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ const AdjustmentSchema = createPropertySchema({
|
|||||||
allowedValues: ['set', 'increment'],
|
allowedValues: ['set', 'increment'],
|
||||||
defaultValue: 'increment',
|
defaultValue: 'increment',
|
||||||
},
|
},
|
||||||
|
// Prevent the property from showing up in the log
|
||||||
|
silent: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ComputedOnlyAdjustmentSchema = createPropertySchema({
|
const ComputedOnlyAdjustmentSchema = createPropertySchema({
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ let ComputedOnlyAttributeSchema = createPropertySchema({
|
|||||||
effects: {
|
effects: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
removeBeforeCompute: true,
|
||||||
},
|
},
|
||||||
'effects.$': {
|
'effects.$': {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ let BranchSchema = createPropertySchema({
|
|||||||
optional: true,
|
optional: true,
|
||||||
parseLevel: 'compile',
|
parseLevel: 'compile',
|
||||||
},
|
},
|
||||||
|
// Prevent the property from showing up in the log
|
||||||
|
silent: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let ComputedOnlyBranchSchema = createPropertySchema({
|
let ComputedOnlyBranchSchema = createPropertySchema({
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ let BuffRemoverSchema = createPropertySchema({
|
|||||||
type: String,
|
type: String,
|
||||||
max: STORAGE_LIMITS.tagLength,
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
|
// Prevent the property from showing up in the log
|
||||||
|
silent: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let ComputedOnlyBuffRemoverSchema = createPropertySchema({});
|
let ComputedOnlyBuffRemoverSchema = createPropertySchema({});
|
||||||
|
|||||||
@@ -29,6 +29,16 @@ let BuffSchema = createPropertySchema({
|
|||||||
],
|
],
|
||||||
defaultValue: 'target',
|
defaultValue: 'target',
|
||||||
},
|
},
|
||||||
|
// Prevent the property from showing up in the log
|
||||||
|
silent: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
// Prevent the children from being crystalized
|
||||||
|
skipCrystalization: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let ComputedOnlyBuffSchema = createPropertySchema({
|
let ComputedOnlyBuffSchema = createPropertySchema({
|
||||||
|
|||||||
@@ -26,7 +26,12 @@ const DamageSchema = createPropertySchema({
|
|||||||
max: STORAGE_LIMITS.calculation,
|
max: STORAGE_LIMITS.calculation,
|
||||||
defaultValue: 'slashing',
|
defaultValue: 'slashing',
|
||||||
regEx: VARIABLE_NAME_REGEX,
|
regEx: VARIABLE_NAME_REGEX,
|
||||||
},
|
},
|
||||||
|
// Prevent the property from showing up in the log
|
||||||
|
silent: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ComputedOnlyDamageSchema = createPropertySchema({
|
const ComputedOnlyDamageSchema = createPropertySchema({
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import SimpleSchema from 'simpl-schema';
|
|||||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
||||||
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js';
|
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js';
|
||||||
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PointBuys are reason-value attached to skills and abilities
|
* PointBuys are reason-value attached to skills and abilities
|
||||||
@@ -13,13 +14,6 @@ let PointBuySchema = createPropertySchema({
|
|||||||
optional: true,
|
optional: true,
|
||||||
max: STORAGE_LIMITS.name,
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
variableName: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
regEx: VARIABLE_NAME_REGEX,
|
|
||||||
min: 2,
|
|
||||||
max: STORAGE_LIMITS.variableName,
|
|
||||||
},
|
|
||||||
ignored: {
|
ignored: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
optional: true,
|
optional: true,
|
||||||
@@ -27,10 +21,18 @@ let PointBuySchema = createPropertySchema({
|
|||||||
'values': {
|
'values': {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.pointBuyRowsCount,
|
||||||
},
|
},
|
||||||
'values.$': {
|
'values.$': {
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
'values.$._id': {
|
||||||
|
type: String,
|
||||||
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
|
autoValue(){
|
||||||
|
if (!this.isSet) return Random.id();
|
||||||
|
}
|
||||||
|
},
|
||||||
'values.$.name': {
|
'values.$.name': {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
@@ -47,6 +49,18 @@ let PointBuySchema = createPropertySchema({
|
|||||||
type: Number,
|
type: Number,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
|
'values.$.min': {
|
||||||
|
type: 'fieldToCompute',
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
'values.$.max': {
|
||||||
|
type: 'fieldToCompute',
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
'values.$.cost': {
|
||||||
|
type: 'fieldToCompute',
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
min: {
|
min: {
|
||||||
type: 'fieldToCompute',
|
type: 'fieldToCompute',
|
||||||
optional: true,
|
optional: true,
|
||||||
@@ -62,6 +76,7 @@ let PointBuySchema = createPropertySchema({
|
|||||||
cost: {
|
cost: {
|
||||||
type: 'fieldToCompute',
|
type: 'fieldToCompute',
|
||||||
optional: true,
|
optional: true,
|
||||||
|
parseLevel: 'compile',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -74,11 +89,46 @@ const ComputedOnlyPointBuySchema = createPropertySchema({
|
|||||||
type: 'computedOnlyField',
|
type: 'computedOnlyField',
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
total: {
|
cost: {
|
||||||
|
type: 'computedOnlyField',
|
||||||
|
optional: true,
|
||||||
|
parseLevel: 'compile',
|
||||||
|
},
|
||||||
|
'values': {
|
||||||
|
type: Array,
|
||||||
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.pointBuyRowsCount,
|
||||||
|
},
|
||||||
|
'values.$': {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
'values.$.min': {
|
||||||
type: 'computedOnlyField',
|
type: 'computedOnlyField',
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
cost: {
|
'values.$.max': {
|
||||||
|
type: 'computedOnlyField',
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
'values.$.cost': {
|
||||||
|
type: 'computedOnlyField',
|
||||||
|
optional: true,
|
||||||
|
parseLevel: 'compile',
|
||||||
|
},
|
||||||
|
'values.$.spent': {
|
||||||
|
type: Number,
|
||||||
|
optional: true,
|
||||||
|
removeBeforeCompute: true,
|
||||||
|
},
|
||||||
|
'values.$.errors': {
|
||||||
|
type: Array,
|
||||||
|
optional: true,
|
||||||
|
removeBeforeCompute: true,
|
||||||
|
},
|
||||||
|
'values.$.errors.$': {
|
||||||
|
type: ErrorSchema,
|
||||||
|
},
|
||||||
|
total: {
|
||||||
type: 'computedOnlyField',
|
type: 'computedOnlyField',
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
@@ -87,6 +137,19 @@ const ComputedOnlyPointBuySchema = createPropertySchema({
|
|||||||
optional: true,
|
optional: true,
|
||||||
removeBeforeCompute: true,
|
removeBeforeCompute: true,
|
||||||
},
|
},
|
||||||
|
pointsLeft: {
|
||||||
|
type: Number,
|
||||||
|
optional: true,
|
||||||
|
removeBeforeCompute: true,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
type: Array,
|
||||||
|
optional: true,
|
||||||
|
removeBeforeCompute: true,
|
||||||
|
},
|
||||||
|
'errors.$': {
|
||||||
|
type: ErrorSchema,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ComputedPointBuySchema = new SimpleSchema()
|
const ComputedPointBuySchema = new SimpleSchema()
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ let SavingThrowSchema = createPropertySchema({
|
|||||||
optional: true,
|
optional: true,
|
||||||
max: STORAGE_LIMITS.variableName,
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
|
// Prevent the property from showing up in the log
|
||||||
|
silent: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ComputedOnlySavingThrowSchema = createPropertySchema({
|
const ComputedOnlySavingThrowSchema = createPropertySchema({
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ let ComputedOnlySkillSchema = createPropertySchema({
|
|||||||
effects: {
|
effects: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
removeBeforeCompute: true,
|
||||||
},
|
},
|
||||||
'effects.$': {
|
'effects.$': {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|||||||
@@ -109,6 +109,11 @@ let TriggerSchema = createPropertySchema({
|
|||||||
type: String,
|
type: String,
|
||||||
max: STORAGE_LIMITS.tagLength,
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
|
// Prevent the property from showing up in the log
|
||||||
|
silent: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ComputedOnlyTriggerSchema = createPropertySchema({
|
const ComputedOnlyTriggerSchema = createPropertySchema({
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { ComputedOnlyFeatureSchema } from '/imports/api/properties/Features.js';
|
|||||||
import { ComputedOnlyFolderSchema } from '/imports/api/properties/Folders.js';
|
import { ComputedOnlyFolderSchema } from '/imports/api/properties/Folders.js';
|
||||||
import { ComputedOnlyItemSchema } from '/imports/api/properties/Items.js';
|
import { ComputedOnlyItemSchema } from '/imports/api/properties/Items.js';
|
||||||
import { ComputedOnlyNoteSchema } from '/imports/api/properties/Notes.js';
|
import { ComputedOnlyNoteSchema } from '/imports/api/properties/Notes.js';
|
||||||
|
import { ComputedOnlyPointBuySchema } from '/imports/api/properties/PointBuys.js';
|
||||||
import { ComputedOnlyProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
import { ComputedOnlyProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
||||||
import { ComputedOnlyReferenceSchema } from '/imports/api/properties/References.js';
|
import { ComputedOnlyReferenceSchema } from '/imports/api/properties/References.js';
|
||||||
import { ComputedOnlyRollSchema } from '/imports/api/properties/Rolls.js';
|
import { ComputedOnlyRollSchema } from '/imports/api/properties/Rolls.js';
|
||||||
@@ -46,6 +47,7 @@ const propertySchemasIndex = {
|
|||||||
folder: ComputedOnlyFolderSchema,
|
folder: ComputedOnlyFolderSchema,
|
||||||
item: ComputedOnlyItemSchema,
|
item: ComputedOnlyItemSchema,
|
||||||
note: ComputedOnlyNoteSchema,
|
note: ComputedOnlyNoteSchema,
|
||||||
|
pointBuy: ComputedOnlyPointBuySchema,
|
||||||
proficiency: ComputedOnlyProficiencySchema,
|
proficiency: ComputedOnlyProficiencySchema,
|
||||||
propertySlot: ComputedOnlySlotSchema,
|
propertySlot: ComputedOnlySlotSchema,
|
||||||
reference: ComputedOnlyReferenceSchema,
|
reference: ComputedOnlyReferenceSchema,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { ComputedFeatureSchema } from '/imports/api/properties/Features.js';
|
|||||||
import { FolderSchema } from '/imports/api/properties/Folders.js';
|
import { FolderSchema } from '/imports/api/properties/Folders.js';
|
||||||
import { ComputedItemSchema } from '/imports/api/properties/Items.js';
|
import { ComputedItemSchema } from '/imports/api/properties/Items.js';
|
||||||
import { ComputedNoteSchema } from '/imports/api/properties/Notes.js';
|
import { ComputedNoteSchema } from '/imports/api/properties/Notes.js';
|
||||||
|
import { ComputedPointBuySchema } from '/imports/api/properties/PointBuys.js';
|
||||||
import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
||||||
import { ReferenceSchema } from '/imports/api/properties/References.js';
|
import { ReferenceSchema } from '/imports/api/properties/References.js';
|
||||||
import { ComputedRollSchema } from '/imports/api/properties/Rolls.js';
|
import { ComputedRollSchema } from '/imports/api/properties/Rolls.js';
|
||||||
@@ -44,6 +45,7 @@ const propertySchemasIndex = {
|
|||||||
feature: ComputedFeatureSchema,
|
feature: ComputedFeatureSchema,
|
||||||
folder: FolderSchema,
|
folder: FolderSchema,
|
||||||
note: ComputedNoteSchema,
|
note: ComputedNoteSchema,
|
||||||
|
pointBuy: ComputedPointBuySchema,
|
||||||
proficiency: ProficiencySchema,
|
proficiency: ProficiencySchema,
|
||||||
propertySlot: ComputedSlotSchema,
|
propertySlot: ComputedSlotSchema,
|
||||||
reference: ReferenceSchema,
|
reference: ReferenceSchema,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { EffectSchema } from '/imports/api/properties/Effects.js';
|
|||||||
import { FeatureSchema } from '/imports/api/properties/Features.js';
|
import { FeatureSchema } from '/imports/api/properties/Features.js';
|
||||||
import { FolderSchema } from '/imports/api/properties/Folders.js';
|
import { FolderSchema } from '/imports/api/properties/Folders.js';
|
||||||
import { NoteSchema } from '/imports/api/properties/Notes.js';
|
import { NoteSchema } from '/imports/api/properties/Notes.js';
|
||||||
|
import { PointBuySchema } from '/imports/api/properties/PointBuys.js';
|
||||||
import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
||||||
import { ReferenceSchema } from '/imports/api/properties/References.js';
|
import { ReferenceSchema } from '/imports/api/properties/References.js';
|
||||||
import { RollSchema } from '/imports/api/properties/Rolls.js';
|
import { RollSchema } from '/imports/api/properties/Rolls.js';
|
||||||
@@ -44,6 +45,7 @@ const propertySchemasIndex = {
|
|||||||
feature: FeatureSchema,
|
feature: FeatureSchema,
|
||||||
folder: FolderSchema,
|
folder: FolderSchema,
|
||||||
note: NoteSchema,
|
note: NoteSchema,
|
||||||
|
pointBuy: PointBuySchema,
|
||||||
proficiency: ProficiencySchema,
|
proficiency: ProficiencySchema,
|
||||||
propertySlot: SlotSchema,
|
propertySlot: SlotSchema,
|
||||||
reference: ReferenceSchema,
|
reference: ReferenceSchema,
|
||||||
|
|||||||
@@ -102,7 +102,13 @@ const PROPERTIES = Object.freeze({
|
|||||||
icon: 'mdi-note-outline',
|
icon: 'mdi-note-outline',
|
||||||
name: 'Note',
|
name: 'Note',
|
||||||
helpText: 'Notes about your character and their adventures',
|
helpText: 'Notes about your character and their adventures',
|
||||||
suggestedParents: ['folder'],
|
suggestedParents: ['note', 'folder'],
|
||||||
|
},
|
||||||
|
pointBuy: {
|
||||||
|
icon: 'mdi-table',
|
||||||
|
name: 'Point Buy',
|
||||||
|
helpText: 'A point buy table that allows the user to select an array of values that match a given cost',
|
||||||
|
suggestedParents: [],
|
||||||
},
|
},
|
||||||
proficiency: {
|
proficiency: {
|
||||||
icon: 'mdi-brightness-1',
|
icon: 'mdi-brightness-1',
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const STORAGE_LIMITS = Object.freeze({
|
|||||||
tagCount: 64,
|
tagCount: 64,
|
||||||
writersCount: 20,
|
writersCount: 20,
|
||||||
libraryCollectionCount: 32,
|
libraryCollectionCount: 32,
|
||||||
|
pointBuyRowsCount: 32,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default STORAGE_LIMITS;
|
export default STORAGE_LIMITS;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
|
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables';
|
||||||
import { assertViewPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
import { assertViewPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||||
import computeCreature from '/imports/api/engine/computeCreature.js';
|
import computeCreature from '/imports/api/engine/computeCreature.js';
|
||||||
import VERSION from '/imports/constants/VERSION.js';
|
import VERSION from '/imports/constants/VERSION.js';
|
||||||
@@ -40,6 +41,9 @@ Meteor.publish('api-creature', function(creatureId){
|
|||||||
CreatureProperties.find({
|
CreatureProperties.find({
|
||||||
'ancestors.id': creatureId,
|
'ancestors.id': creatureId,
|
||||||
}),
|
}),
|
||||||
|
CreatureVariables.find({
|
||||||
|
_creatureId: creatureId,
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}, {
|
}, {
|
||||||
url: 'api/creature/:0'
|
url: 'api/creature/:0'
|
||||||
|
|||||||
72
app/imports/ui/components/global/SmartBtn.vue
Normal file
72
app/imports/ui/components/global/SmartBtn.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<v-btn
|
||||||
|
v-bind="$attrs"
|
||||||
|
:disabled="isDisabled"
|
||||||
|
:loading="loading"
|
||||||
|
@click="click"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: {
|
||||||
|
context: { default: {} }
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
disabled: Boolean,
|
||||||
|
debounce: {
|
||||||
|
type: Number,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
timesClicked: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isDisabled(){
|
||||||
|
return this.context.editPermission === false || this.disabled;
|
||||||
|
},
|
||||||
|
debounceTime() {
|
||||||
|
if (Number.isFinite(this.debounce)){
|
||||||
|
return this.debounce;
|
||||||
|
} else if (Number.isFinite(this.context.debounceTime)){
|
||||||
|
return this.context.debounceTime;
|
||||||
|
} else {
|
||||||
|
return 750;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
this.debounceClicks = debounce(this.clicks, this.debounceTime);
|
||||||
|
},
|
||||||
|
beforeDestroy(){
|
||||||
|
this.debounceClicks.flush();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
click() {
|
||||||
|
this.timesClicked += 1;
|
||||||
|
this.debounceClicks();
|
||||||
|
this.$emit('click', this.acknowledgeChange);
|
||||||
|
},
|
||||||
|
clicks() {
|
||||||
|
this.$emit('clicks', this.timesClicked, this.acknowledgeChange);
|
||||||
|
this.loading = true;
|
||||||
|
this.timesClicked = 0;
|
||||||
|
},
|
||||||
|
acknowledgeChange(error){
|
||||||
|
this.loading = false;
|
||||||
|
if (error) {
|
||||||
|
snackbar({ text: error.reason || error.message || error.toString() });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -115,7 +115,7 @@ export default {
|
|||||||
},
|
},
|
||||||
change(val){
|
change(val){
|
||||||
this.dirty = true;
|
this.dirty = true;
|
||||||
if (this.hasChangeListener) this.loading = true;
|
if (this.hasChangeListener()) this.loading = true;
|
||||||
this.$emit('change', val, this.acknowledgeChange);
|
this.$emit('change', val, this.acknowledgeChange);
|
||||||
},
|
},
|
||||||
hasChangeListener(){
|
hasChangeListener(){
|
||||||
|
|||||||
34
app/imports/ui/components/global/SmartSlider.vue
Normal file
34
app/imports/ui/components/global/SmartSlider.vue
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<v-slider
|
||||||
|
ref="input"
|
||||||
|
v-bind="$attrs"
|
||||||
|
class="dc-text-field"
|
||||||
|
:hide-details="!(errors && errors.length)"
|
||||||
|
:loading="loading"
|
||||||
|
:error-messages="errors"
|
||||||
|
:value="safeValue"
|
||||||
|
:disabled="isDisabled"
|
||||||
|
:outlined="!regular"
|
||||||
|
@change="change"
|
||||||
|
@focus="focused = true"
|
||||||
|
@blur="focused = false"
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<slot name="prepend" />
|
||||||
|
</template>
|
||||||
|
<template #append>
|
||||||
|
<slot name="append" />
|
||||||
|
</template>
|
||||||
|
</v-slider>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import SmartInput from '/imports/ui/components/global/SmartInputMixin.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [SmartInput],
|
||||||
|
props: {
|
||||||
|
regular: Boolean,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -5,17 +5,21 @@ import IconPicker from '/imports/ui/components/global/IconPicker.vue';
|
|||||||
import TextField from '/imports/ui/components/global/TextField.vue';
|
import TextField from '/imports/ui/components/global/TextField.vue';
|
||||||
import TextArea from '/imports/ui/components/global/TextArea.vue';
|
import TextArea from '/imports/ui/components/global/TextArea.vue';
|
||||||
import SmartSelect from '/imports/ui/components/global/SmartSelect.vue';
|
import SmartSelect from '/imports/ui/components/global/SmartSelect.vue';
|
||||||
|
import SmartBtn from '/imports/ui/components/global/SmartBtn.vue';
|
||||||
import SmartCombobox from '/imports/ui/components/global/SmartCombobox.vue';
|
import SmartCombobox from '/imports/ui/components/global/SmartCombobox.vue';
|
||||||
import SmartCheckbox from '/imports/ui/components/global/SmartCheckbox.vue';
|
import SmartCheckbox from '/imports/ui/components/global/SmartCheckbox.vue';
|
||||||
import SmartSwitch from '/imports/ui/components/global/SmartSwitch.vue';
|
import SmartSwitch from '/imports/ui/components/global/SmartSwitch.vue';
|
||||||
import SvgIcon from '/imports/ui/components/global/SvgIcon.vue';
|
import SvgIcon from '/imports/ui/components/global/SvgIcon.vue';
|
||||||
|
import SmartSlider from '/imports/ui/components/global/SmartSlider.vue';
|
||||||
|
|
||||||
Vue.component('DatePicker', DatePicker);
|
Vue.component('DatePicker', DatePicker);
|
||||||
Vue.component('IconPicker', IconPicker);
|
Vue.component('IconPicker', IconPicker);
|
||||||
Vue.component('TextField', TextField);
|
Vue.component('TextField', TextField);
|
||||||
Vue.component('TextArea', TextArea);
|
Vue.component('TextArea', TextArea);
|
||||||
Vue.component('SmartSelect', SmartSelect);
|
Vue.component('SmartSelect', SmartSelect);
|
||||||
|
Vue.component('SmartBtn', SmartBtn);
|
||||||
Vue.component('SmartCombobox', SmartCombobox);
|
Vue.component('SmartCombobox', SmartCombobox);
|
||||||
Vue.component('SmartCheckbox', SmartCheckbox);
|
Vue.component('SmartCheckbox', SmartCheckbox);
|
||||||
|
Vue.component('SmartSlider', SmartSlider);
|
||||||
Vue.component('SmartSwitch', SmartSwitch);
|
Vue.component('SmartSwitch', SmartSwitch);
|
||||||
Vue.component('SvgIcon', SvgIcon);
|
Vue.component('SvgIcon', SvgIcon);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
/>
|
/>
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
<v-btn
|
<v-btn
|
||||||
|
v-if="node.parent.id === parentSlotId"
|
||||||
icon
|
icon
|
||||||
:disabled="context.editPermission === false"
|
:disabled="context.editPermission === false"
|
||||||
@click.stop="remove(node)"
|
@click.stop="remove(node)"
|
||||||
@@ -91,6 +92,7 @@
|
|||||||
<build-tree-node-list
|
<build-tree-node-list
|
||||||
v-if="showExpanded"
|
v-if="showExpanded"
|
||||||
:children="computedChildren"
|
:children="computedChildren"
|
||||||
|
:parent-slot-id="computedSlotId"
|
||||||
@selected="e => $emit('selected', e)"
|
@selected="e => $emit('selected', e)"
|
||||||
/>
|
/>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
@@ -147,6 +149,10 @@ export default {
|
|||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
parentSlotId: {
|
||||||
|
type: String,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data(){return {
|
data(){return {
|
||||||
expanded: false,
|
expanded: false,
|
||||||
@@ -197,6 +203,21 @@ export default {
|
|||||||
}
|
}
|
||||||
return this.children;
|
return this.children;
|
||||||
},
|
},
|
||||||
|
computedSlotId() {
|
||||||
|
if (this.condenseChild) {
|
||||||
|
if (this.children[0].node.type === 'propertySlot') {
|
||||||
|
return this.children[0].node._id;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.node.type === 'propertySlot') {
|
||||||
|
return this.node._id;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
canExpand(){
|
canExpand(){
|
||||||
return !!this.computedChildren.length || this.canFillWithMany;
|
return !!this.computedChildren.length || this.canFillWithMany;
|
||||||
},
|
},
|
||||||
@@ -230,41 +251,41 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
.rotate-90 {
|
.rotate-90 {
|
||||||
transform: rotate(90deg) translateZ(0);
|
transform: rotate(90deg) translateZ(0);
|
||||||
}
|
}
|
||||||
.expand-area {
|
.expand-area {
|
||||||
box-shadow: -2px 0px 0px 0px #808080;
|
box-shadow: -2px 0px 0px 0px #808080;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
.handle {
|
.handle {
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
.empty .drag-area {
|
.empty .drag-area {
|
||||||
box-shadow: -2px 0px 0px 0px rgb(128, 128, 128, 0.4);
|
box-shadow: -2px 0px 0px 0px rgb(128, 128, 128, 0.4);
|
||||||
}
|
}
|
||||||
.empty .expand-button {
|
.empty .expand-button {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
.found {
|
.found {
|
||||||
background: rgba(200, 0, 0, 0.1) !important;
|
background: rgba(200, 0, 0, 0.1) !important;
|
||||||
}
|
}
|
||||||
.ghost {
|
.ghost {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
background: rgba(251, 0, 0, 0.3);
|
background: rgba(251, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
.v-icon.v-icon--disabled {
|
.v-icon.v-icon--disabled {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.v-icon {
|
.v-icon {
|
||||||
transition: none !important;
|
transition: none !important;
|
||||||
}
|
}
|
||||||
.theme--light .tree-node-title:hover {
|
.theme--light .tree-node-title:hover {
|
||||||
background-color: rgba(0,0,0,.04);
|
background-color: rgba(0,0,0,.04);
|
||||||
}
|
}
|
||||||
.theme--dark .tree-node-title:hover {
|
.theme--dark .tree-node-title:hover {
|
||||||
background-color: rgba(255,255,255,.04);
|
background-color: rgba(255,255,255,.04);
|
||||||
}
|
}
|
||||||
.tree-node-title{
|
.tree-node-title{
|
||||||
transition: background ease 0.3s, color ease 0.15s;
|
transition: background ease 0.3s, color ease 0.15s;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,26 +5,31 @@
|
|||||||
:key="child.node._id"
|
:key="child.node._id"
|
||||||
:node="child.node"
|
:node="child.node"
|
||||||
:children="child.children"
|
:children="child.children"
|
||||||
|
:parent-slot-id="parentSlotId"
|
||||||
@selected="e => $emit('selected', e)"
|
@selected="e => $emit('selected', e)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import BuildTreeNode from '/imports/ui/creature/buildTree/BuildTreeNode.vue';
|
import BuildTreeNode from '/imports/ui/creature/buildTree/BuildTreeNode.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
BuildTreeNode,
|
BuildTreeNode,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
children: {
|
children: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
},
|
parentSlotId: {
|
||||||
data(){ return {
|
type: String,
|
||||||
expanded: false,
|
default: undefined,
|
||||||
}},
|
},
|
||||||
};
|
},
|
||||||
|
data(){ return {
|
||||||
|
expanded: false,
|
||||||
|
}},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
Next
|
Next
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
:disabled="biographyAlert"
|
:disabled="!!biographyAlert"
|
||||||
:text="step < 2"
|
:text="step < 2"
|
||||||
:color="step < 2? '' : 'accent'"
|
:color="step < 2? '' : 'accent'"
|
||||||
@click="submit"
|
@click="submit"
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
<v-menu
|
<v-menu
|
||||||
bottom
|
bottom
|
||||||
left
|
left
|
||||||
|
transition="slide-y-transition"
|
||||||
>
|
>
|
||||||
<template #activator="{ on }">
|
<template #activator="{ on }">
|
||||||
<v-badge
|
<v-badge
|
||||||
@@ -48,12 +49,21 @@
|
|||||||
<v-icon class="mr-2">
|
<v-icon class="mr-2">
|
||||||
mdi-file-hidden
|
mdi-file-hidden
|
||||||
</v-icon>
|
</v-icon>
|
||||||
{{ hiddenCount }} hidden {{ hiddenCount > 1 ? 'slots' : 'slot' }}
|
{{ hiddenCount }} hidden {{ hiddenCount > 1 ? 'properties' : 'property' }}
|
||||||
</v-subheader>
|
</v-subheader>
|
||||||
|
<v-list-item
|
||||||
|
v-for="pointBuy in hiddenPointBuys"
|
||||||
|
:key="pointBuy._id"
|
||||||
|
@click="unhideProp(pointBuy._id)"
|
||||||
|
>
|
||||||
|
<v-list-item-title>
|
||||||
|
{{ getPropertyTitle(pointBuy) }}
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
<v-list-item
|
<v-list-item
|
||||||
v-for="slot in hiddenSlots"
|
v-for="slot in hiddenSlots"
|
||||||
:key="slot._id"
|
:key="slot._id"
|
||||||
@click="unhideSlot(slot._id)"
|
@click="unhideProp(slot._id)"
|
||||||
>
|
>
|
||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
{{ getPropertyTitle(slot) }}
|
{{ getPropertyTitle(slot) }}
|
||||||
@@ -226,7 +236,7 @@ export default {
|
|||||||
].sort((a, b) => a.order - b.order);
|
].sort((a, b) => a.order - b.order);
|
||||||
},
|
},
|
||||||
hiddenCount() {
|
hiddenCount() {
|
||||||
return this.hiddenSlots.length;
|
return this.hiddenSlots.length + this.hiddenPointBuys.length;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
@@ -236,6 +246,16 @@ export default {
|
|||||||
variables() {
|
variables() {
|
||||||
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
||||||
},
|
},
|
||||||
|
hiddenPointBuys() {
|
||||||
|
return CreatureProperties.find({
|
||||||
|
type: 'pointBuy',
|
||||||
|
'ancestors.id': this.creatureId,
|
||||||
|
ignored: true,
|
||||||
|
pointsLeft: {$ne: 0},
|
||||||
|
removed: {$ne: true},
|
||||||
|
inactive: {$ne: true},
|
||||||
|
}).fetch();
|
||||||
|
},
|
||||||
hiddenSlots(){
|
hiddenSlots(){
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
type: 'propertySlot',
|
type: 'propertySlot',
|
||||||
@@ -284,7 +304,7 @@ export default {
|
|||||||
slotBuildTree(){
|
slotBuildTree(){
|
||||||
const slots = CreatureProperties.find({
|
const slots = CreatureProperties.find({
|
||||||
'ancestors.id': this.creatureId,
|
'ancestors.id': this.creatureId,
|
||||||
type: 'propertySlot',
|
type: {$in: ['propertySlot', 'pointBuy']},
|
||||||
$or: [
|
$or: [
|
||||||
{'slotCondition.value': {$nin: [false, 0, '']}},
|
{'slotCondition.value': {$nin: [false, 0, '']}},
|
||||||
{'slotCondition.value': {$exists: false}},
|
{'slotCondition.value': {$exists: false}},
|
||||||
@@ -308,16 +328,15 @@ export default {
|
|||||||
]);
|
]);
|
||||||
traverse(tree, (child, parents) => {
|
traverse(tree, (child, parents) => {
|
||||||
const model = child.node;
|
const model = child.node;
|
||||||
const isSlotWithSpace = model.type === 'propertySlot' &&
|
const isSlotWithSpace = model.type === 'propertySlot' && (
|
||||||
model.spaceLeft > 0 ||
|
model.spaceLeft > 0 ||
|
||||||
!model.quantityExpected ||
|
!model.quantityExpected ||
|
||||||
model.quantityExpected.value === 0;
|
model.quantityExpected.value === 0
|
||||||
|
);
|
||||||
if(isSlotWithSpace) {
|
if(isSlotWithSpace) {
|
||||||
model._canFill = true;
|
model._canFill = true;
|
||||||
parents.forEach(node => {
|
parents.forEach(node => {
|
||||||
if (node.node.type === 'propertySlot'){
|
node.node._descendantCanFill = true;
|
||||||
node.node._descendantCanFill = true;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -385,7 +404,7 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
getPropertyTitle,
|
getPropertyTitle,
|
||||||
unhideSlot(_id) {
|
unhideProp(_id) {
|
||||||
updateCreatureProperty.call({
|
updateCreatureProperty.call({
|
||||||
_id,
|
_id,
|
||||||
path: ['ignored'],
|
path: ['ignored'],
|
||||||
|
|||||||
@@ -5,6 +5,18 @@
|
|||||||
leave-absolute
|
leave-absolute
|
||||||
hide-on-leave
|
hide-on-leave
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-for="pointBuy in pointBuys"
|
||||||
|
:key="pointBuy._id"
|
||||||
|
style="transition: all 0.3s !important"
|
||||||
|
>
|
||||||
|
<point-buy-card
|
||||||
|
:model="pointBuy"
|
||||||
|
hover
|
||||||
|
@ignore="ignoreProp(pointBuy._id)"
|
||||||
|
@click="editPointBuy(pointBuy._id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="slot in slots"
|
v-for="slot in slots"
|
||||||
:key="slot._id"
|
:key="slot._id"
|
||||||
@@ -13,7 +25,7 @@
|
|||||||
<slot-card
|
<slot-card
|
||||||
:model="slot"
|
:model="slot"
|
||||||
hover
|
hover
|
||||||
@ignore="ignoreSlot(slot._id)"
|
@ignore="ignoreProp(slot._id)"
|
||||||
@click="fillSlot(slot._id)"
|
@click="fillSlot(slot._id)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -24,6 +36,7 @@
|
|||||||
<script lang="js">
|
<script lang="js">
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import SlotCard from '/imports/ui/creature/slots/SlotCard.vue';
|
import SlotCard from '/imports/ui/creature/slots/SlotCard.vue';
|
||||||
|
import PointBuyCard from '/imports/ui/properties/components/pointBuy/PointBuyCard.vue';
|
||||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||||
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
|
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
|
||||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||||
@@ -32,13 +45,14 @@ import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SlotCard,
|
SlotCard,
|
||||||
|
PointBuyCard,
|
||||||
ColumnLayout,
|
ColumnLayout,
|
||||||
},
|
},
|
||||||
inject: {
|
inject: {
|
||||||
context: { default: {} }
|
context: { default: {} }
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
ignoreSlot(_id){
|
ignoreProp(_id){
|
||||||
updateCreatureProperty.call({
|
updateCreatureProperty.call({
|
||||||
_id,
|
_id,
|
||||||
path: ['ignored'],
|
path: ['ignored'],
|
||||||
@@ -75,6 +89,16 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
editPointBuy(_id){
|
||||||
|
this.$store.commit('pushDialogStack', {
|
||||||
|
component: 'creature-property-dialog',
|
||||||
|
elementId: `point-buy-card-${_id}`,
|
||||||
|
data: {
|
||||||
|
_id,
|
||||||
|
startInEditTab: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
slots(){
|
slots(){
|
||||||
@@ -99,7 +123,16 @@ export default {
|
|||||||
removed: {$ne: true},
|
removed: {$ne: true},
|
||||||
inactive: {$ne: true},
|
inactive: {$ne: true},
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
pointBuys(){
|
||||||
|
return CreatureProperties.find({
|
||||||
|
type: 'pointBuy',
|
||||||
|
'ancestors.id': this.context.creatureId,
|
||||||
|
ignored: { $ne: true },
|
||||||
|
removed: {$ne: true},
|
||||||
|
inactive: {$ne: true},
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
import SingleCardLayout from '/imports/ui/layouts/SingleCardLayout.vue'
|
import SingleCardLayout from '/imports/ui/layouts/SingleCardLayout.vue'
|
||||||
import Tabletops from '/imports/api/tabletop/Tabletops.js';
|
import Tabletops from '/imports/api/tabletop/Tabletops.js';
|
||||||
import insertTabletop from '/imports/api/tabletop/methods/insertTabletop.js';
|
import insertTabletop from '/imports/api/tabletop/methods/insertTabletop.js';
|
||||||
import snackbar from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@@ -27,9 +27,9 @@
|
|||||||
<div class="text-body-1 mb-1">
|
<div class="text-body-1 mb-1">
|
||||||
{{ displayedText }}
|
{{ displayedText }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!hideBreadcrumbs && model.ancestors">
|
<div v-if="!hideBreadcrumbs && ancestors">
|
||||||
<breadcrumbs
|
<breadcrumbs
|
||||||
:model="model"
|
:model="{...model, ancestors}"
|
||||||
class="text-caption"
|
class="text-caption"
|
||||||
no-links
|
no-links
|
||||||
no-icons
|
no-icons
|
||||||
@@ -41,94 +41,101 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import getEffectIcon from '/imports/ui/utility/getEffectIcon.js';
|
import getEffectIcon from '/imports/ui/utility/getEffectIcon.js';
|
||||||
import Breadcrumbs from '/imports/ui/creature/creatureProperties/Breadcrumbs.vue';
|
import Breadcrumbs from '/imports/ui/creature/creatureProperties/Breadcrumbs.vue';
|
||||||
import { isFinite } from 'lodash';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
|
import { isFinite } from 'lodash';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
hideBreadcrumbs: Boolean,
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
props: {
|
},
|
||||||
hideBreadcrumbs: Boolean,
|
computed: {
|
||||||
model: {
|
hasClickListener(){
|
||||||
type: Object,
|
return this.$listeners && this.$listeners.click
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
computed: {
|
displayedText(){
|
||||||
hasClickListener(){
|
if (this.model.operation === 'conditional'){
|
||||||
return this.$listeners && this.$listeners.click
|
return this.model.text || this.model.name || this.operation
|
||||||
},
|
} else {
|
||||||
displayedText(){
|
return this.model.name || this.operation
|
||||||
if (this.model.operation === 'conditional'){
|
|
||||||
return this.model.text || this.model.name || this.operation
|
|
||||||
} else {
|
|
||||||
return this.model.name || this.operation
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resolvedValue(){
|
|
||||||
let amount = this.model.amount;
|
|
||||||
if (!amount) return;
|
|
||||||
return amount.value !== undefined ? amount.value : amount.calculation;
|
|
||||||
},
|
|
||||||
effectIcon(){
|
|
||||||
let value = this.resolvedValue;
|
|
||||||
return getEffectIcon(this.model.operation, value);
|
|
||||||
},
|
|
||||||
operation(){
|
|
||||||
switch(this.model.operation) {
|
|
||||||
case 'base': return 'Base value';
|
|
||||||
case 'add': return 'Add';
|
|
||||||
case 'mul': return 'Multiply';
|
|
||||||
case 'min': return 'Minimum';
|
|
||||||
case 'max': return 'Maximum';
|
|
||||||
case 'advantage': return 'Advantage';
|
|
||||||
case 'disadvantage': return 'Disadvantage';
|
|
||||||
case 'passiveAdd': return 'Passive bonus';
|
|
||||||
case 'fail': return 'Always fail';
|
|
||||||
case 'conditional': return 'Conditional benefit' ;
|
|
||||||
default: return '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
showValue(){
|
|
||||||
switch(this.model.operation) {
|
|
||||||
case 'base': return true;
|
|
||||||
case 'add': return true;
|
|
||||||
case 'mul': return true;
|
|
||||||
case 'min': return true;
|
|
||||||
case 'max': return true;
|
|
||||||
case 'advantage': return false;
|
|
||||||
case 'disadvantage': return false;
|
|
||||||
case 'passiveAdd': return true;
|
|
||||||
case 'fail': return false;
|
|
||||||
case 'conditional': return false;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
displayedValue(){
|
|
||||||
let value = this.resolvedValue;
|
|
||||||
switch(this.model.operation) {
|
|
||||||
case 'base': return value;
|
|
||||||
case 'add': return isFinite(value) ? Math.abs(value) : value;
|
|
||||||
case 'mul': return value;
|
|
||||||
case 'min': return value;
|
|
||||||
case 'max': return value;
|
|
||||||
case 'advantage': return;
|
|
||||||
case 'disadvantage': return;
|
|
||||||
case 'passiveAdd': return isFinite(value) ? Math.abs(value) : value;
|
|
||||||
case 'fail': return;
|
|
||||||
case 'conditional': return undefined;
|
|
||||||
default: return undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
resolvedValue() {
|
||||||
click(e){
|
let amount = this.model.amount;
|
||||||
this.$emit('click', e);
|
if (!amount) return;
|
||||||
},
|
return amount.value !== undefined ? amount.value : amount.calculation;
|
||||||
},
|
},
|
||||||
};
|
effectIcon(){
|
||||||
|
let value = this.resolvedValue;
|
||||||
|
return getEffectIcon(this.model.operation, value);
|
||||||
|
},
|
||||||
|
operation(){
|
||||||
|
switch(this.model.operation) {
|
||||||
|
case 'base': return 'Base value';
|
||||||
|
case 'add': return 'Add';
|
||||||
|
case 'mul': return 'Multiply';
|
||||||
|
case 'min': return 'Minimum';
|
||||||
|
case 'max': return 'Maximum';
|
||||||
|
case 'advantage': return 'Advantage';
|
||||||
|
case 'disadvantage': return 'Disadvantage';
|
||||||
|
case 'passiveAdd': return 'Passive bonus';
|
||||||
|
case 'fail': return 'Always fail';
|
||||||
|
case 'conditional': return 'Conditional benefit' ;
|
||||||
|
default: return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showValue(){
|
||||||
|
switch(this.model.operation) {
|
||||||
|
case 'base': return true;
|
||||||
|
case 'add': return true;
|
||||||
|
case 'mul': return true;
|
||||||
|
case 'min': return true;
|
||||||
|
case 'max': return true;
|
||||||
|
case 'advantage': return false;
|
||||||
|
case 'disadvantage': return false;
|
||||||
|
case 'passiveAdd': return true;
|
||||||
|
case 'fail': return false;
|
||||||
|
case 'conditional': return false;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
displayedValue(){
|
||||||
|
let value = this.resolvedValue;
|
||||||
|
switch(this.model.operation) {
|
||||||
|
case 'base': return value;
|
||||||
|
case 'add': return isFinite(value) ? Math.abs(value) : value;
|
||||||
|
case 'mul': return value;
|
||||||
|
case 'min': return value;
|
||||||
|
case 'max': return value;
|
||||||
|
case 'advantage': return;
|
||||||
|
case 'disadvantage': return;
|
||||||
|
case 'passiveAdd': return isFinite(value) ? Math.abs(value) : value;
|
||||||
|
case 'fail': return;
|
||||||
|
case 'conditional': return undefined;
|
||||||
|
default: return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
meteor: {
|
||||||
|
ancestors() {
|
||||||
|
const prop = CreatureProperties.findOne(this.model._id);
|
||||||
|
return prop && prop.ancestors || [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
click(e){
|
||||||
|
this.$emit('click', e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<v-card
|
||||||
|
v-if="model"
|
||||||
|
v-bind="$attrs"
|
||||||
|
:data-id="`point-buy-card-${model._id}`"
|
||||||
|
:style="`border: solid 1px ${accentColor};`"
|
||||||
|
hover
|
||||||
|
class="slot-card d-flex flex-column"
|
||||||
|
@mouseover="hover = true"
|
||||||
|
@mouseleave="hover = false"
|
||||||
|
@click="$emit('click')"
|
||||||
|
>
|
||||||
|
<card-highlight
|
||||||
|
:active="hover"
|
||||||
|
/>
|
||||||
|
<v-card-title>
|
||||||
|
{{ model.name || 'Point Buy' }}
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
{{ model.spent }}
|
||||||
|
<template v-if="model.total && (typeof model.total.value === 'number')">
|
||||||
|
/ {{ model.total && model.total.value }}
|
||||||
|
</template>
|
||||||
|
</v-card-text>
|
||||||
|
<v-spacer />
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
color="accent"
|
||||||
|
@click.stop="$emit('ignore')"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-close</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import CardHighlight from '/imports/ui/components/CardHighlight.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CardHighlight,
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
theme: {
|
||||||
|
default: {
|
||||||
|
isDark: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data(){ return {
|
||||||
|
hover: false,
|
||||||
|
}},
|
||||||
|
computed: {
|
||||||
|
accentColor(){
|
||||||
|
if (this.theme.isDark){
|
||||||
|
return this.$vuetify.theme.themes.dark.primary;
|
||||||
|
} else {
|
||||||
|
return this.$vuetify.theme.themes.light.primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<v-list-item-title class="d-flex align-center">
|
<v-list-item-title class="d-flex align-center">
|
||||||
<roll-popup
|
<roll-popup
|
||||||
v-if="!hideModifier"
|
v-if="!hideModifier"
|
||||||
class="prof-mod mr-1"
|
class="prof-mod mr-1 flex-shrink-0"
|
||||||
button-class="pl-3 pr-2"
|
button-class="pl-3 pr-2"
|
||||||
text
|
text
|
||||||
:roll-text="displayedModifier"
|
:roll-text="displayedModifier"
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
:value="model.proficiency"
|
:value="model.proficiency"
|
||||||
class="prof-icon ml-3 mr-2"
|
class="prof-icon ml-3 mr-2"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div class="text-truncate">
|
||||||
{{ model.name }}
|
{{ model.name }}
|
||||||
<template v-if="model.conditionalBenefits && model.conditionalBenefits.length">
|
<template v-if="model.conditionalBenefits && model.conditionalBenefits.length">
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -141,6 +141,18 @@
|
|||||||
@change="change('usesUsed', ...arguments)"
|
@change="change('usesUsed', ...arguments)"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
sm="6"
|
||||||
|
md="4"
|
||||||
|
>
|
||||||
|
<smart-switch
|
||||||
|
label="Don't show in log"
|
||||||
|
:value="model.silent"
|
||||||
|
:error-messages="errors.silent"
|
||||||
|
@change="change('silent', ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
<smart-select
|
<smart-select
|
||||||
label="Reset"
|
label="Reset"
|
||||||
|
|||||||
@@ -59,6 +59,12 @@
|
|||||||
:value="model.tags"
|
:value="model.tags"
|
||||||
@change="change('tags', ...arguments)"
|
@change="change('tags', ...arguments)"
|
||||||
/>
|
/>
|
||||||
|
<smart-switch
|
||||||
|
label="Don't show in log"
|
||||||
|
:value="model.silent"
|
||||||
|
:error-messages="errors.silent"
|
||||||
|
@change="change('silent', ...arguments)"
|
||||||
|
/>
|
||||||
<form-section
|
<form-section
|
||||||
v-if="$slots.children"
|
v-if="$slots.children"
|
||||||
name="Children"
|
name="Children"
|
||||||
|
|||||||
@@ -38,6 +38,12 @@
|
|||||||
:value="model.tags"
|
:value="model.tags"
|
||||||
@change="change('tags', ...arguments)"
|
@change="change('tags', ...arguments)"
|
||||||
/>
|
/>
|
||||||
|
<smart-switch
|
||||||
|
label="Don't show in log"
|
||||||
|
:value="model.silent"
|
||||||
|
:error-messages="errors.silent"
|
||||||
|
@change="change('silent', ...arguments)"
|
||||||
|
/>
|
||||||
<form-section
|
<form-section
|
||||||
name="Children"
|
name="Children"
|
||||||
standalone
|
standalone
|
||||||
|
|||||||
@@ -47,12 +47,44 @@
|
|||||||
<form-section
|
<form-section
|
||||||
name="Advanced"
|
name="Advanced"
|
||||||
>
|
>
|
||||||
<smart-switch
|
<v-row dense>
|
||||||
label="Hide remove button"
|
<v-col
|
||||||
:value="model.hideRemoveButton"
|
cols="12"
|
||||||
:error-messages="errors.hideRemoveButton"
|
sm="6"
|
||||||
@change="change('hideRemoveButton', ...arguments)"
|
md="4"
|
||||||
/>
|
>
|
||||||
|
<smart-switch
|
||||||
|
label="Hide remove button"
|
||||||
|
:value="model.hideRemoveButton"
|
||||||
|
:error-messages="errors.hideRemoveButton"
|
||||||
|
@change="change('hideRemoveButton', ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
sm="6"
|
||||||
|
md="4"
|
||||||
|
>
|
||||||
|
<smart-switch
|
||||||
|
label="Don't show in log"
|
||||||
|
:value="model.silent"
|
||||||
|
:error-messages="errors.silent"
|
||||||
|
@change="change('silent', ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
sm="6"
|
||||||
|
md="4"
|
||||||
|
>
|
||||||
|
<smart-switch
|
||||||
|
label="Don't freeze variables"
|
||||||
|
:value="model.skipCrystalization"
|
||||||
|
:error-messages="errors.skipCrystalization"
|
||||||
|
@change="change('skipCrystalization', ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
<smart-combobox
|
<smart-combobox
|
||||||
label="Tags"
|
label="Tags"
|
||||||
multiple
|
multiple
|
||||||
|
|||||||
@@ -115,6 +115,20 @@
|
|||||||
:value="model.tags"
|
:value="model.tags"
|
||||||
@change="change('tags', ...arguments)"
|
@change="change('tags', ...arguments)"
|
||||||
/>
|
/>
|
||||||
|
<v-row dense>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
sm="6"
|
||||||
|
md="4"
|
||||||
|
>
|
||||||
|
<smart-switch
|
||||||
|
label="Don't show in log"
|
||||||
|
:value="model.silent"
|
||||||
|
:error-messages="errors.silent"
|
||||||
|
@change="change('silent', ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
</form-section>
|
</form-section>
|
||||||
</form-sections>
|
</form-sections>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -53,6 +53,12 @@
|
|||||||
:error-messages="errors.tags"
|
:error-messages="errors.tags"
|
||||||
@change="change('tags', ...arguments)"
|
@change="change('tags', ...arguments)"
|
||||||
/>
|
/>
|
||||||
|
<smart-switch
|
||||||
|
label="Don't show in log"
|
||||||
|
:value="model.silent"
|
||||||
|
:error-messages="errors.silent"
|
||||||
|
@change="change('silent', ...arguments)"
|
||||||
|
/>
|
||||||
<form-section
|
<form-section
|
||||||
v-if="$slots.children"
|
v-if="$slots.children"
|
||||||
name="Children"
|
name="Children"
|
||||||
|
|||||||
219
app/imports/ui/properties/forms/PointBuyForm.vue
Normal file
219
app/imports/ui/properties/forms/PointBuyForm.vue
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<div class="point-buy-form">
|
||||||
|
<point-buy-spend-form
|
||||||
|
:model="model"
|
||||||
|
@change="e => $emit('change', e)"
|
||||||
|
@push="e => $emit('change', e)"
|
||||||
|
@pull="e => $emit('change', e)"
|
||||||
|
/>
|
||||||
|
<form-sections>
|
||||||
|
<form-section name="Settings">
|
||||||
|
<v-row dense>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
md="6"
|
||||||
|
>
|
||||||
|
<text-field
|
||||||
|
ref="focusFirst"
|
||||||
|
label="Table name"
|
||||||
|
:value="model.name"
|
||||||
|
:error-messages="errors.name"
|
||||||
|
@change="change('name', ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
md="6"
|
||||||
|
>
|
||||||
|
<computed-field
|
||||||
|
label="Min"
|
||||||
|
hint="The minimum value for each row"
|
||||||
|
:model="model.min"
|
||||||
|
:error-messages="errors.min"
|
||||||
|
@change="({path, value, ack}) =>
|
||||||
|
$emit('change', {path: ['min', ...path], value, ack})"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
md="6"
|
||||||
|
>
|
||||||
|
<computed-field
|
||||||
|
label="Max"
|
||||||
|
hint="The maximum value for each row"
|
||||||
|
:model="model.max"
|
||||||
|
:error-messages="errors.max"
|
||||||
|
@change="({path, value, ack}) =>
|
||||||
|
$emit('change', {path: ['max', ...path], value, ack})"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
md="6"
|
||||||
|
>
|
||||||
|
<computed-field
|
||||||
|
label="Cost"
|
||||||
|
hint="A function of `value` that determines the cost of each row"
|
||||||
|
hide-value
|
||||||
|
:model="model.cost"
|
||||||
|
:error-messages="errors.cost"
|
||||||
|
@change="({path, value, ack}) =>
|
||||||
|
$emit('change', {path: ['cost', ...path], value, ack})"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
md="6"
|
||||||
|
>
|
||||||
|
<computed-field
|
||||||
|
label="Total available points"
|
||||||
|
hint="The total allowed cost of all rows"
|
||||||
|
:model="model.total"
|
||||||
|
:error-messages="errors.total"
|
||||||
|
@change="({path, value, ack}) =>
|
||||||
|
$emit('change', {path: ['total', ...path], value, ack})"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</form-section>
|
||||||
|
<form-section name="Rows">
|
||||||
|
<v-slide-x-transition
|
||||||
|
group
|
||||||
|
leave-absolute
|
||||||
|
>
|
||||||
|
<v-row
|
||||||
|
v-for="(row, i) in model.values"
|
||||||
|
:key="row._id"
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
<v-divider
|
||||||
|
v-if="i"
|
||||||
|
style="flex-basis: 100%;"
|
||||||
|
class="mb-6"
|
||||||
|
/>
|
||||||
|
<v-col cols="11">
|
||||||
|
<v-row dense>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
md="6"
|
||||||
|
>
|
||||||
|
<text-field
|
||||||
|
ref="focusFirst"
|
||||||
|
label="Row Name"
|
||||||
|
:value="row.name"
|
||||||
|
:error-messages="errors.values && errors.values[i] && errors.values[i].name"
|
||||||
|
@change="change(['values', i, 'name'], ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
md="6"
|
||||||
|
>
|
||||||
|
<text-field
|
||||||
|
label="Variable name"
|
||||||
|
:value="row.variableName"
|
||||||
|
hint="Use this name in calculations to reference this row of the table"
|
||||||
|
:error-messages="errors.values && errors.values[i] && errors.values[i].variableName"
|
||||||
|
@change="change(['values', i, 'variableName'], ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
v-if="row.errors && row.errors.length"
|
||||||
|
cols="12"
|
||||||
|
>
|
||||||
|
<calculation-error-list
|
||||||
|
:errors="row.errors"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="1"
|
||||||
|
class="d-flex align-center justify-center"
|
||||||
|
>
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
large
|
||||||
|
@click="$emit('pull', {path: ['values', i]})"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-delete</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row
|
||||||
|
key="addButton"
|
||||||
|
dense
|
||||||
|
justify="end"
|
||||||
|
class="mb-4"
|
||||||
|
>
|
||||||
|
<v-col
|
||||||
|
cols="1"
|
||||||
|
class="d-flex justify-center"
|
||||||
|
>
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
outlined
|
||||||
|
:loading="addRowLoading"
|
||||||
|
:disabled="rowsFull"
|
||||||
|
@click="addRow"
|
||||||
|
>
|
||||||
|
<v-icon>
|
||||||
|
mdi-plus
|
||||||
|
</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-slide-x-transition>
|
||||||
|
</form-section>
|
||||||
|
<form-section
|
||||||
|
v-if="$slots.children"
|
||||||
|
name="Children"
|
||||||
|
>
|
||||||
|
<slot name="children" />
|
||||||
|
</form-section>
|
||||||
|
</form-sections>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import attributeListMixin from '/imports/ui/properties/forms/shared/lists/attributeListMixin.js';
|
||||||
|
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
|
||||||
|
import { PointBuySchema } from '/imports/api/properties/PointBuys.js';
|
||||||
|
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
|
||||||
|
import PointBuySpendForm from '/imports/ui/properties/forms/PointBuySpendForm.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CalculationErrorList,
|
||||||
|
PointBuySpendForm,
|
||||||
|
},
|
||||||
|
mixins: [propertyFormMixin, attributeListMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
addRowLoading: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
rowsFull(){
|
||||||
|
if (!this.model.values) return false;
|
||||||
|
let maxCount = PointBuySchema.get('values', 'maxCount');
|
||||||
|
return this.model.values.length >= maxCount;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
acknowledgeAddResult(){
|
||||||
|
this.addRowLoading = false;
|
||||||
|
},
|
||||||
|
addRow(){
|
||||||
|
this.addRowLoading = true;
|
||||||
|
this.$emit('push', {
|
||||||
|
path: ['values'],
|
||||||
|
value: {
|
||||||
|
_id: Random.id(),
|
||||||
|
},
|
||||||
|
ack: this.acknowledgeAddResult,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
108
app/imports/ui/properties/forms/PointBuySpendForm.vue
Normal file
108
app/imports/ui/properties/forms/PointBuySpendForm.vue
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<div class="point-buy-spend-form">
|
||||||
|
<v-row
|
||||||
|
v-if="model.values && model.values.length"
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
<v-col
|
||||||
|
cols="10"
|
||||||
|
md="11"
|
||||||
|
/>
|
||||||
|
<v-col
|
||||||
|
cols="2"
|
||||||
|
md="1"
|
||||||
|
class="text-truncate d-flex justify-center"
|
||||||
|
>
|
||||||
|
Cost
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row
|
||||||
|
v-for="(row, i) in model.values"
|
||||||
|
:key="row._id"
|
||||||
|
dense
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<v-col
|
||||||
|
cols="12"
|
||||||
|
md="2"
|
||||||
|
class="d-flex justify-md-end"
|
||||||
|
>
|
||||||
|
{{ row.name }}
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="2"
|
||||||
|
md="1"
|
||||||
|
class="d-flex justify-md-center justify-end"
|
||||||
|
>
|
||||||
|
{{ row.value }}
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="8"
|
||||||
|
>
|
||||||
|
<smart-slider
|
||||||
|
thumb-label
|
||||||
|
dense
|
||||||
|
:ticks="max(row) - min(row) <= 20"
|
||||||
|
:min="min(row)"
|
||||||
|
:max="max(row)"
|
||||||
|
:value="row.value"
|
||||||
|
:error-messages="errors.values && errors.values[i] && errors.values[i].value"
|
||||||
|
@change="(value, ack) => $emit('change', {path: ['values', i, 'value'], value, ack})"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
cols="2"
|
||||||
|
md="1"
|
||||||
|
class="text-truncate d-flex justify-center"
|
||||||
|
>
|
||||||
|
{{ row.spent }}
|
||||||
|
</v-col>
|
||||||
|
<v-col
|
||||||
|
v-if="row.errors && row.errors.length"
|
||||||
|
cols="12"
|
||||||
|
>
|
||||||
|
<calculation-error-list
|
||||||
|
:errors="row.errors"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
<v-col
|
||||||
|
v-if="typeof model.spent === 'number'"
|
||||||
|
cols="12"
|
||||||
|
class="text-h4 mb-4 pr-8 d-flex justify-end"
|
||||||
|
:class="{
|
||||||
|
'error--text': model.spent > (model.total && model.total.value),
|
||||||
|
'warning--text': model.spent < (model.total && model.total.value),
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ model.spent }}
|
||||||
|
<template v-if="model.total && (typeof model.total.value === 'number')">
|
||||||
|
/ {{ model.total && model.total.value }}
|
||||||
|
</template>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
|
||||||
|
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CalculationErrorList,
|
||||||
|
},
|
||||||
|
mixins: [propertyFormMixin],
|
||||||
|
methods: {
|
||||||
|
max(row) {
|
||||||
|
return row.max ? row.max && row.max.value : this.model.max && this.model.max.value;
|
||||||
|
},
|
||||||
|
min(row) {
|
||||||
|
return row.min ? row.min && row.min.value : this.model.min && this.model.min.value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -65,6 +65,12 @@
|
|||||||
:error-messages="errors.tags"
|
:error-messages="errors.tags"
|
||||||
@change="change('tags', ...arguments)"
|
@change="change('tags', ...arguments)"
|
||||||
/>
|
/>
|
||||||
|
<smart-switch
|
||||||
|
label="Don't show in log"
|
||||||
|
:value="model.silent"
|
||||||
|
:error-messages="errors.silent"
|
||||||
|
@change="change('silent', ...arguments)"
|
||||||
|
/>
|
||||||
<form-section
|
<form-section
|
||||||
v-if="$slots.children"
|
v-if="$slots.children"
|
||||||
name="Children"
|
name="Children"
|
||||||
|
|||||||
@@ -140,6 +140,12 @@
|
|||||||
:value="model.tags"
|
:value="model.tags"
|
||||||
@change="change('tags', ...arguments)"
|
@change="change('tags', ...arguments)"
|
||||||
/>
|
/>
|
||||||
|
<smart-switch
|
||||||
|
label="Don't show in log"
|
||||||
|
:value="model.silent"
|
||||||
|
:error-messages="errors.silent"
|
||||||
|
@change="change('silent', ...arguments)"
|
||||||
|
/>
|
||||||
</form-section>
|
</form-section>
|
||||||
</form-sections>
|
</form-sections>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -73,5 +73,8 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css">
|
||||||
|
.error-list .v-alert__content{
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
@change="(value, ack) => $emit('change', {path: ['calculation'], value, ack})"
|
@change="(value, ack) => $emit('change', {path: ['calculation'], value, ack})"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-if="model.value !== undefined || model.value !== null"
|
v-if="showValue"
|
||||||
#value
|
#value
|
||||||
>
|
>
|
||||||
{{ model.value }}
|
{{ model.value }}
|
||||||
@@ -28,8 +28,20 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
hideValue: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
showValue() {
|
||||||
|
const value = this.model.value;
|
||||||
|
if (
|
||||||
|
this.hideValue ||
|
||||||
|
(value === undefined || value === null) ||
|
||||||
|
value == this.model.calculation
|
||||||
|
) return false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
errorList(){
|
errorList(){
|
||||||
if (this.model.parseError){
|
if (this.model.parseError){
|
||||||
return [this.model.parseError, ...this.model.errors];
|
return [this.model.parseError, ...this.model.errors];
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<v-expansion-panel-header>
|
<v-expansion-panel-header>
|
||||||
{{ name }}
|
{{ name }}
|
||||||
</v-expansion-panel-header>
|
</v-expansion-panel-header>
|
||||||
<v-expansion-panel-content>
|
<v-expansion-panel-content class="pt-2">
|
||||||
<slot />
|
<slot />
|
||||||
</v-expansion-panel-content>
|
</v-expansion-panel-content>
|
||||||
</v-expansion-panel>
|
</v-expansion-panel>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<v-expansion-panel-header>
|
<v-expansion-panel-header>
|
||||||
{{ name }}
|
{{ name }}
|
||||||
</v-expansion-panel-header>
|
</v-expansion-panel-header>
|
||||||
<v-expansion-panel-content>
|
<v-expansion-panel-content class="pt-2">
|
||||||
<slot />
|
<slot />
|
||||||
</v-expansion-panel-content>
|
</v-expansion-panel-content>
|
||||||
</v-expansion-panel>
|
</v-expansion-panel>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const FeatureForm = () => import('/imports/ui/properties/forms/FeatureForm.vue')
|
|||||||
const FolderForm = () => import('/imports/ui/properties/forms/FolderForm.vue');
|
const FolderForm = () => import('/imports/ui/properties/forms/FolderForm.vue');
|
||||||
const ItemForm = () => import('/imports/ui/properties/forms/ItemForm.vue');
|
const ItemForm = () => import('/imports/ui/properties/forms/ItemForm.vue');
|
||||||
const NoteForm = () => import('/imports/ui/properties/forms/NoteForm.vue');
|
const NoteForm = () => import('/imports/ui/properties/forms/NoteForm.vue');
|
||||||
|
const PointBuyForm = () => import('/imports/ui/properties/forms/PointBuyForm.vue');
|
||||||
const ProficiencyForm = () => import('/imports/ui/properties/forms/ProficiencyForm.vue');
|
const ProficiencyForm = () => import('/imports/ui/properties/forms/ProficiencyForm.vue');
|
||||||
const ReferenceForm = () => import('/imports/ui/properties/forms/ReferenceForm.vue');
|
const ReferenceForm = () => import('/imports/ui/properties/forms/ReferenceForm.vue');
|
||||||
const RollForm = () => import('/imports/ui/properties/forms/RollForm.vue');
|
const RollForm = () => import('/imports/ui/properties/forms/RollForm.vue');
|
||||||
@@ -45,6 +46,7 @@ export default {
|
|||||||
folder: FolderForm,
|
folder: FolderForm,
|
||||||
item: ItemForm,
|
item: ItemForm,
|
||||||
note: NoteForm,
|
note: NoteForm,
|
||||||
|
pointBuy: PointBuyForm,
|
||||||
proficiency: ProficiencyForm,
|
proficiency: ProficiencyForm,
|
||||||
propertySlot: SlotForm,
|
propertySlot: SlotForm,
|
||||||
reference: ReferenceForm,
|
reference: ReferenceForm,
|
||||||
|
|||||||
@@ -107,25 +107,18 @@
|
|||||||
</v-row>
|
</v-row>
|
||||||
<v-row dense>
|
<v-row dense>
|
||||||
<property-field
|
<property-field
|
||||||
v-if="baseEffects.length || effects.length"
|
v-if="effects && effects.length"
|
||||||
:cols="{col: 12}"
|
:cols="{col: 12}"
|
||||||
name="Effects"
|
name="Effects"
|
||||||
>
|
>
|
||||||
<v-list style="width: 100%;">
|
<v-list style="width: 100%;">
|
||||||
<attribute-effect
|
|
||||||
v-for="effect in baseEffects"
|
|
||||||
:key="effect._id"
|
|
||||||
:model="effect"
|
|
||||||
:hide-breadcrumbs="effect._id === model._id"
|
|
||||||
:data-id="effect._id"
|
|
||||||
@click="effect._id !== model._id && clickEffect(effect._id)"
|
|
||||||
/>
|
|
||||||
<attribute-effect
|
<attribute-effect
|
||||||
v-for="effect in effects"
|
v-for="effect in effects"
|
||||||
:key="effect._id"
|
:key="effect._id"
|
||||||
:model="effect"
|
:model="effect"
|
||||||
:data-id="effect._id"
|
:data-id="effect._id"
|
||||||
@click="clickEffect(effect._id)"
|
:hide-breadcrumbs="effect._id === model._id"
|
||||||
|
@click="effect._id !== model._id && clickEffect(effect._id)"
|
||||||
/>
|
/>
|
||||||
</v-list>
|
</v-list>
|
||||||
</property-field>
|
</property-field>
|
||||||
@@ -137,7 +130,6 @@
|
|||||||
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js'
|
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js'
|
||||||
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
|
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
|
||||||
import AttributeEffect from '/imports/ui/properties/components/attributes/AttributeEffect.vue';
|
import AttributeEffect from '/imports/ui/properties/components/attributes/AttributeEffect.vue';
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
|
||||||
import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||||
import IncrementButton from '/imports/ui/components/IncrementButton.vue';
|
import IncrementButton from '/imports/ui/components/IncrementButton.vue';
|
||||||
import getProficiencyIcon from '/imports/ui/utility/getProficiencyIcon.js';
|
import getProficiencyIcon from '/imports/ui/utility/getProficiencyIcon.js';
|
||||||
@@ -211,31 +203,8 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
baseEffects(){
|
|
||||||
if (this.context.creatureId && this.model.variableName){
|
|
||||||
let creatureId = this.context.creatureId;
|
|
||||||
return CreatureProperties.find({
|
|
||||||
'ancestors.id': creatureId,
|
|
||||||
type: 'attribute',
|
|
||||||
variableName: this.model.variableName,
|
|
||||||
removed: {$ne: true},
|
|
||||||
inactive: {$ne: true},
|
|
||||||
}).map( prop => ({
|
|
||||||
_id: prop._id,
|
|
||||||
name: 'Attribute base value',
|
|
||||||
operation: 'base',
|
|
||||||
amount: prop.baseValue,
|
|
||||||
stats: [prop.variableName],
|
|
||||||
ancestors: prop.ancestors,
|
|
||||||
}) ).filter(effect => effect.amount);
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
effects() {
|
effects() {
|
||||||
return CreatureProperties.find({
|
return this.model.effects;
|
||||||
_id: { $in: this.model.effects?.map(e => e._id) || [] }
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
45
app/imports/ui/properties/viewers/PointBuyViewer.vue
Normal file
45
app/imports/ui/properties/viewers/PointBuyViewer.vue
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<div class="point-buy-viewer">
|
||||||
|
<v-row dense>
|
||||||
|
<property-field
|
||||||
|
v-for="(row, i) in model.values"
|
||||||
|
:key="row._id"
|
||||||
|
:name="row.name"
|
||||||
|
:value="row.value"
|
||||||
|
/>
|
||||||
|
</v-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js'
|
||||||
|
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||||
|
import { timingOptions, eventOptions, actionPropertyTypeOptions } from '/imports/api/properties/Triggers.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [propertyViewerMixin],
|
||||||
|
inject: {
|
||||||
|
context: {
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
slotTypeName(){
|
||||||
|
if (!this.model.slotType) return;
|
||||||
|
return getPropertyName(this.model.slotType);
|
||||||
|
},
|
||||||
|
timingText(){
|
||||||
|
if (!this.model.timing) return;
|
||||||
|
return timingOptions[this.model.timing];
|
||||||
|
},
|
||||||
|
actionPropertyText(){
|
||||||
|
if (!this.model.actionPropertyType) return;
|
||||||
|
return actionPropertyTypeOptions[this.model.actionPropertyType];
|
||||||
|
},
|
||||||
|
eventText(){
|
||||||
|
if (!this.model.event) return;
|
||||||
|
return eventOptions[this.model.event];
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -47,6 +47,12 @@
|
|||||||
name="Passive score"
|
name="Passive score"
|
||||||
:value="passiveScore"
|
:value="passiveScore"
|
||||||
/>
|
/>
|
||||||
|
<property-field
|
||||||
|
v-if="model.overridden"
|
||||||
|
:cols="{cols: 6, md: 12}"
|
||||||
|
name="Overridden"
|
||||||
|
value="Overriden by another property with the same variable name"
|
||||||
|
/>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row dense>
|
<v-row dense>
|
||||||
<property-description
|
<property-description
|
||||||
@@ -55,7 +61,7 @@
|
|||||||
/>
|
/>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row
|
<v-row
|
||||||
v-if="baseEffects.length || ability || effects.length"
|
v-if="ability || (effects && effects.length)"
|
||||||
dense
|
dense
|
||||||
>
|
>
|
||||||
<property-field
|
<property-field
|
||||||
@@ -63,14 +69,6 @@
|
|||||||
name="Effects"
|
name="Effects"
|
||||||
>
|
>
|
||||||
<v-list style="width: 100%">
|
<v-list style="width: 100%">
|
||||||
<attribute-effect
|
|
||||||
v-for="effect in baseEffects"
|
|
||||||
:key="effect._id === model._id ? 'this_base' : effect._id"
|
|
||||||
:model="effect"
|
|
||||||
:hide-breadcrumbs="effect._id === model._id"
|
|
||||||
:data-id="effect._id"
|
|
||||||
@click="effect._id !== model._id && clickEffect(effect._id)"
|
|
||||||
/>
|
|
||||||
<attribute-effect
|
<attribute-effect
|
||||||
v-if="ability"
|
v-if="ability"
|
||||||
:key="ability._id"
|
:key="ability._id"
|
||||||
@@ -188,32 +186,8 @@ export default {
|
|||||||
variables(){
|
variables(){
|
||||||
return CreatureVariables.findOne({_creatureId: this.context.creatureId}) || {};
|
return CreatureVariables.findOne({_creatureId: this.context.creatureId}) || {};
|
||||||
},
|
},
|
||||||
baseEffects(){
|
|
||||||
if (this.context.creatureId){
|
|
||||||
let creatureId = this.context.creatureId;
|
|
||||||
return CreatureProperties.find({
|
|
||||||
'ancestors.id': creatureId,
|
|
||||||
type: 'skill',
|
|
||||||
variableName: this.model.variableName,
|
|
||||||
removed: {$ne: true},
|
|
||||||
inactive: {$ne: true},
|
|
||||||
}).map( prop => ({
|
|
||||||
_id: prop._id,
|
|
||||||
name: 'Skill base value',
|
|
||||||
operation: 'base',
|
|
||||||
calculation: prop.baseValueCalculation,
|
|
||||||
amount: {value: prop.baseValue?.value},
|
|
||||||
stats: [prop.variableName],
|
|
||||||
ancestors: prop.ancestors,
|
|
||||||
}) ).filter(effect => effect.amount?.value);
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
effects() {
|
effects() {
|
||||||
return CreatureProperties.find({
|
return this.model.effects;
|
||||||
_id: { $in: this.model.effects?.map(e => e._id) || [] }
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
baseProficiencies(){
|
baseProficiencies(){
|
||||||
if (this.context.creatureId){
|
if (this.context.creatureId){
|
||||||
@@ -265,7 +239,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
_id: abilityProp._id,
|
_id: abilityProp._id,
|
||||||
name: abilityProp.name,
|
name: abilityProp.name,
|
||||||
operation: 'base',
|
operation: 'add',
|
||||||
amount: {value: abilityProp.modifier},
|
amount: {value: abilityProp.modifier},
|
||||||
stats: [this.model.variableName],
|
stats: [this.model.variableName],
|
||||||
ancestors: abilityProp.ancestors,
|
ancestors: abilityProp.ancestors,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const FeatureViewer = () => import ('/imports/ui/properties/viewers/FeatureViewe
|
|||||||
const FolderViewer = () => import ('/imports/ui/properties/viewers/FolderViewer.vue');
|
const FolderViewer = () => import ('/imports/ui/properties/viewers/FolderViewer.vue');
|
||||||
const ItemViewer = () => import ('/imports/ui/properties/viewers/ItemViewer.vue');
|
const ItemViewer = () => import ('/imports/ui/properties/viewers/ItemViewer.vue');
|
||||||
const NoteViewer = () => import ('/imports/ui/properties/viewers/NoteViewer.vue');
|
const NoteViewer = () => import ('/imports/ui/properties/viewers/NoteViewer.vue');
|
||||||
|
const PointBuyViewer = () => import ('/imports/ui/properties/viewers/PointBuyViewer.vue');
|
||||||
const ProficiencyViewer = () => import ('/imports/ui/properties/viewers/ProficiencyViewer.vue');
|
const ProficiencyViewer = () => import ('/imports/ui/properties/viewers/ProficiencyViewer.vue');
|
||||||
const ReferenceViewer = () => import ('/imports/ui/properties/viewers/ReferenceViewer.vue');
|
const ReferenceViewer = () => import ('/imports/ui/properties/viewers/ReferenceViewer.vue');
|
||||||
const RollViewer = () => import ('/imports/ui/properties/viewers/RollViewer.vue');
|
const RollViewer = () => import ('/imports/ui/properties/viewers/RollViewer.vue');
|
||||||
@@ -45,6 +46,7 @@ export default {
|
|||||||
folder: FolderViewer,
|
folder: FolderViewer,
|
||||||
item: ItemViewer,
|
item: ItemViewer,
|
||||||
note: NoteViewer,
|
note: NoteViewer,
|
||||||
|
pointBuy: PointBuyViewer,
|
||||||
proficiency: ProficiencyViewer,
|
proficiency: ProficiencyViewer,
|
||||||
propertySlot: SlotViewer,
|
propertySlot: SlotViewer,
|
||||||
roll: RollViewer,
|
roll: RollViewer,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import TabletopMap from '/imports/ui/tabletop/TabletopMap.vue';
|
|||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import TabletopActionCards from '/imports/ui/tabletop/TabletopActionCards.vue';
|
import TabletopActionCards from '/imports/ui/tabletop/TabletopActionCards.vue';
|
||||||
import MiniCharacterSheet from '/imports/ui/creature/character/MiniCharacterSheet.vue';
|
import MiniCharacterSheet from '/imports/ui/creature/character/MiniCharacterSheet.vue';
|
||||||
import snackbar from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
Reference in New Issue
Block a user