diff --git a/app/.meteor/packages b/app/.meteor/packages index 69c4b9f3..0208062b 100644 --- a/app/.meteor/packages +++ b/app/.meteor/packages @@ -18,7 +18,7 @@ meteorhacks:subs-manager chuangbo:marked meteor-base@1.4.0 mobile-experience@1.1.0 -mongo@1.9.0 +mongo@1.10.0 session@1.2.0 jquery@1.11.10 tracker@1.2.0 @@ -30,14 +30,14 @@ standard-minifier-js@2.6.0 shell-server@0.5.0 seba:minifiers-autoprefixer templates:array -ecmascript@0.14.2 +ecmascript@0.14.3 es5-shim@4.8.0 reactive-dict@1.3.0 percolate:synced-cron ongoworks:speakingurl service-configuration@1.0.11 google-config-ui@1.0.1 -dynamic-import@0.5.1 +dynamic-import@0.5.2 ddp-rate-limiter@1.0.7 rate-limit@1.0.9 meteortesting:mocha diff --git a/app/.meteor/release b/app/.meteor/release index d2597811..3ea26528 100644 --- a/app/.meteor/release +++ b/app/.meteor/release @@ -1 +1 @@ -METEOR@1.10.1 +METEOR@1.10.2 diff --git a/app/.meteor/versions b/app/.meteor/versions index c5984b8c..fe231134 100644 --- a/app/.meteor/versions +++ b/app/.meteor/versions @@ -70,14 +70,14 @@ meteortesting:mocha@1.1.5 meteortesting:mocha-core@7.0.1 minifier-css@1.5.0 minifier-js@2.6.0 -minimongo@1.5.0 +minimongo@1.6.0 mobile-experience@1.1.0 mobile-status-bar@1.1.0 modern-browsers@0.1.5 modules@0.15.0 modules-runtime@0.12.0 momentjs:moment@2.24.0 -mongo@1.9.1 +mongo@1.10.0 mongo-decimal@0.1.1 mongo-dev-server@1.1.0 mongo-id@1.0.7 @@ -104,10 +104,10 @@ service-configuration@1.0.11 session@1.2.0 sha@1.0.9 shell-server@0.5.0 -socket-stream-client@0.2.3 +socket-stream-client@0.3.0 spacebars@1.0.15 spacebars-compiler@1.1.3 -srp@1.0.12 +srp@1.1.0 standard-minifier-js@2.6.0 static-html@1.2.2 templates:array@1.0.3 @@ -118,6 +118,6 @@ templating-tools@1.1.2 tmeasday:check-npm-versions@0.3.2 tracker@1.2.0 underscore@1.0.10 -url@1.2.0 +url@1.3.0 webapp@1.9.1 webapp-hashing@1.0.9 diff --git a/app/imports/api/creature/computation/afterComputation/evaluateString.js b/app/imports/api/creature/computation/afterComputation/evaluateString.js new file mode 100644 index 00000000..d149eb4a --- /dev/null +++ b/app/imports/api/creature/computation/afterComputation/evaluateString.js @@ -0,0 +1,107 @@ +import * as math from 'mathjs'; + +export default function evaluateString(string, scope){ + let errors = []; + if (!string){ + errors.push('No string provided'); + return {result: string, errors}; + } + + if (!scope) errors.push('No scope provided'); + + // Parse the string using mathjs + let calc; + try { + calc = math.parse(string); + } catch (e) { + errors.push(e); + return {result: string, errors}; + } + + // Replace all bare symbols with symbol.value + let transformedCalc = calc.transform(replaceBareSymbolsWithValueAccessor); + + // Evaluate the expression to a number or return with substitutions + try { + let result = transformedCalc.evaluate(scope); + return {result, errors}; + } catch (e1){ + errors.push(e1); + try { + result = simplifyWithAccessors(calc, scope).toHTML(); + return {result, errors}; + } catch (e2){ + errors.push(e2); + return {result: calc.toHTML(), errors}; + } + } +} + +function replaceBareSymbolsWithValueAccessor(node, path, parent) { + if (node.isSymbolNode && path !== 'object') { + const object = new math.SymbolNode(node.name); + const address = new math.ConstantNode('value'); + const index = new math.IndexNode([address]); + return new math.AccessorNode(object, index); + } else { + return node; + } +} + +function simplifyWithAccessors(calc, scope){ + let noAccessorCalc = calc.transform(substituteAccessors(scope)); + return math.simplify(noAccessorCalc); +} + +// returns a function to replace all accessors with either their resolved value +// or a symbol to simplify with +function substituteAccessors(scope){ + return function(node, path, parent){ + if (node.isAccessorNode){ + try { + return evaluateAccessor(node, scope); + } catch (e) { + console.log(typeof e); + return replaceAccessorWithSymbol(node); + } + } else { + return node; + } + } +} + +// Throws error if symbol is undefined in scope +function evaluateAccessor(node, scope){ + let value = node.evaluate(scope); + if (value === undefined){ + throw 'Undefined symbol' + } + return new math.ConstantNode(value); +} + +function replaceAccessorWithSymbol(node){ + let symbolNode = new math.SymbolNode(node.toString()); + return symbolNode; +} + +function overrideSymbolNodeHTML(symbolNode){ + let safeName = escape(symbolNode.name); + symbolNode.toHTML = function(){ + console.log('running custom tohtml function') + return `${safeName}` + } + return symbolNode; +} + +// Escape special HTML characters +// Copied directly from math.js source to help with overriding toHTML +function escape (value) { + let text = String(value) + text = text.replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(//g, '>') + + return text +} diff --git a/app/imports/api/creature/computation/combineStat.js b/app/imports/api/creature/computation/combineStat.js index fb4ce6ce..00ed9abb 100644 --- a/app/imports/api/creature/computation/combineStat.js +++ b/app/imports/api/creature/computation/combineStat.js @@ -19,8 +19,9 @@ function combineAttribute(stat, aggregator){ if (!stat.decimal) result = Math.floor(result); stat.value = result; if (stat.attributeType === 'ability') { - stat.mod = Math.floor((result - 10) / 2); + stat.modifier = Math.floor((result - 10) / 2); } + stat.currentValue = stat.value - (stat.damage || 0); } function combineSkill(stat, aggregator, memo){ @@ -30,7 +31,7 @@ function combineSkill(stat, aggregator, memo){ if (!ability.computationDetails.computed){ computeStat(ability, memo); } - stat.abilityMod = ability.mod; + stat.abilityMod = ability.modifier; } // Combine all the child proficiencies for (let i in stat.proficiencies){ diff --git a/app/imports/api/creature/computation/evaluateCalculation.js b/app/imports/api/creature/computation/evaluateCalculation.js index c87e0866..fce10d16 100644 --- a/app/imports/api/creature/computation/evaluateCalculation.js +++ b/app/imports/api/creature/computation/evaluateCalculation.js @@ -16,7 +16,7 @@ export default function evaluateCalculation(string, memo){ if (node.isSymbolNode) { let val = computedValueOfVariableName(node.name, memo); if (val === null) return node; - return new math.expression.node.ConstantNode(val); + return new math.ConstantNode(val); } else { return node; diff --git a/app/imports/api/creature/computation/writeCreatureVariables.js b/app/imports/api/creature/computation/writeCreatureVariables.js index 5a310531..5255a1b9 100644 --- a/app/imports/api/creature/computation/writeCreatureVariables.js +++ b/app/imports/api/creature/computation/writeCreatureVariables.js @@ -11,7 +11,8 @@ export default function writeCreatureVariables(memo, creatureId) { 'reset', 'resetMultiplier', 'value', - 'mod', + 'currentValue', + 'modifier', 'ability', 'skillType', 'baseProficiency', diff --git a/app/imports/ui/components/computation/Computed.vue b/app/imports/ui/components/computation/Computed.vue new file mode 100644 index 00000000..0bd8fa62 --- /dev/null +++ b/app/imports/ui/components/computation/Computed.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/app/imports/ui/components/computation/ComputedForCreature.vue b/app/imports/ui/components/computation/ComputedForCreature.vue new file mode 100644 index 00000000..137b189b --- /dev/null +++ b/app/imports/ui/components/computation/ComputedForCreature.vue @@ -0,0 +1,29 @@ + + + diff --git a/app/imports/ui/creature/character/CharacterSheet.vue b/app/imports/ui/creature/character/CharacterSheet.vue index 5e0fde17..17763f43 100644 --- a/app/imports/ui/creature/character/CharacterSheet.vue +++ b/app/imports/ui/creature/character/CharacterSheet.vue @@ -3,8 +3,8 @@ menu
- {{ character.name }} + {{ creature.name }}
refresh @@ -143,6 +143,10 @@ required: true, }, }, + reactiveProvide: { + name: 'computationContext', + include: ['creature'], + }, data(){return { theme, tab: 0, @@ -181,7 +185,7 @@ component: 'delete-confirmation-dialog', elementId: 'creature-menu', data: { - name: this.character.name, + name: this.creature.name, typeName: 'Character' }, callback(confirmation){ @@ -204,13 +208,10 @@ return [this.creatureId]; }, }, - character(){ + creature(){ return Creatures.findOne(this.creatureId) || {}; }, }, - provide: { - creature: this.character, - } } diff --git a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue index 740fabf4..dcd09470 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue @@ -162,6 +162,14 @@ :data-id="action._id" @click="clickProperty({_id: action._id})" /> + Attacks + @@ -180,6 +188,7 @@ import ResourceCard from '/imports/ui/properties/components/attributes/ResourceCard.vue'; import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue'; import ActionListTile from '/imports/ui/properties/components/actions/ActionListTile.vue'; + import AttackListTile from '/imports/ui/properties/components/actions/AttackListTile.vue'; const getAttributeOfType = function(charId, type){ return CreatureProperties.find({ @@ -215,6 +224,7 @@ ResourceCard, SpellSlotListTile, ActionListTile, + AttackListTile, }, props: { creatureId: { @@ -300,6 +310,14 @@ sort: {order: 1}, }); }, + attacks(){ + return CreatureProperties.find({ + 'ancestors.id': this.creatureId, + type: 'attack', + }, { + sort: {order: 1}, + }); + }, }, methods: { clickProperty({_id}){ diff --git a/app/imports/ui/properties/components/actions/ActionListTile.vue b/app/imports/ui/properties/components/actions/ActionListTile.vue index 38af3baa..6f834193 100644 --- a/app/imports/ui/properties/components/actions/ActionListTile.vue +++ b/app/imports/ui/properties/components/actions/ActionListTile.vue @@ -10,7 +10,12 @@ + + diff --git a/app/imports/ui/vueSetup.js b/app/imports/ui/vueSetup.js index 11460432..eb588b0d 100644 --- a/app/imports/ui/vueSetup.js +++ b/app/imports/ui/vueSetup.js @@ -4,6 +4,7 @@ import Vuetify from "vuetify"; import store from "/imports/ui/vuexStore.js"; import VueMeteorTracker from 'vue-meteor-tracker'; import AppLayout from '/imports/ui/layouts/AppLayout.vue'; +import ReactiveProvide from 'vue-reactive-provide'; import router from "/imports/ui/router.js"; import { theme } from '/imports/ui/theme.js'; import "vuetify/dist/vuetify.min.css"; @@ -15,6 +16,9 @@ Vue.use(Vuetify, { theme, iconfont: "md", }); +Vue.use(ReactiveProvide, { + name: 'reactiveProvide', // default value +}) // App start Meteor.startup(() => { diff --git a/app/package-lock.json b/app/package-lock.json index b16ee609..30707cef 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1273,18 +1273,18 @@ "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==" }, "mathjs": { - "version": "5.10.3", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-5.10.3.tgz", - "integrity": "sha512-ySjg30BC3dYjQm73ILZtwcWzFJde0VU6otkXW/57IjjuYRa3Qaf0Kb8pydEuBZYtqW2OxreAtsricrAmOj3jIw==", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-6.6.4.tgz", + "integrity": "sha512-fvmP89ujJbDAC8ths7FZh7PWdA71dfA5WJVAzJbQhSDCHK1aBk8WRf1XcTw51ERs+sKx9nYBGsRshqmb/oe8Ag==", "requires": { - "complex.js": "2.0.11", - "decimal.js": "10.2.0", - "escape-latex": "1.2.0", - "fraction.js": "4.0.12", - "javascript-natural-sort": "0.7.1", - "seed-random": "2.2.0", - "tiny-emitter": "2.1.0", - "typed-function": "1.1.0" + "complex.js": "^2.0.11", + "decimal.js": "^10.2.0", + "escape-latex": "^1.2.0", + "fraction.js": "^4.0.12", + "javascript-natural-sort": "^0.7.1", + "seed-random": "^2.2.0", + "tiny-emitter": "^2.1.0", + "typed-function": "^1.1.1" } }, "mem": { @@ -2702,9 +2702,9 @@ "dev": true }, "typed-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.0.tgz", - "integrity": "sha512-TuQzwiT4DDg19beHam3E66oRXhyqlyfgjHB/5fcvsRXbfmWPJfto9B4a0TBdTrQAPGlGmXh/k7iUI+WsObgORA==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.1.tgz", + "integrity": "sha512-RbN7MaTQBZLJYzDENHPA0nUmWT0Ex80KHItprrgbTPufYhIlTePvCXZxyQK7wgn19FW5bnuaBIKcBb5mRWjB1Q==" }, "underscore": { "version": "1.10.2", @@ -2769,6 +2769,11 @@ "lodash.omit": "^4.5.0" } }, + "vue-reactive-provide": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/vue-reactive-provide/-/vue-reactive-provide-0.3.0.tgz", + "integrity": "sha512-hx2JtRPRvne9NY4s1r7ASsCaO8CIby30qwC1kGQRxsrWApO3he+rziGOzTDSfvmr852zWMb11n6qAwHCz6C/vw==" + }, "vue-router": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.6.tgz", diff --git a/app/package.json b/app/package.json index ef60434e..aba940f6 100644 --- a/app/package.json +++ b/app/package.json @@ -25,7 +25,7 @@ "date-fns": "^1.30.1", "lodash": "^4.17.15", "marked": "^0.8.2", - "mathjs": "^5.10.3", + "mathjs": "^6.6.4", "meteor-node-stubs": "^0.3.3", "moo": "^0.5.1", "nearley": "^2.19.1", @@ -35,6 +35,7 @@ "underscore": "^1.10.2", "vue": "2.6.10", "vue-meteor-tracker": "^2.0.0-beta.5", + "vue-reactive-provide": "^0.3.0", "vue-router": "^3.1.6", "vuedraggable": "^2.23.2", "vuetify": "^1.5.24", @@ -61,9 +62,16 @@ ], "parser": "vue-eslint-parser", "rules": { - "vue/component-tags-order": ["error", { - "order": ["template", "script", "style"] - }] + "vue/component-tags-order": [ + "error", + { + "order": [ + "template", + "script", + "style" + ] + } + ] } } ],