Show Point buy on Build tab
This commit is contained in:
@@ -247,6 +247,7 @@ 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 = {
|
||||
|
||||
@@ -42,6 +42,7 @@ export default function computePointBuy(computation, node) {
|
||||
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({
|
||||
|
||||
@@ -137,6 +137,11 @@ const ComputedOnlyPointBuySchema = createPropertySchema({
|
||||
optional: true,
|
||||
removeBeforeCompute: true,
|
||||
},
|
||||
pointsLeft: {
|
||||
type: Number,
|
||||
optional: true,
|
||||
removeBeforeCompute: true,
|
||||
},
|
||||
errors: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
v-if="node.parent.id === parentSlotId"
|
||||
icon
|
||||
:disabled="context.editPermission === false"
|
||||
@click.stop="remove(node)"
|
||||
@@ -91,6 +92,7 @@
|
||||
<build-tree-node-list
|
||||
v-if="showExpanded"
|
||||
:children="computedChildren"
|
||||
:parent-slot-id="computedSlotId"
|
||||
@selected="e => $emit('selected', e)"
|
||||
/>
|
||||
<div v-else>
|
||||
@@ -147,6 +149,10 @@ export default {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
parentSlotId: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
data(){return {
|
||||
expanded: false,
|
||||
@@ -197,6 +203,21 @@ export default {
|
||||
}
|
||||
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(){
|
||||
return !!this.computedChildren.length || this.canFillWithMany;
|
||||
},
|
||||
@@ -230,41 +251,41 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.rotate-90 {
|
||||
transform: rotate(90deg) translateZ(0);
|
||||
}
|
||||
.expand-area {
|
||||
box-shadow: -2px 0px 0px 0px #808080;
|
||||
margin-left: 0;
|
||||
}
|
||||
.handle {
|
||||
cursor: move;
|
||||
}
|
||||
.empty .drag-area {
|
||||
box-shadow: -2px 0px 0px 0px rgb(128, 128, 128, 0.4);
|
||||
}
|
||||
.empty .expand-button {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.rotate-90 {
|
||||
transform: rotate(90deg) translateZ(0);
|
||||
}
|
||||
.expand-area {
|
||||
box-shadow: -2px 0px 0px 0px #808080;
|
||||
margin-left: 0;
|
||||
}
|
||||
.handle {
|
||||
cursor: move;
|
||||
}
|
||||
.empty .drag-area {
|
||||
box-shadow: -2px 0px 0px 0px rgb(128, 128, 128, 0.4);
|
||||
}
|
||||
.empty .expand-button {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.found {
|
||||
background: rgba(200, 0, 0, 0.1) !important;
|
||||
}
|
||||
.ghost {
|
||||
.ghost {
|
||||
opacity: 0.5;
|
||||
background: rgba(251, 0, 0, 0.3);
|
||||
}
|
||||
.v-icon.v-icon--disabled {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.v-icon.v-icon--disabled {
|
||||
opacity: 0;
|
||||
}
|
||||
.v-icon {
|
||||
transition: none !important;
|
||||
}
|
||||
.theme--light .tree-node-title:hover {
|
||||
background-color: rgba(0,0,0,.04);
|
||||
}
|
||||
.theme--light .tree-node-title:hover {
|
||||
background-color: rgba(0,0,0,.04);
|
||||
}
|
||||
.theme--dark .tree-node-title:hover {
|
||||
background-color: rgba(255,255,255,.04);
|
||||
}
|
||||
background-color: rgba(255,255,255,.04);
|
||||
}
|
||||
.tree-node-title{
|
||||
transition: background ease 0.3s, color ease 0.15s;
|
||||
}
|
||||
|
||||
@@ -5,26 +5,31 @@
|
||||
:key="child.node._id"
|
||||
:node="child.node"
|
||||
:children="child.children"
|
||||
:parent-slot-id="parentSlotId"
|
||||
@selected="e => $emit('selected', e)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import BuildTreeNode from '/imports/ui/creature/buildTree/BuildTreeNode.vue';
|
||||
import BuildTreeNode from '/imports/ui/creature/buildTree/BuildTreeNode.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BuildTreeNode,
|
||||
},
|
||||
props: {
|
||||
children: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data(){ return {
|
||||
expanded: false,
|
||||
}},
|
||||
};
|
||||
export default {
|
||||
components: {
|
||||
BuildTreeNode,
|
||||
},
|
||||
props: {
|
||||
children: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
parentSlotId: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
data(){ return {
|
||||
expanded: false,
|
||||
}},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<v-menu
|
||||
bottom
|
||||
left
|
||||
transition="slide-y-transition"
|
||||
>
|
||||
<template #activator="{ on }">
|
||||
<v-badge
|
||||
@@ -48,12 +49,21 @@
|
||||
<v-icon class="mr-2">
|
||||
mdi-file-hidden
|
||||
</v-icon>
|
||||
{{ hiddenCount }} hidden {{ hiddenCount > 1 ? 'slots' : 'slot' }}
|
||||
{{ hiddenCount }} hidden {{ hiddenCount > 1 ? 'properties' : 'property' }}
|
||||
</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-for="slot in hiddenSlots"
|
||||
:key="slot._id"
|
||||
@click="unhideSlot(slot._id)"
|
||||
@click="unhideProp(slot._id)"
|
||||
>
|
||||
<v-list-item-title>
|
||||
{{ getPropertyTitle(slot) }}
|
||||
@@ -226,7 +236,7 @@ export default {
|
||||
].sort((a, b) => a.order - b.order);
|
||||
},
|
||||
hiddenCount() {
|
||||
return this.hiddenSlots.length;
|
||||
return this.hiddenSlots.length + this.hiddenPointBuys.length;
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
@@ -236,6 +246,16 @@ export default {
|
||||
variables() {
|
||||
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(){
|
||||
return CreatureProperties.find({
|
||||
type: 'propertySlot',
|
||||
@@ -284,7 +304,7 @@ export default {
|
||||
slotBuildTree(){
|
||||
const slots = CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'propertySlot',
|
||||
type: {$in: ['propertySlot', 'pointBuy']},
|
||||
$or: [
|
||||
{'slotCondition.value': {$nin: [false, 0, '']}},
|
||||
{'slotCondition.value': {$exists: false}},
|
||||
@@ -308,16 +328,15 @@ export default {
|
||||
]);
|
||||
traverse(tree, (child, parents) => {
|
||||
const model = child.node;
|
||||
const isSlotWithSpace = model.type === 'propertySlot' &&
|
||||
const isSlotWithSpace = model.type === 'propertySlot' && (
|
||||
model.spaceLeft > 0 ||
|
||||
!model.quantityExpected ||
|
||||
model.quantityExpected.value === 0;
|
||||
model.quantityExpected.value === 0
|
||||
);
|
||||
if(isSlotWithSpace) {
|
||||
model._canFill = true;
|
||||
parents.forEach(node => {
|
||||
if (node.node.type === 'propertySlot'){
|
||||
node.node._descendantCanFill = true;
|
||||
}
|
||||
node.node._descendantCanFill = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -385,7 +404,7 @@ export default {
|
||||
});
|
||||
},
|
||||
getPropertyTitle,
|
||||
unhideSlot(_id) {
|
||||
unhideProp(_id) {
|
||||
updateCreatureProperty.call({
|
||||
_id,
|
||||
path: ['ignored'],
|
||||
|
||||
@@ -5,6 +5,18 @@
|
||||
leave-absolute
|
||||
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
|
||||
v-for="slot in slots"
|
||||
:key="slot._id"
|
||||
@@ -13,7 +25,7 @@
|
||||
<slot-card
|
||||
:model="slot"
|
||||
hover
|
||||
@ignore="ignoreSlot(slot._id)"
|
||||
@ignore="ignoreProp(slot._id)"
|
||||
@click="fillSlot(slot._id)"
|
||||
/>
|
||||
</div>
|
||||
@@ -24,6 +36,7 @@
|
||||
<script lang="js">
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
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 updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.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 {
|
||||
components: {
|
||||
SlotCard,
|
||||
PointBuyCard,
|
||||
ColumnLayout,
|
||||
},
|
||||
inject: {
|
||||
context: { default: {} }
|
||||
},
|
||||
methods: {
|
||||
ignoreSlot(_id){
|
||||
ignoreProp(_id){
|
||||
updateCreatureProperty.call({
|
||||
_id,
|
||||
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: {
|
||||
slots(){
|
||||
@@ -99,7 +123,16 @@ export default {
|
||||
removed: {$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>
|
||||
|
||||
@@ -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>
|
||||
@@ -1,6 +1,9 @@
|
||||
<template lang="html">
|
||||
<div class="point-buy-spend-form">
|
||||
<v-row dense>
|
||||
<v-row
|
||||
v-if="model.values && model.values.length"
|
||||
dense
|
||||
>
|
||||
<v-col
|
||||
cols="10"
|
||||
md="11"
|
||||
@@ -67,6 +70,7 @@
|
||||
dense
|
||||
>
|
||||
<v-col
|
||||
v-if="typeof model.spent === 'number'"
|
||||
cols="12"
|
||||
class="text-h4 mb-4 pr-8 d-flex justify-end"
|
||||
:class="{
|
||||
@@ -74,7 +78,10 @@
|
||||
'warning--text': model.spent < (model.total && model.total.value),
|
||||
}"
|
||||
>
|
||||
{{ 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>
|
||||
@@ -88,6 +95,7 @@ export default {
|
||||
components: {
|
||||
CalculationErrorList,
|
||||
},
|
||||
mixins: [propertyFormMixin],
|
||||
methods: {
|
||||
max(row) {
|
||||
return row.max ? row.max && row.max.value : this.model.max && this.model.max.value;
|
||||
@@ -96,6 +104,5 @@ export default {
|
||||
return row.min ? row.min && row.min.value : this.model.min && this.model.min.value;
|
||||
},
|
||||
},
|
||||
mixins: [propertyFormMixin],
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user