Started fixing action target selection
This commit is contained in:
@@ -35,7 +35,7 @@ const ActionSchema = new SimpleSchema({
|
||||
},
|
||||
targetIds: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
optional: true,
|
||||
},
|
||||
'targetIds.$': {
|
||||
type: String,
|
||||
|
||||
@@ -40,14 +40,28 @@ export default async function applyAction(action: EngineAction, userInput: Input
|
||||
action._isSimulation = simulate;
|
||||
action.taskCount = 0;
|
||||
let task = options?.task;
|
||||
console.log('task', task, action.targetIds)
|
||||
if (!task) {
|
||||
const prop = await getSingleProperty(action.creatureId, action.rootPropId);
|
||||
if (!prop) throw new Meteor.Error('Not found', 'Root action property could not be found');
|
||||
|
||||
// If the target ids weren't already set, get them from the user
|
||||
console.log(action.targetIds, prop);
|
||||
if (!action.targetIds && (
|
||||
prop.target === 'singleTarget' ||
|
||||
prop.target === 'multipleTargets'
|
||||
)) {
|
||||
console.log('getting targetIds');
|
||||
action.targetIds = await (userInput.targetIds(prop.targets));
|
||||
console.log('got targetIds', action.targetIds);
|
||||
}
|
||||
|
||||
task = {
|
||||
prop,
|
||||
targetIds: action.targetIds || [],
|
||||
}
|
||||
}
|
||||
|
||||
await applyTask(action, task, userInput);
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ export default async function spendResources(
|
||||
!quantity ||
|
||||
!isFinite(quantity)
|
||||
) continue;
|
||||
|
||||
await applyTask(action, {
|
||||
prop,
|
||||
targetIds,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import Task from '/imports/api/engine/action/tasks/Task';
|
||||
|
||||
type InputProvider = {
|
||||
/**
|
||||
* Get the ids of the creatures being targeted
|
||||
*/
|
||||
targetIds(target: 'singleTarget' | 'multipleTargets', currentTargetIds?: string[]): Promise<string[]>;
|
||||
/**
|
||||
* Show the user the next property or task to apply and wait for input to continue
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,9 @@ export default function getReplayChoicesInputProvider(actionId: string, decision
|
||||
const decisionStack = [...decisions].reverse();
|
||||
const dRoller = getDeterministicDiceRoller(actionId);
|
||||
const replaySavedInput: InputProvider = {
|
||||
targetIds() {
|
||||
return Promise.resolve(decisionStack.pop());
|
||||
},
|
||||
nextStep() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
|
||||
|
||||
const inputProviderForTests: InputProvider = {
|
||||
async targetIds(target, currentTargetIds = []) {
|
||||
return currentTargetIds;
|
||||
},
|
||||
/**
|
||||
* For testing, randomness is hard to deal with
|
||||
* rollDice function returns the average roll for every dice rolled
|
||||
|
||||
@@ -47,7 +47,7 @@ export default async function applyItemAsAmmoTask(task: ItemAsAmmoTask, action:
|
||||
type: 'item',
|
||||
}],
|
||||
// Log the item name as a heading if it has child properties to apply
|
||||
...itemChildren.length && {
|
||||
...itemChildren.length && !task.params.skipChildren && {
|
||||
contents: [{
|
||||
name: getPropertyTitle(item) || 'Ammo',
|
||||
inline: false,
|
||||
|
||||
@@ -651,8 +651,12 @@ export function hasAncestorRelationship(propA: TreeDoc, propB: TreeDoc): boolean
|
||||
if (propA.root.id !== propB.root.id) {
|
||||
return false;
|
||||
}
|
||||
// Return if there is an ancestor relationship in either direction
|
||||
return isAncestor(propA, propB) || isAncestor(propB, propA);
|
||||
// Return if there is an parent relationship in either direction
|
||||
return propA.parentId === propB._id
|
||||
|| propB.parentId === propA._id
|
||||
// or an ancestor relationship in either direction
|
||||
|| isAncestor(propA, propB)
|
||||
|| isAncestor(propB, propA);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -856,4 +860,5 @@ function writeBulkOperations(collection: Mongo.Collection<TreeDoc>, operations)
|
||||
}
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -86,7 +86,8 @@ import ChoiceInput from '/imports/client/ui/creature/actions/input/ChoiceInput.v
|
||||
import DialogBase from '/imports/client/ui/dialogStack/DialogBase.vue';
|
||||
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 RollInput from '/imports/client/ui/creature/actions/input/RollInput.vue';
|
||||
import TargetsInput from '/imports/client/ui/creature/actions/input/TargetsInput.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -95,7 +96,8 @@ export default {
|
||||
ChoiceInput,
|
||||
DialogBase,
|
||||
LogContent,
|
||||
RollInput,
|
||||
//RollInput,
|
||||
TargetsInput,
|
||||
},
|
||||
props: {
|
||||
actionId: {
|
||||
@@ -203,6 +205,16 @@ export default {
|
||||
this.$store.dispatch('popDialogStack');
|
||||
},
|
||||
// inputProvider methods
|
||||
async targetIds(target) {
|
||||
console.log('input provider UI targetIds')
|
||||
this.userInput = [];
|
||||
this.activeInputParams = {
|
||||
target,
|
||||
tabletopId: this.action.tabletopId,
|
||||
};
|
||||
this.activeInput = 'targets-input'
|
||||
return this.promiseInput();
|
||||
},
|
||||
async rollDice(dice) {
|
||||
return Promise.resolve(this.deterministicDiceRoller(dice));
|
||||
/* Dice Animation and user control goes here:
|
||||
|
||||
@@ -79,6 +79,7 @@ const throwInputRequestedError = () => {
|
||||
|
||||
function getErrorOnInputRequestProvider(actionId) {
|
||||
const errorOnInputRequest: InputProvider = {
|
||||
targetIds: throwInputRequestedError,
|
||||
nextStep: throwInputRequestedError,
|
||||
rollDice: getDeterministicDiceRoller(actionId),
|
||||
choose: throwInputRequestedError,
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="choice-input">
|
||||
<creature-list-tile
|
||||
v-for="creature in creatures"
|
||||
:key="creature._id"
|
||||
:model="creature"
|
||||
selection
|
||||
:selected="value.includes(creature._id)"
|
||||
@click="selectCreature(creature._id)"
|
||||
/>
|
||||
<v-btn
|
||||
@click="$emit('continue');"
|
||||
>
|
||||
Done
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import CreatureListTile from '/imports/client/ui/creature/creatureList/CreatureListTile.vue';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CreatureListTile
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
target: {
|
||||
type: String,
|
||||
default: 'multipleTargets',
|
||||
},
|
||||
tabletopId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
selectCreature(id) {
|
||||
let newValue;
|
||||
if (this.value.includes(id)) {
|
||||
newValue = this.value.filter((creatureId) => creatureId !== id);
|
||||
} else if (this.target === 'singleTarget') {
|
||||
newValue = [id];
|
||||
} else {
|
||||
newValue = [...this.value, id];
|
||||
}
|
||||
this.$emit('input', newValue);
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
creatures() {
|
||||
return Creatures.find({
|
||||
tabletopId: this.tabletopId,
|
||||
}, {
|
||||
sort: {
|
||||
name: 1,
|
||||
},
|
||||
}).fetch();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -16,7 +16,7 @@
|
||||
class="creature"
|
||||
:model="creature"
|
||||
:selection="selection"
|
||||
:is-selected="selectedCreature === creature._id"
|
||||
:is-selected="selectedCreature === creature._id || selectedCreatures.has(creature._id)"
|
||||
v-bind="selection ? {} : {to: creature.url}"
|
||||
:dense="dense"
|
||||
:data-id="dense ? undefined : creature._id"
|
||||
@@ -50,6 +50,10 @@
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
selectedCreatures: {
|
||||
type: Set,
|
||||
default: () => new Set(),
|
||||
},
|
||||
dense: Boolean,
|
||||
},
|
||||
data(){return {
|
||||
|
||||
@@ -216,6 +216,7 @@ export default {
|
||||
},
|
||||
doAction() {
|
||||
this.doActionLoading = true;
|
||||
this.$emit('close-menu')
|
||||
doAction(this.model, this.$store, this.model._id).catch((e) => {
|
||||
console.error(e);
|
||||
snackbar({ text: e.message || e.reason || e.toString() });
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
<selected-creature-bar
|
||||
:key="activeCreatureId"
|
||||
:creature-id="activeCreatureId"
|
||||
@active-action-change="activeActionId = $event"
|
||||
/>
|
||||
</v-slide-y-reverse-transition>
|
||||
</v-footer>
|
||||
@@ -167,6 +168,7 @@ export default {
|
||||
},
|
||||
moreTargets(){
|
||||
const activeAction = CreatureProperties.findOne(this.activeActionId);
|
||||
console.log(this.activeActionId, activeAction)
|
||||
if (!activeAction) return;
|
||||
if (activeAction.target === 'singleTarget') {
|
||||
return this.targets.length === 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="html">
|
||||
<v-card
|
||||
:style="`height: ${height}px; width: ${width}px; overflow: hidden;`"
|
||||
:style="`height: ${height}px; width: ${width}px;`"
|
||||
class="tabletop-creature-card"
|
||||
:class="{ active }"
|
||||
:hover="hasClickListener"
|
||||
@@ -31,7 +31,6 @@
|
||||
style="opacity: 0.7; margin-top: 2px"
|
||||
/>
|
||||
</v-img>
|
||||
<card-highlight :active="hover" />
|
||||
<div class="d-flex justify-center">
|
||||
<v-scale-transition>
|
||||
<v-btn
|
||||
@@ -40,7 +39,7 @@
|
||||
:elevation="targeted ? 8 : 2"
|
||||
fab
|
||||
small
|
||||
@click.stop="targeted ? $emit('untarget') : $emit('target')"
|
||||
@click.stop.prevent="targeted ? $emit('untarget') : $emit('target')"
|
||||
>
|
||||
<v-icon>{{ targeted ? 'mdi-target' : 'mdi-target' }}</v-icon>
|
||||
</v-btn>
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
transition: 'opacity 0.2s ease',
|
||||
}"
|
||||
:model="selectedProp"
|
||||
@close-menu="menuOpen = false"
|
||||
/>
|
||||
<v-card
|
||||
v-else-if="activeIcon && activeIcon.tab"
|
||||
@@ -180,6 +181,12 @@ export default {
|
||||
this.selectedIcon = undefined;
|
||||
}
|
||||
},
|
||||
selectedIcon: {
|
||||
immediate: true,
|
||||
handler: function ({ propId } = {}) {
|
||||
this.$emit('active-action-change', propId)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
log(e) {
|
||||
@@ -217,15 +224,17 @@ export default {
|
||||
this.menuX = x;
|
||||
this.selectedIcon = icon;
|
||||
this.menuOpen = true;
|
||||
},
|
||||
},
|
||||
clickOutsideMenu () {
|
||||
this.menuOpen = false;
|
||||
},
|
||||
menuClickOutsideInclude() {
|
||||
return compact([
|
||||
const outside = compact([
|
||||
document.querySelector('.selected-creature-bar'),
|
||||
document.querySelector('.tabletop-prop-menu')
|
||||
...document.querySelectorAll('.tabletop-creature-card'),
|
||||
document.querySelector('.tabletop-prop-menu'),
|
||||
]);
|
||||
return outside;
|
||||
},
|
||||
openCharacterSheet(tab, elementId) {
|
||||
this.$store.commit(
|
||||
|
||||
@@ -29,12 +29,19 @@ Meteor.publish('singleCharacter', function (creatureId) {
|
||||
let permissionCreature = Creatures.findOne({
|
||||
_id: creatureId,
|
||||
}, {
|
||||
fields: { owner: 1, readers: 1, writers: 1, public: 1, computeVersion: 1 }
|
||||
fields: {
|
||||
owner: 1,
|
||||
readers: 1,
|
||||
writers: 1,
|
||||
public: 1,
|
||||
computeVersion: 1,
|
||||
tabletopId: 1,
|
||||
}
|
||||
});
|
||||
try { assertViewPermission(permissionCreature, userId) }
|
||||
catch (e) { return [] }
|
||||
loadCreature(creatureId, self);
|
||||
if (permissionCreature.computeVersion !== VERSION && computation.firstRun) {
|
||||
if (permissionCreature?.computeVersion !== VERSION && computation.firstRun) {
|
||||
try {
|
||||
rebuildCreatureNestedSets(creatureId).then(() => {
|
||||
try {
|
||||
@@ -71,6 +78,22 @@ Meteor.publish('singleCharacter', function (creatureId) {
|
||||
username: 1,
|
||||
},
|
||||
}),
|
||||
// Also publish summaries of creatures in the same tabletop
|
||||
Creatures.find({
|
||||
tabletopId: permissionCreature?.tabletopId,
|
||||
}, {
|
||||
fields: {
|
||||
_id: 1,
|
||||
name: 1,
|
||||
picture: 1,
|
||||
avatarPicture: 1,
|
||||
tabletopId: 1,
|
||||
initiativeRoll: 1,
|
||||
settings: 1,
|
||||
propCount: 1,
|
||||
},
|
||||
limit: 110, // Party vs 100 creatures was a fun encounter to run, so let's support that
|
||||
}),
|
||||
];
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user