Refactored actions, 'cast a spell' task now works
This commit is contained in:
@@ -37,15 +37,15 @@ export default {
|
||||
methods: {
|
||||
rest(){
|
||||
this.loading = true;
|
||||
const emptyProp = {
|
||||
_id: this.creatureId,
|
||||
root: { id: this.creatureId },
|
||||
};
|
||||
doAction(emptyProp, this.$store, `rest-btn-${this.type}`, {
|
||||
subtaskFn: 'reset',
|
||||
prop: emptyProp,
|
||||
targetIds: [this.creatureId],
|
||||
eventName: this.type,
|
||||
doAction({
|
||||
creatureId: this.creatureId,
|
||||
$store: this.$store,
|
||||
elementId: `rest-btn-${this.type}`,
|
||||
task: {
|
||||
subtaskFn: 'reset',
|
||||
targetIds: [this.creatureId],
|
||||
eventName: this.type,
|
||||
},
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
}).finally(() => {
|
||||
|
||||
@@ -41,38 +41,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-card-actions>
|
||||
<v-btn
|
||||
text
|
||||
@click="cancel"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-spacer slot="actions" />
|
||||
<v-btn
|
||||
v-show="!actionDone"
|
||||
text
|
||||
:disabled="!userInputReady || !resumeActionFn"
|
||||
@click="stepAction"
|
||||
>
|
||||
Step
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="actionDone"
|
||||
text
|
||||
@click="finishAction"
|
||||
>
|
||||
{{ 'Apply Results' }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-else
|
||||
text
|
||||
:disabled="actionBusy"
|
||||
@click="startAction"
|
||||
>
|
||||
{{ 'Start' }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -88,6 +56,7 @@ import EngineActions from '/imports/api/engine/action/EngineActions';
|
||||
import LogContent from '/imports/client/ui/log/LogContent.vue';
|
||||
//import RollInput from '/imports/client/ui/creature/actions/input/RollInput.vue';
|
||||
import TargetsInput from '/imports/client/ui/creature/actions/input/TargetsInput.vue';
|
||||
import CastSpellInput from '/imports/client/ui/creature/actions/input/CastSpellInput.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -98,6 +67,7 @@ export default {
|
||||
LogContent,
|
||||
//RollInput,
|
||||
TargetsInput,
|
||||
CastSpellInput,
|
||||
},
|
||||
props: {
|
||||
actionId: {
|
||||
@@ -161,7 +131,7 @@ export default {
|
||||
taskCount: undefined,
|
||||
};
|
||||
applyAction(
|
||||
this.actionResult, this, { simulate: true, stepThrough, task: this.task}
|
||||
this.actionResult, this, { simulate: true, stepThrough}
|
||||
).then(() => {
|
||||
this.actionDone = true;
|
||||
// If we aren't stepping through close the dialog and apply the action
|
||||
@@ -194,6 +164,7 @@ export default {
|
||||
this.activeInput = undefined;
|
||||
this.activeInputParams = {};
|
||||
this.userInputReady = false;
|
||||
console.log({savedInput})
|
||||
resolve(savedInput);
|
||||
}
|
||||
});
|
||||
@@ -248,6 +219,16 @@ export default {
|
||||
this.activeInput = 'check-input';
|
||||
return this.promiseInput();
|
||||
},
|
||||
async castSpell(suggestedParams) {
|
||||
this.userInput = suggestedParams;
|
||||
console.log(this.action);
|
||||
console.log(this.action.root);
|
||||
this.activeInputParams = {
|
||||
creatureId: this.action.creatureId,
|
||||
};
|
||||
this.activeInput = 'cast-spell-input';
|
||||
return this.promiseInput();
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -6,32 +6,46 @@ import InputProvider from '/imports/api/engine/action/functions/userInput/InputP
|
||||
import applyAction from '/imports/api/engine/action/functions/applyAction';
|
||||
import { runAction } from '/imports/api/engine/action/methods/runAction';
|
||||
import getDeterministicDiceRoller from '/imports/api/engine/action/functions/userInput/getDeterministicDiceRoller';
|
||||
import { getSingleProperty } from '../../../../api/engine/loadCreatures';
|
||||
|
||||
type BaseDoActionParams = {
|
||||
creatureId: string;
|
||||
$store: Store<any>;
|
||||
elementId: string;
|
||||
}
|
||||
|
||||
type DoTaskParams = BaseDoActionParams & {
|
||||
task: Task;
|
||||
propId?: undefined;
|
||||
}
|
||||
|
||||
type DoActionParams = BaseDoActionParams & {
|
||||
propId: string;
|
||||
task?: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an action on the client that first creates the action on both the client and server, then
|
||||
* simulates the action, opening the action dialog if necessary to get input from the user, saving
|
||||
* 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.
|
||||
*
|
||||
* @param prop The property initializing the action, if no task is applied the property will be
|
||||
* applied as the starting point of the action
|
||||
* @param $store The Vuex store instance that has the dialog stack
|
||||
* @param elementId The element to animate the dialog from if a dialog needs to open
|
||||
* @param task The task to apply instead of applying the property itself
|
||||
*/
|
||||
export default async function doAction(
|
||||
prop: { _id: string, root: { id: string } },
|
||||
$store: Store<any>,
|
||||
elementId: string,
|
||||
task?: Task,
|
||||
) {
|
||||
export default async function doAction({ propId, creatureId, $store, elementId, task }: DoActionParams | DoTaskParams) {
|
||||
if (!task) {
|
||||
if (!propId) throw new Meteor.Error('no-prop-id', 'Either propId or task must be provided');
|
||||
task = {
|
||||
prop: getSingleProperty(creatureId, propId),
|
||||
targetIds: [],
|
||||
};
|
||||
}
|
||||
// Create the action
|
||||
const actionId = insertAction.call({
|
||||
action: {
|
||||
creatureId: prop.root.id,
|
||||
rootPropId: prop._id,
|
||||
creatureId,
|
||||
task,
|
||||
results: [],
|
||||
taskCount: 0,
|
||||
_decisions: [],
|
||||
}
|
||||
});
|
||||
|
||||
@@ -45,9 +59,9 @@ export default async function doAction(
|
||||
// Either way, call the action method afterwards
|
||||
try {
|
||||
const finishedAction = await applyAction(
|
||||
action, getErrorOnInputRequestProvider(action._id), { simulate: true, task }
|
||||
action, getErrorOnInputRequestProvider(action._id), { simulate: true }
|
||||
);
|
||||
return callActionMethod(finishedAction, task);
|
||||
return callActionMethod(finishedAction);
|
||||
} catch (e) {
|
||||
if (e !== 'input-requested') throw e;
|
||||
return new Promise(resolve => {
|
||||
@@ -60,7 +74,7 @@ export default async function doAction(
|
||||
},
|
||||
callback(action: EngineAction) {
|
||||
if (!action) return;
|
||||
resolve(callActionMethod(action, task));
|
||||
resolve(callActionMethod(action));
|
||||
return elementId;
|
||||
},
|
||||
});
|
||||
@@ -68,10 +82,9 @@ export default async function doAction(
|
||||
}
|
||||
}
|
||||
|
||||
const callActionMethod = (action: EngineAction, task?: Task) => {
|
||||
const callActionMethod = (action: EngineAction) => {
|
||||
if (!action._id) throw new Meteor.Error('type-error', 'Action must have and _id');
|
||||
//@ts-expect-error callAsync not defined in types
|
||||
return runAction.callAsync({ actionId: action._id, decisions: action._decisions, task });
|
||||
return runAction.callAsync({ actionId: action._id, decisions: action._decisions });
|
||||
}
|
||||
|
||||
const throwInputRequestedError = () => {
|
||||
@@ -86,6 +99,7 @@ function getErrorOnInputRequestProvider(actionId) {
|
||||
choose: throwInputRequestedError,
|
||||
advantage: throwInputRequestedError,
|
||||
check: throwInputRequestedError,
|
||||
castSpell: throwInputRequestedError,
|
||||
}
|
||||
return errorOnInputRequest;
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Apply an action on the client that first creates the action on both the client and server, then
|
||||
* simulates the action, opening the action dialog if necessary to get input from the user, saving
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import Task from '/imports/api/engine/action/tasks/Task';
|
||||
|
||||
export default function doClientAction(propIdOrTask: string | Task) {
|
||||
|
||||
}
|
||||
@@ -1,70 +1,64 @@
|
||||
<template lang="html">
|
||||
<dialog-base>
|
||||
<template slot="toolbar">
|
||||
<v-toolbar-title>
|
||||
Cast a Spell
|
||||
</v-toolbar-title>
|
||||
<v-spacer />
|
||||
<text-field
|
||||
ref="focusFirst"
|
||||
label="Name"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
regular
|
||||
hide-details
|
||||
:value="searchValue"
|
||||
:error-messages="searchError"
|
||||
:debounce="200"
|
||||
@change="searchChanged"
|
||||
/>
|
||||
<v-menu
|
||||
v-model="filterMenuOpen"
|
||||
left
|
||||
:close-on-content-click="false"
|
||||
>
|
||||
<template #activator="{ on }">
|
||||
<div>
|
||||
<text-field
|
||||
ref="focusFirst"
|
||||
label="Name"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
regular
|
||||
hide-details
|
||||
:value="searchValue"
|
||||
:error-messages="searchError"
|
||||
:debounce="200"
|
||||
@change="searchChanged"
|
||||
/>
|
||||
<v-menu
|
||||
v-model="filterMenuOpen"
|
||||
left
|
||||
:close-on-content-click="false"
|
||||
>
|
||||
<template #activator="{ on }">
|
||||
<v-btn
|
||||
icon
|
||||
:class="{'primary--text': filtersApplied}"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon>mdi-filter</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="filter in booleanFilters"
|
||||
:key="filter.name"
|
||||
style="height: 52px;"
|
||||
>
|
||||
<v-checkbox
|
||||
v-model="filter.enabled"
|
||||
style="flex-grow: 0; margin-right: 8px;"
|
||||
/>
|
||||
<v-switch
|
||||
v-model="filter.value"
|
||||
:disabled="!filter.enabled"
|
||||
:label="filter.name"
|
||||
/>
|
||||
</v-list-item>
|
||||
<div class="layout">
|
||||
<v-btn
|
||||
icon
|
||||
:class="{'primary--text': filtersApplied}"
|
||||
v-on="on"
|
||||
text
|
||||
@click="clearBooleanFilters"
|
||||
>
|
||||
<v-icon>mdi-filter</v-icon>
|
||||
Clear
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="filter in booleanFilters"
|
||||
:key="filter.name"
|
||||
style="height: 52px;"
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
text
|
||||
class="primary--text"
|
||||
@click="filterMenuOpen = false"
|
||||
>
|
||||
<v-checkbox
|
||||
v-model="filter.enabled"
|
||||
style="flex-grow: 0; margin-right: 8px;"
|
||||
/>
|
||||
<v-switch
|
||||
v-model="filter.value"
|
||||
:disabled="!filter.enabled"
|
||||
:label="filter.name"
|
||||
/>
|
||||
</v-list-item>
|
||||
<div class="layout">
|
||||
<v-btn
|
||||
text
|
||||
@click="clearBooleanFilters"
|
||||
>
|
||||
Clear
|
||||
</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
text
|
||||
class="primary--text"
|
||||
@click="filterMenuOpen = false"
|
||||
>
|
||||
Done
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
Done
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<split-list-layout>
|
||||
<template slot="left">
|
||||
<div
|
||||
@@ -151,29 +145,19 @@
|
||||
</v-list-item-group>
|
||||
</template>
|
||||
</split-list-layout>
|
||||
<template slot="actions">
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
text
|
||||
@click="$store.dispatch('popDialogStack')"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
:disabled="!canCast"
|
||||
class="mx-2 px-4"
|
||||
color="primary"
|
||||
@click="cast"
|
||||
>
|
||||
Cast
|
||||
</v-btn>
|
||||
</template>
|
||||
</dialog-base>
|
||||
<v-btn
|
||||
text
|
||||
:disabled="!canCast"
|
||||
class="mx-2 px-4"
|
||||
color="primary"
|
||||
@click="cast"
|
||||
>
|
||||
Cast
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import DialogBase from '/imports/client/ui/dialogStack/DialogBase.vue';
|
||||
import SplitListLayout from '/imports/client/ui/properties/components/attributes/SplitListLayout.vue';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import spellsWithSubheaders from '/imports/client/ui/properties/components/spells/spellsWithSubheaders';
|
||||
@@ -193,7 +177,6 @@ const slotFilter = {
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DialogBase,
|
||||
SplitListLayout,
|
||||
SpellSlotListTile,
|
||||
SpellListTile,
|
||||
@@ -207,6 +190,10 @@ export default {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
spellId: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
@@ -215,8 +202,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
searchString: undefined,
|
||||
selectedSlotId: this.slotId,
|
||||
selectedSpellId: this.spellId,
|
||||
selectedSlotId: this.value.slotId,
|
||||
selectedSpellId: this.value.spellId,
|
||||
selectedSlot: undefined,
|
||||
selectedSpell: undefined,
|
||||
searchValue: undefined,
|
||||
@@ -253,7 +240,8 @@ export default {
|
||||
watch: {
|
||||
selectedSpellId: {
|
||||
handler(spellId) {
|
||||
this.selectedSpell = CreatureProperties.findOne(spellId)
|
||||
this.selectedSpell = CreatureProperties.findOne(spellId);
|
||||
this.$emit('input', { ...this.value, spellId });
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
@@ -291,6 +279,11 @@ export default {
|
||||
selectedSlotId: {
|
||||
handler(slotId) {
|
||||
this.selectedSlot = CreatureProperties.findOne(slotId);
|
||||
if (slotId === 'ritual') {
|
||||
this.$emit('input', { ...this.value, slotId: undefined, ritual: true });
|
||||
} else {
|
||||
this.$emit('input', { ...this.value, slotId, ritual: false });
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
@@ -347,14 +340,7 @@ export default {
|
||||
}
|
||||
},
|
||||
cast() {
|
||||
let selectedSlotId = this.selectedSlotId;
|
||||
const ritual = selectedSlotId === 'ritual';
|
||||
if (selectedSlotId === 'no-slot' || selectedSlotId === 'ritual') selectedSlotId = undefined;
|
||||
this.$store.dispatch('popDialogStack', {
|
||||
spellId: this.selectedSpellId,
|
||||
slotId: selectedSlotId,
|
||||
ritual,
|
||||
});
|
||||
this.$emit('continue');
|
||||
}
|
||||
},
|
||||
meteor: {
|
||||
@@ -615,16 +615,20 @@ export default {
|
||||
incrementChange(_id, { type, value, ack }) {
|
||||
const model = CreatureProperties.findOne(_id);
|
||||
if (type === 'increment') value = -value;
|
||||
doAction(model, this.$store, model._id, {
|
||||
subtaskFn: 'damageProp',
|
||||
prop: model,
|
||||
targetIds: [model.root.id],
|
||||
params: {
|
||||
title: getPropertyTitle(model),
|
||||
operation: type,
|
||||
value,
|
||||
targetProp: model,
|
||||
}
|
||||
doAction({
|
||||
creatureId: model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: `${model._id}-${type}`,
|
||||
task: {
|
||||
subtaskFn: 'damageProp',
|
||||
targetIds: [model.root.id],
|
||||
params: {
|
||||
title: getPropertyTitle(model),
|
||||
operation: type,
|
||||
value,
|
||||
targetProp: model,
|
||||
},
|
||||
},
|
||||
}).then(() =>{
|
||||
ack?.();
|
||||
}).catch((error) => {
|
||||
|
||||
@@ -180,16 +180,21 @@ export default {
|
||||
},
|
||||
damage({operation, value, ack}){
|
||||
const model = this.model;
|
||||
doAction(model, this.$store, model._id, {
|
||||
subtaskFn: 'damageProp',
|
||||
prop: model,
|
||||
targetIds: [model.root.id],
|
||||
params: {
|
||||
title: getPropertyTitle(model),
|
||||
operation: operation,
|
||||
value,
|
||||
targetProp: model,
|
||||
}
|
||||
doAction({
|
||||
creatureId: model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: '??',
|
||||
task: {
|
||||
subtaskFn: 'damageProp',
|
||||
prop: model,
|
||||
targetIds: [model.root.id],
|
||||
params: {
|
||||
title: getPropertyTitle(model),
|
||||
operation: operation,
|
||||
value,
|
||||
targetProp: model,
|
||||
}
|
||||
},
|
||||
}).then(() =>{
|
||||
ack?.();
|
||||
}).catch((error) => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Load commonly used dialogs immediately
|
||||
import ActionDialog from '/imports/client/ui/creature/actions/ActionDialog.vue';
|
||||
import CastSpellWithSlotDialog from '/imports/client/ui/properties/components/spells/CastSpellWithSlotDialog.vue';
|
||||
import CharacterCreationDialog from '/imports/client/ui/creature/character/CharacterCreationDialog.vue';
|
||||
import CharacterSheetDialog from '/imports/client/ui/tabletop/CharacterSheetDialog.vue';
|
||||
import CreatureFormDialog from '/imports/client/ui/creature/CreatureFormDialog.vue';
|
||||
@@ -42,7 +41,6 @@ const UsernameDialog = () => import('/imports/client/ui/user/UsernameDialog.vue'
|
||||
export default {
|
||||
ActionDialog,
|
||||
ArchiveDialog,
|
||||
CastSpellWithSlotDialog,
|
||||
CharacterCreationDialog,
|
||||
CharacterImportDialog,
|
||||
CharacterSheetDialog,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
outlined
|
||||
style="font-size: 16px; letter-spacing: normal;"
|
||||
class="mr-2"
|
||||
data-id="do-action-button"
|
||||
:color="model.color || 'primary'"
|
||||
:loading="doActionLoading"
|
||||
:disabled="model.insufficientResources || !context.editPermission || !!targetingError"
|
||||
@@ -224,7 +225,12 @@ export default {
|
||||
},
|
||||
doAction() {
|
||||
this.doActionLoading = true;
|
||||
doAction(this.model, this.$store, this.model._id).catch((e) => {
|
||||
doAction({
|
||||
propId: this.model._id,
|
||||
creatureId: this.model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: 'do-action-button',
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
}).finally(() => {
|
||||
this.doActionLoading = false;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<script lang="js">
|
||||
import doAction from '/imports/client/ui/creature/actions/doAction';
|
||||
import PropertyIcon from '/imports/client/ui/properties/shared/PropertyIcon.vue';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -39,10 +40,22 @@ export default {
|
||||
},
|
||||
data(){return {
|
||||
hovering: false,
|
||||
loading: false,
|
||||
}},
|
||||
methods: {
|
||||
doAction() {
|
||||
doAction(this.model, this.$store, `event-btn-${this.model._id}`);
|
||||
async doAction() {
|
||||
this.loading = true;
|
||||
doAction({
|
||||
propId: this.model._id,
|
||||
creatureId: this.model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: `event-btn-${this.model._id}`,
|
||||
}).catch(error => {
|
||||
snackbar({ text: error.reason || error.message || error.toString() });
|
||||
console.error(error);
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,14 +92,19 @@ export default {
|
||||
},
|
||||
check() {
|
||||
this.checkLoading = true;
|
||||
doAction(this.model, this.$store, `check-btn-${this.model._id}`, {
|
||||
subtaskFn: 'check',
|
||||
prop: this.model,
|
||||
targetIds: [this.model.root.id],
|
||||
advantage: this.model.advantage,
|
||||
skillVariableName: undefined,
|
||||
abilityVariableName: this.model.variableName,
|
||||
dc: null,
|
||||
doAction({
|
||||
creatureId: this.model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: `check-btn-${this.model._id}`,
|
||||
task: {
|
||||
subtaskFn: 'check',
|
||||
prop: this.model,
|
||||
targetIds: [this.model.root.id],
|
||||
advantage: this.model.advantage,
|
||||
skillVariableName: undefined,
|
||||
abilityVariableName: this.model.variableName,
|
||||
dc: null,
|
||||
},
|
||||
}).catch(error => {
|
||||
snackbar({ text: error.reason || error.message || error.toString() });
|
||||
console.error(error);
|
||||
|
||||
@@ -77,14 +77,19 @@ export default {
|
||||
signed: numberToSignedString,
|
||||
check(){
|
||||
this.checkLoading = true;
|
||||
doAction(this.model, this.$store, `check-btn-${this.model._id}`, {
|
||||
subtaskFn: 'check',
|
||||
prop: this.model,
|
||||
targetIds: [this.model.root.id],
|
||||
advantage: this.model.advantage,
|
||||
skillVariableName: this.model.variableName,
|
||||
abilityVariableName: this.model.ability,
|
||||
dc: null,
|
||||
doAction({
|
||||
creatureId: this.model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: `check-btn-${this.model._id}`,
|
||||
task: {
|
||||
subtaskFn: 'check',
|
||||
prop: this.model,
|
||||
targetIds: [this.model.root.id],
|
||||
advantage: this.model.advantage,
|
||||
skillVariableName: this.model.variableName,
|
||||
abilityVariableName: this.model.ability,
|
||||
dc: null,
|
||||
},
|
||||
}).catch(error => {
|
||||
snackbar({ text: error.reason || error.message || error.toString() });
|
||||
console.error(error);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
color="accent"
|
||||
style="width: 100%;"
|
||||
outlined
|
||||
data-id="cast-spell-btn"
|
||||
@click="castSpell"
|
||||
>
|
||||
Cast a spell
|
||||
@@ -32,8 +33,8 @@
|
||||
|
||||
<script lang="js">
|
||||
import SpellSlotListTile from '/imports/client/ui/properties/components/attributes/SpellSlotListTile.vue';
|
||||
import doAction from '/imports/client/ui/creature/actions/doAction';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
import doCastSpell from '/imports/api/engine/action/methods/doCastSpell';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -50,38 +51,31 @@ export default {
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data(){return {
|
||||
castSpellLoading: false,
|
||||
}},
|
||||
methods: {
|
||||
castSpell() {
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'cast-spell-with-slot-dialog',
|
||||
elementId: 'spell-slot-card',
|
||||
data: {
|
||||
creatureId: this.creatureId,
|
||||
},
|
||||
callback({ spellId, slotId, advantage, ritual } = {}) {
|
||||
if (!spellId) return;
|
||||
doCastSpell.call({
|
||||
spellId,
|
||||
slotId,
|
||||
ritual,
|
||||
scope: {
|
||||
'~attackAdvantage': { value: advantage },
|
||||
},
|
||||
}, error => {
|
||||
if (!error) return;
|
||||
snackbar({ text: error.reason || error.message || error.toString() });
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
this.castSpellLoading = true;
|
||||
doAction({
|
||||
creatureId: this.model.root.id,
|
||||
propId: this.model._id,
|
||||
$store: this.$store,
|
||||
elementId: `spell-slot-card-${this.model._id}`
|
||||
}).catch(error => {
|
||||
snackbar({ text: error.reason || error.message || error.toString() });
|
||||
console.error(error);
|
||||
}).finally(() => {
|
||||
this.castSpellLoading = false;
|
||||
});
|
||||
},
|
||||
clickProperty({ _id }) {
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
elementId: `spell-slot-card-${_id}`,
|
||||
elementId: 'cast-spell-btn',
|
||||
data: { _id },
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>../../../../../api/engine/action/methods/doCastSpell
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template lang="html">
|
||||
<v-list-item
|
||||
:key="model._id"
|
||||
:data-id="`spell-slot-list-tile-${model._id}`"
|
||||
class="spell-slot-list-tile"
|
||||
v-bind="$attrs"
|
||||
v-on="hasClickListener ? {click} : {}"
|
||||
@@ -91,16 +92,15 @@ export default {
|
||||
this.$emit('click', e);
|
||||
},
|
||||
disabled(i) {
|
||||
if (!this.context.editPermission) return true;
|
||||
// Use these if only the next filled or empty slot can be clicked
|
||||
// if (this.model.value === i) return false;
|
||||
// if (this.model.value === i - 1) return false;
|
||||
// return true
|
||||
return false;
|
||||
return !this.context.editPermission;
|
||||
},
|
||||
damageProperty({ type, value, ack }) {
|
||||
const model = this.model;
|
||||
doAction(model, this.$store, model._id, {
|
||||
doAction({
|
||||
creatureId: model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: `spell-slot-list-tile-${model._id}`,
|
||||
task: {
|
||||
subtaskFn: 'damageProp',
|
||||
prop: model,
|
||||
targetIds: [model.root.id],
|
||||
@@ -110,7 +110,7 @@ export default {
|
||||
value,
|
||||
targetProp: model,
|
||||
}
|
||||
}).then(() =>{
|
||||
}}).then(() =>{
|
||||
ack?.();
|
||||
}).catch((error) => {
|
||||
if (ack) {
|
||||
|
||||
@@ -95,17 +95,22 @@ export default {
|
||||
damageProperty({value, type, ack}) {
|
||||
const model = this.model;
|
||||
if (type === 'increment') value = -value;
|
||||
doAction(model, this.$store, model._id, {
|
||||
subtaskFn: 'damageProp',
|
||||
prop: model,
|
||||
targetIds: [model.root.id],
|
||||
params: {
|
||||
title: getPropertyTitle(model),
|
||||
operation: type,
|
||||
value,
|
||||
targetProp: model,
|
||||
doAction({
|
||||
creatureId: model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: this.dataId,
|
||||
task: {
|
||||
subtaskFn: 'damageProp',
|
||||
prop: model,
|
||||
targetIds: [model.root.id],
|
||||
params: {
|
||||
title: getPropertyTitle(model),
|
||||
operation: type,
|
||||
value,
|
||||
targetProp: model,
|
||||
}
|
||||
}
|
||||
}).then(() =>{
|
||||
}).then(() => {
|
||||
ack?.();
|
||||
}).catch((error) => {
|
||||
if (ack) {
|
||||
@@ -127,4 +132,4 @@ export default {
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -104,14 +104,19 @@ export default {
|
||||
},
|
||||
check() {
|
||||
this.checkLoading = true;
|
||||
doAction(this.model, this.$store, `check-btn-${this.model._id}`, {
|
||||
subtaskFn: 'check',
|
||||
prop: this.model,
|
||||
targetIds: [this.model.root.id],
|
||||
advantage: this.model.advantage,
|
||||
skillVariableName: this.model.variableName,
|
||||
abilityVariableName: this.model.ability,
|
||||
dc: null,
|
||||
doAction({
|
||||
creatureId: this.model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: `check-btn-${this.model._id}`,
|
||||
task: {
|
||||
subtaskFn: 'check',
|
||||
prop: this.model,
|
||||
targetIds: [this.model.root.id],
|
||||
advantage: this.model.advantage,
|
||||
skillVariableName: this.model.variableName,
|
||||
abilityVariableName: this.model.ability,
|
||||
dc: null,
|
||||
},
|
||||
}).catch(error => {
|
||||
snackbar({ text: error.reason || error.message || error.toString() });
|
||||
console.error(error);
|
||||
@@ -135,4 +140,4 @@ export default {
|
||||
.v-icon.theme--light {
|
||||
color: rgba(0, 0, 0, 0.54) !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -119,13 +119,11 @@
|
||||
|
||||
<script lang="js">
|
||||
import propertyViewerMixin from '/imports/client/ui/properties/viewers/shared/propertyViewerMixin';
|
||||
//TODO import doAction from '/imports/api/engine/actions/doAction';
|
||||
import ActionConditionView from '/imports/client/ui/properties/components/actions/ActionConditionView.vue';
|
||||
import AttributeConsumedView from '/imports/client/ui/properties/components/actions/AttributeConsumedView.vue';
|
||||
import ItemConsumedView from '/imports/client/ui/properties/components/actions/ItemConsumedView.vue';
|
||||
import PropertyIcon from '/imports/client/ui/properties/shared/PropertyIcon.vue';
|
||||
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty';
|
||||
import doCastSpell from '/imports/api/engine/action/methods/doCastSpell.js';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
import doAction from '/imports/client/ui/creature/actions/doAction';
|
||||
|
||||
@@ -190,7 +188,12 @@ export default {
|
||||
methods: {
|
||||
doAction() {
|
||||
this.doActionLoading = true;
|
||||
doAction(this.model, this.$store, this.model._id).catch((e) => {
|
||||
doAction({
|
||||
creatureId: this.model.root.id,
|
||||
$store: this.$store,
|
||||
propId: this.model._id,
|
||||
elementId: 'do-action-button',
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
snackbar({ text: e.message || e.reason || e.toString() });
|
||||
}).finally(() => {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
:style="{
|
||||
fontSize: '24px'
|
||||
}"
|
||||
data-id="do-action-button"
|
||||
:color="model.color || 'primary'"
|
||||
:loading="doActionLoading"
|
||||
:disabled="model.insufficientResources || !context.editPermission"
|
||||
@@ -217,7 +218,12 @@ export default {
|
||||
doAction() {
|
||||
this.doActionLoading = true;
|
||||
this.$emit('close-menu')
|
||||
doAction(this.model, this.$store, this.model._id).catch((e) => {
|
||||
doAction({
|
||||
propId: this.model._id,
|
||||
creatureId: this.model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: 'do-action-button',
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
snackbar({ text: e.message || e.reason || e.toString() });
|
||||
}).finally(() => {
|
||||
|
||||
@@ -57,8 +57,6 @@
|
||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString.js';
|
||||
import doAction from '/imports/client/ui/creature/actions/doAction';
|
||||
import AttributeConsumedView from '/imports/client/ui/properties/components/actions/AttributeConsumedView.vue';
|
||||
import ItemConsumedView from '/imports/client/ui/properties/components/actions/ItemConsumedView.vue';
|
||||
import PropertyIcon from '/imports/client/ui/properties/shared/PropertyIcon.vue';
|
||||
import MarkdownText from '/imports/client/ui/components/MarkdownText.vue';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue.js';
|
||||
@@ -69,8 +67,6 @@ import { some } from 'lodash';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AttributeConsumedView,
|
||||
ItemConsumedView,
|
||||
MarkdownText,
|
||||
PropertyIcon,
|
||||
TreeNodeList,
|
||||
@@ -175,7 +171,12 @@ export default {
|
||||
doAction() {
|
||||
this.doActionLoading = true;
|
||||
this.$emit('close-menu')
|
||||
doAction(this.model, this.$store, this.model._id).catch((e) => {
|
||||
doAction({
|
||||
propId: this.model._id,
|
||||
creatureId: this.model.root.id,
|
||||
$store: this.$store,
|
||||
elementId: 'do-action-button',
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
snackbar({ text: e.message || e.reason || e.toString() });
|
||||
}).finally(() => {
|
||||
|
||||
@@ -166,7 +166,9 @@ export default {
|
||||
actions(){
|
||||
return getProperties(this.activeCreatureId, { type: 'action', actionType: { $ne: 'event'} });
|
||||
},
|
||||
moreTargets(){
|
||||
moreTargets() {
|
||||
// Disable portrait targeting for now, they aren't used by the action engine yet
|
||||
return false;
|
||||
const activeAction = CreatureProperties.findOne(this.activeActionId);
|
||||
if (!activeAction) return;
|
||||
if (activeAction.target === 'singleTarget') {
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
<v-card
|
||||
v-else-if="activeIcon && activeIcon.tab"
|
||||
style="width: 300px"
|
||||
data-id="tabletop-standard-card"
|
||||
>
|
||||
<v-card-title>
|
||||
<v-icon left>
|
||||
@@ -167,33 +168,15 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
|
||||
import TabletopActionCard from '/imports/client/ui/tabletop/TabletopActionCard.vue';
|
||||
import TabletopBuffCard from '/imports/client/ui/tabletop/TabletopBuffCard.vue';
|
||||
import CreatureBarIcon from '/imports/client/ui/tabletop/selectedCreatureBar/CreatureBarIcon.vue';
|
||||
import { compact } from 'lodash';
|
||||
|
||||
//import TabletopPortrait from '/imports/client/ui/tabletop/selectedCreatureBar/TabletopPortrait.vue';
|
||||
//import TabletopBuffIcons from '/imports/client/ui/tabletop/selectedCreatureBar/TabletopBuffIcons.vue';
|
||||
//import TabletopActions from '/imports/client/ui/tabletop/selectedCreatureBar/TabletopActions.vue';
|
||||
//import TabletopGroupedFolders from '/imports/client/ui/tabletop/selectedCreatureBar/TabletopGroupedFolders.vue';
|
||||
//import TabletopResources from '/imports/client/ui/tabletop/selectedCreatureBar/TabletopResources.vue';
|
||||
//import TabletopCreatureSheetTabs from '/imports/client/ui/tabletop/selectedCreatureBar/TabletopCreatureSheetTabs.vue';
|
||||
//import TabletopDetailPopover from '/imports/client/ui/tabletop/selectedCreatureBar/TabletopDetailPopover.vue';
|
||||
import { compact, chunk } from 'lodash';
|
||||
import doAction from '../../creature/actions/doAction';
|
||||
|
||||
function splitToNChunks(inputArray, n) {
|
||||
let result = [];
|
||||
const array = [...inputArray] // Create shallow copy, because splice mutates array
|
||||
for (let i = n; i > 0; i--) {
|
||||
result.push(array.splice(0, Math.ceil(array.length / i)));
|
||||
}
|
||||
return result;
|
||||
return chunk(inputArray, Math.ceil(inputArray.length / n));
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
//TabletopPortrait,
|
||||
//TabletopBuffIcons,
|
||||
//TabletopActions,
|
||||
//TabletopGroupedFolders,
|
||||
//TabletopResources,
|
||||
//TabletopCreatureSheetTabs,
|
||||
CreatureBarIcon,
|
||||
TabletopActionCard,
|
||||
TabletopBuffCard,
|
||||
@@ -261,6 +244,7 @@ export default {
|
||||
}
|
||||
if (icon.actionName) {
|
||||
this.openStandardAction(icon.standardId)
|
||||
return;
|
||||
}
|
||||
if (this.selectedIcon === icon) {
|
||||
this.selectedIcon = undefined;
|
||||
@@ -285,6 +269,7 @@ export default {
|
||||
return outside;
|
||||
},
|
||||
openCharacterSheet(tab, elementId) {
|
||||
this.menuOpen = false;
|
||||
this.$store.commit(
|
||||
'setTabForCharacterSheet',
|
||||
{ id: this.creatureId, tab }
|
||||
@@ -296,13 +281,26 @@ export default {
|
||||
creatureId: this.creatureId,
|
||||
},
|
||||
});
|
||||
this.menuOpen = false;
|
||||
},
|
||||
openStandardAction(standardId) {
|
||||
// TODO standard action dialogs
|
||||
this.menuOpen = false;
|
||||
if (standardId === 'cast-spell') {
|
||||
doAction({
|
||||
creatureId: this.creatureId,
|
||||
$store: this.$store,
|
||||
elementId: standardId,
|
||||
task: {
|
||||
subtaskFn: 'castSpell',
|
||||
targetIds: [],
|
||||
params: {
|
||||
spellId: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
openPropertyDetails(elementId) {
|
||||
this.menuOpen = false;
|
||||
const propId = this.selectedProp._id;
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
@@ -310,8 +308,6 @@ export default {
|
||||
data: { _id: propId },
|
||||
callback: () => propId
|
||||
});
|
||||
// Close the menu while the dialog is open
|
||||
this.menuOpen = false;
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
@@ -344,7 +340,7 @@ export default {
|
||||
|
||||
// Get the folders that could hide a property
|
||||
const folderGroupsById = {};
|
||||
CreatureProperties.find({
|
||||
CreatureProperties.find({
|
||||
'root.id': this.creatureId,
|
||||
type: 'folder',
|
||||
groupStats: true,
|
||||
@@ -475,4 +471,4 @@ export default {
|
||||
.tabletop-prop-menu.rows-4 {
|
||||
bottom: 212px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user