Merge branch 'develop' of https://github.com/ThaumRystra/DiceCloud into develop

This commit is contained in:
Thaum Rystra
2024-10-30 10:03:51 +02:00
18 changed files with 85 additions and 90 deletions

View File

@@ -103,7 +103,7 @@ function subDocsExist(prop, key) {
export function removeEmptyCalculations(prop) { export function removeEmptyCalculations(prop) {
prop._computationDetails.emptyCalculations.forEach(calcObj => { prop._computationDetails.emptyCalculations.forEach(calcObj => {
if (!calcObj.effects?.length) { if (!calcObj.effectIds?.length && !calcObj.proficiencyIds?.length) {
unset(prop, calcObj._key); unset(prop, calcObj._key);
} }
}); });

View File

@@ -48,6 +48,7 @@ function unloadCreature(creatureId, subscription) {
export function getSingleProperty(creatureId: string, propertyId: string) { export function getSingleProperty(creatureId: string, propertyId: string) {
const creature = loadedCreatures.get(creatureId) const creature = loadedCreatures.get(creatureId)
const property = creature?.properties.get(propertyId); const property = creature?.properties.get(propertyId);
if (property?.removed) return;
if (property) { if (property) {
return EJSON.clone(property); return EJSON.clone(property);
} }
@@ -64,8 +65,9 @@ export function getSingleProperty(creatureId: string, propertyId: string) {
export function getProperties(creatureId: string): CreatureProperty[] { export function getProperties(creatureId: string): CreatureProperty[] {
const creature = loadedCreatures.get(creatureId); const creature = loadedCreatures.get(creatureId);
if (creature) { if (creature) {
const props = Array.from(creature.properties.values()); const props = Array.from(creature.properties.values())
props.sort((a, b) => a.left - b.left); .sort((a, b) => a.left - b.left)
.filter(prop => !prop.removed);
return EJSON.clone(props); return EJSON.clone(props);
} }
// console.time(`Cache miss on creature properties: ${creatureId}`) // console.time(`Cache miss on creature properties: ${creatureId}`)
@@ -82,13 +84,10 @@ export function getProperties(creatureId: string): CreatureProperty[] {
export function getPropertiesOfType(creatureId, propType) { export function getPropertiesOfType(creatureId, propType) {
const creature = loadedCreatures.get(creatureId); const creature = loadedCreatures.get(creatureId);
if (creature) { if (creature) {
const props: CreatureProperty[] = [] const props = Array.from(
for (const prop of creature.properties.values()) { creature.properties.values()
if (prop.type === propType) { .filter(prop => !prop.removed && prop.type === propType)
props.push(prop); ).sort((a, b) => a.left - b.left);
}
}
props.sort((a, b) => a.left - b.left);
return EJSON.clone(props); return EJSON.clone(props);
} }
// console.time(`Cache miss on creature properties: ${creatureId}`) // console.time(`Cache miss on creature properties: ${creatureId}`)
@@ -112,13 +111,10 @@ export function getPropertiesOfType(creatureId, propType) {
export function getPropertiesByFilter(creatureId, filterFn: (any) => boolean, mongoFilter: Mongo.Selector<object>) { export function getPropertiesByFilter(creatureId, filterFn: (any) => boolean, mongoFilter: Mongo.Selector<object>) {
const creature = loadedCreatures.get(creatureId); const creature = loadedCreatures.get(creatureId);
if (creature) { if (creature) {
const props: CreatureProperty[] = [] const props: CreatureProperty[] = Array.from(
for (const prop of creature.properties.values()) { creature.properties.values()
if (filterFn(prop)) { .filter(filterFn)
props.push(prop); ).sort((a, b) => a.left - b.left);
}
}
props.sort((a, b) => a.left - b.left);
return EJSON.clone(props); return EJSON.clone(props);
} }
// console.time(`Cache miss on creature properties: ${creatureId}`) // console.time(`Cache miss on creature properties: ${creatureId}`)
@@ -235,7 +231,7 @@ export function getPropertyChildren(creatureId, property) {
if (!creature) return []; if (!creature) return [];
const props: CreatureProperty[] = []; const props: CreatureProperty[] = [];
for (const prop of creature.properties.values()) { for (const prop of creature.properties.values()) {
if (prop.parentId === property._id) { if (prop.parentId === property._id && prop.removed !== true) {
props.push(prop); props.push(prop);
} }
} }

View File

@@ -1,5 +1,4 @@
import SimpleSchema from 'simpl-schema'; import SimpleSchema from 'simpl-schema';
import '/imports/api/sharing/sharing';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
export interface Shared { export interface Shared {

View File

@@ -39,7 +39,6 @@
<tree-node-view <tree-node-view
:model="node" :model="node"
:selected="selected" :selected="selected"
:show-external-details="showExternalDetails"
/> />
</div> </div>
</div> </div>
@@ -58,7 +57,6 @@
:organize="organize" :organize="organize"
:selected-node="selectedNode" :selected-node="selectedNode"
:start-expanded="startExpanded" :start-expanded="startExpanded"
:show-external-details="showExternalDetails"
@move-within-root="e => $emit('move-within-root', e)" @move-within-root="e => $emit('move-within-root', e)"
@move-between-roots="e => $emit('move-between-roots', e)" @move-between-roots="e => $emit('move-between-roots', e)"
@selected="e => $emit('selected', e)" @selected="e => $emit('selected', e)"
@@ -117,7 +115,6 @@ export default {
}, },
selected: Boolean, selected: Boolean,
startExpanded: Boolean, startExpanded: Boolean,
showExternalDetails: Boolean,
}, },
data() { data() {
return { return {

View File

@@ -23,7 +23,6 @@
:organize="organize" :organize="organize"
:lazy="lazy" :lazy="lazy"
:start-expanded="startExpanded" :start-expanded="startExpanded"
:show-external-details="showExternalDetails"
@selected="e => $emit('selected', e)" @selected="e => $emit('selected', e)"
@move-within-root="e => $emit('move-within-root', e)" @move-within-root="e => $emit('move-within-root', e)"
@move-between-roots="e => $emit('move-between-roots', e)" @move-between-roots="e => $emit('move-between-roots', e)"
@@ -68,7 +67,6 @@ export default {
default: () => [], default: () => [],
}, },
startExpanded: Boolean, startExpanded: Boolean,
showExternalDetails: Boolean,
}, },
data() { data() {
return { return {

View File

@@ -30,7 +30,7 @@ type DoActionParams = BaseDoActionParams & {
* the decisions the user makes, then applying the action as a method call to the server with the * the decisions the user makes, then applying the action as a method call to the server with the
* saved decisions, which will persist the action results. * saved decisions, which will persist the action results.
*/ */
export default async function doAction({ propId, creatureId, $store, elementId, task }: DoActionParams | DoTaskParams) { export default async function doAction({ propId, creatureId, $store, elementId, task }: DoActionParams | DoTaskParams): Promise<any | void> {
if (!task) { if (!task) {
if (!propId) throw new Meteor.Error('no-prop-id', 'Either propId or task must be provided'); if (!propId) throw new Meteor.Error('no-prop-id', 'Either propId or task must be provided');
task = { task = {
@@ -64,7 +64,7 @@ export default async function doAction({ propId, creatureId, $store, elementId,
return callActionMethod(finishedAction); return callActionMethod(finishedAction);
} catch (e) { } catch (e) {
if (e !== 'input-requested') throw e; if (e !== 'input-requested') throw e;
return new Promise(resolve => { return new Promise<void>((resolve, reject) => {
$store.commit('pushDialogStack', { $store.commit('pushDialogStack', {
component: 'action-dialog', component: 'action-dialog',
elementId, elementId,
@@ -72,9 +72,14 @@ export default async function doAction({ propId, creatureId, $store, elementId,
actionId, actionId,
task, task,
}, },
callback(action: EngineAction) { async callback(action: EngineAction) {
if (!action) return; try {
resolve(callActionMethod(action)); if (action) await callActionMethod(action);
resolve();
}
catch (e) {
reject(e);
}
return elementId; return elementId;
}, },
}); });

View File

@@ -11,7 +11,7 @@
outlined outlined
style="font-size: 16px; letter-spacing: normal;" style="font-size: 16px; letter-spacing: normal;"
class="mr-2" class="mr-2"
data-id="do-action-button" :data-id="`${model._id}-do-action-button`"
:color="model.color || 'primary'" :color="model.color || 'primary'"
:loading="doActionLoading" :loading="doActionLoading"
:disabled="model.insufficientResources || !context.editPermission || !!targetingError" :disabled="model.insufficientResources || !context.editPermission || !!targetingError"
@@ -89,7 +89,6 @@
<tree-node-list <tree-node-list
v-if="children && children.length" v-if="children && children.length"
start-expanded start-expanded
show-external-details
:children="children" :children="children"
:root="model.root" :root="model.root"
@selected="e => $emit('sub-click', e)" @selected="e => $emit('sub-click', e)"
@@ -229,7 +228,7 @@ export default {
propId: this.model._id, propId: this.model._id,
creatureId: this.model.root.id, creatureId: this.model.root.id,
$store: this.$store, $store: this.$store,
elementId: 'do-action-button', elementId: `${this.model._id}-do-action-button`,
}).catch((e) => { }).catch((e) => {
console.error(e); console.error(e);
}).finally(() => { }).finally(() => {

View File

@@ -33,16 +33,22 @@
<script lang="js"> <script lang="js">
import getEffectIcon from '/imports/client/ui/utility/getEffectIcon'; import getEffectIcon from '/imports/client/ui/utility/getEffectIcon';
import { isFinite } from 'lodash'; import { isFinite } from 'lodash';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
export default { export default {
props: { props: {
hideBreadcrumbs: Boolean, hideBreadcrumbs: Boolean,
model: { effectId: {
type: Object, type: String,
required: true, required: true,
}, },
}, },
meteor: {
model() {
return CreatureProperties.findOne(this.effectId);
},
},
computed: { computed: {
hasClickListener(){ hasClickListener(){
return this.$listeners && this.$listeners.click return this.$listeners && this.$listeners.click

View File

@@ -27,17 +27,23 @@
<script lang="js"> <script lang="js">
import ProficiencyIcon from '/imports/client/ui/properties/shared/ProficiencyIcon.vue'; import ProficiencyIcon from '/imports/client/ui/properties/shared/ProficiencyIcon.vue';
import numberToSignedString from '/imports/api/utility/numberToSignedString'; import numberToSignedString from '/imports/api/utility/numberToSignedString';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
export default { export default {
components: { components: {
ProficiencyIcon, ProficiencyIcon,
}, },
props: { props: {
model: { proficiencyId: {
type: Object, type: String,
required: true, required: true,
}, },
}, },
meteor: {
model() {
return CreatureProperties.findOne(this.proficiencyId);
},
},
computed: { computed: {
displayedText(){ displayedText(){
return this.model.name || (this.model.type == 'proficiency' ? 'Proficiency' : 'Skill') return this.model.name || (this.model.type == 'proficiency' ? 'Proficiency' : 'Skill')

View File

@@ -46,12 +46,11 @@ export default {
return true; return true;
}, },
displayedValue() { displayedValue() {
let value = this.model.value; // Use the unaffected value instead if the calculation has it, because effects can modify the value
// Use the base value instead if the calculation has it, because effects can modify the value if (this.model.unaffected !== undefined) {
if (this.model.baseValue !== undefined) { return this.model.unaffected;
value = this.model.baseValue;
} }
return value; return this.model.value;
}, },
errorList(){ errorList(){
if (this.model.parseError){ if (this.model.parseError){

View File

@@ -18,24 +18,6 @@
<span v-if="model.target === 'self'">to self</span> <span v-if="model.target === 'self'">to self</span>
</div> </div>
</div> </div>
<template v-if="showExternalDetails">
<div
v-for="effect in (model.amount && model.amount.effects)"
:key="effect._id"
>
<div
v-if="effect.amount.value !== 0"
style="position:relative; top:-15px; left:5px; height:25px;"
>
<inline-effect
:key="effect._id"
hide-breadcrumbs
:data-id="effect._id"
:model="effect"
/>
</div>
</div>
</template>
</div> </div>
</template> </template>

View File

@@ -3,7 +3,6 @@
:is="treeNodeView" :is="treeNodeView"
:model="model" :model="model"
:selected="selected" :selected="selected"
:show-external-details="showExternalDetails"
:class="{ :class="{
'inactive': model.inactive, 'inactive': model.inactive,
}" }"
@@ -25,7 +24,6 @@ export default {
required: true, required: true,
}, },
selected: Boolean, selected: Boolean,
showExternalDetails: Boolean,
}, },
computed: { computed: {
treeNodeView(){ treeNodeView(){

View File

@@ -12,7 +12,6 @@ export default {
}, },
selected: Boolean, selected: Boolean,
hideIcon: Boolean, hideIcon: Boolean,
showExternalDetails: Boolean,
}, },
computed: { computed: {
title() { title() {

View File

@@ -31,9 +31,9 @@
'justify-end': end, 'justify-end': end,
'flex-wrap': wrap, 'flex-wrap': wrap,
'mono': isMono, 'mono': isMono,
'flex-grow-0': calculation && calculation.effects, 'flex-grow-0': hasEffectsOrProficiencies,
'flex-grow-1': !calculation || !calculation.effects, 'flex-grow-1': !hasEffectsOrProficiencies,
'ma-3': calculation && calculation.effects, 'ma-3': hasEffectsOrProficiencies,
...$attrs.class, ...$attrs.class,
}" }"
style="overflow-x: auto;" style="overflow-x: auto;"
@@ -49,36 +49,32 @@
</slot> </slot>
</div> </div>
<div <div
v-if="calculation && (calculation.effects || calculation.proficiencies)" v-if="hasEffectsOrProficiencies"
class="flex-grow-1" class="flex-grow-1"
style="max-width: 100%;" style="max-width: 100%;"
> >
<inline-effect <inline-effect
v-if="typeof calculation.value === 'number' && calculation.baseValue !== 0" v-for="effectId in calculation.effectIds"
hide-breadcrumbs :key="effectId"
:model="{ :data-id="effectId"
name: 'Base value', :effect-id="effectId"
operation: 'base', @click="clickEffect(effectId)"
amount: {value: calculation.baseValue},
}"
@click="clickEffect(effect._id)"
/>
<inline-effect
v-for="effect in calculation.effects"
:key="effect._id"
:data-id="effect._id"
:model="effect"
@click="clickEffect(effect._id)"
/> />
<inline-proficiency <inline-proficiency
v-for="proficiency in calculation.proficiencies" v-for="proficiencyId in calculation.proficiencyIds"
:key="proficiency._id" :key="proficiencyId"
:data-id="proficiency._id" :data-id="proficiencyId"
:model="proficiency" :proficiency-id="proficiencyId"
:proficiency-bonus="calculation.proficiencyBonus" @click="clickEffect(proficiencyId)"
@click="clickEffect(proficiency._id)"
/> />
</div> </div>
<div
v-if="hasEffectsOrProficiencies"
class="d-flex justify-end border-t-sm pt-2"
style="width: 100%; opacity: 0.5"
>
{{ calculation.value }}
</div>
</div> </div>
</fieldset> </fieldset>
</v-col> </v-col>
@@ -152,6 +148,9 @@ export default {
if (this.signed) { if (this.signed) {
return numberToSignedString(calculation.value); return numberToSignedString(calculation.value);
} }
if (this.hasEffectsOrProficiencies) {
return calculation.unaffected;
}
return calculation.value; return calculation.value;
}, },
// large and center are only applied to calculations if we are showing their // large and center are only applied to calculations if we are showing their
@@ -172,6 +171,15 @@ export default {
if (this.valueNotReduced) return true; if (this.valueNotReduced) return true;
return this.mono; return this.mono;
}, },
hasEffects(){
return this.calculation?.effectIds?.length > 0;
},
hasProficiencies(){
return this.calculation?.proficiencyIds?.length > 0;
},
hasEffectsOrProficiencies() {
return this.hasEffects || this.hasProficiencies;
},
}, },
methods: { methods: {
numberToSignedString, numberToSignedString,

View File

@@ -87,7 +87,6 @@
<tree-node-list <tree-node-list
v-if="children && children.length" v-if="children && children.length"
start-expanded start-expanded
show-external-details
:children="children" :children="children"
:root="model.root" :root="model.root"
@selected="e => $emit('sub-click', e)" @selected="e => $emit('sub-click', e)"

View File

@@ -44,7 +44,6 @@
<tree-node-list <tree-node-list
v-if="children && children.length" v-if="children && children.length"
start-expanded start-expanded
show-external-details
:children="children" :children="children"
:root="model.root" :root="model.root"
@selected="e => $emit('sub-click', e)" @selected="e => $emit('sub-click', e)"

View File

@@ -4,6 +4,7 @@ import { assertViewPermission } from '/imports/api/creature/creatures/creaturePe
import computeCreature from '/imports/api/engine/computeCreature'; import computeCreature from '/imports/api/engine/computeCreature';
import VERSION from '/imports/constants/VERSION'; import VERSION from '/imports/constants/VERSION';
import { getCreature, getProperties, getVariables } from '/imports/api/engine/loadCreatures'; import { getCreature, getProperties, getVariables } from '/imports/api/engine/loadCreatures';
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION';
JsonRoutes.add('get', 'api/creature/:id', function (req, res) { JsonRoutes.add('get', 'api/creature/:id', function (req, res) {
const creatureId = req.params.id; const creatureId = req.params.id;
@@ -46,6 +47,9 @@ JsonRoutes.add('get', 'api/creature/:id', function (req, res) {
// Send the results // Send the results
JsonRoutes.sendResult(res, { JsonRoutes.sendResult(res, {
data: { data: {
meta: {
schemaVersion: SCHEMA_VERSION,
},
creatures: [creature], creatures: [creature],
creatureProperties: getProperties(creatureId), creatureProperties: getProperties(creatureId),
creatureVariables: getVariables(creatureId), creatureVariables: getVariables(creatureId),

View File

@@ -17,4 +17,5 @@ import '/imports/api/creature/creatureProperties/methods/index';
import '/imports/api/creature/archive/methods/index'; import '/imports/api/creature/archive/methods/index';
import '/imports/api/creature/creatures/methods/index'; import '/imports/api/creature/creatures/methods/index';
import '/imports/api/engine/action/methods/index'; import '/imports/api/engine/action/methods/index';
import '/imports/api/sharing/sharing';
import '/imports/server/config/publicationStrategies'; import '/imports/server/config/publicationStrategies';