Began migrating action engine to async
To suspending actions to await user input
This commit is contained in:
@@ -1,27 +0,0 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
|
||||||
|
|
||||||
// Actions are creature actions that have been partially executed and not yet resolved
|
|
||||||
// They require some user input to progress
|
|
||||||
let Actions = new Mongo.Collection('actions');
|
|
||||||
|
|
||||||
let CreaturePropertySchema = new SimpleSchema({
|
|
||||||
_id: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
},
|
|
||||||
// Which creature is taking the action
|
|
||||||
_creatureId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
},
|
|
||||||
// The user who began taking the action
|
|
||||||
user: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
},
|
|
||||||
// The property that is about to be applied
|
|
||||||
property: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
108
app/imports/api/creature/actions/ActiveActions.js
Normal file
108
app/imports/api/creature/actions/ActiveActions.js
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||||
|
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||||
|
|
||||||
|
// Actions are creature actions that have been partially executed and not yet resolved
|
||||||
|
// They require some user input to progress
|
||||||
|
let ActiveActions = new Mongo.Collection('activeActions');
|
||||||
|
|
||||||
|
let ActiveActionSchema = new SimpleSchema({
|
||||||
|
_id: {
|
||||||
|
type: String,
|
||||||
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
|
},
|
||||||
|
// Which creature is taking the action
|
||||||
|
creatureId: {
|
||||||
|
type: String,
|
||||||
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
|
},
|
||||||
|
// The user who began taking the action
|
||||||
|
userId: {
|
||||||
|
type: String,
|
||||||
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
|
},
|
||||||
|
// Requests for user input
|
||||||
|
questions: {
|
||||||
|
type: Object,
|
||||||
|
blackbox: true,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
// User responses
|
||||||
|
answers: {
|
||||||
|
type: Object,
|
||||||
|
blackbox: true,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
ActiveActions.attachSchema(ActiveActionSchema);
|
||||||
|
|
||||||
|
export default ActiveActions;
|
||||||
|
|
||||||
|
export const answerAction = new ValidatedMethod({
|
||||||
|
name: 'activeActions.answer',
|
||||||
|
validate: null /*new SimpleSchema({
|
||||||
|
activeActionId: SimpleSchema.RegEx.Id,
|
||||||
|
answers: {
|
||||||
|
type: Object,
|
||||||
|
blackbox: true,
|
||||||
|
},
|
||||||
|
}).validator()*/,
|
||||||
|
applyOptions: {
|
||||||
|
throwStubExceptions: false,
|
||||||
|
},
|
||||||
|
mixins: [RateLimiterMixin],
|
||||||
|
rateLimit: {
|
||||||
|
numRequests: 5,
|
||||||
|
timeInterval: 5000,
|
||||||
|
},
|
||||||
|
run({ activeActionId, answers }) {
|
||||||
|
return ActiveActions.update({}, {
|
||||||
|
$set: { answers },
|
||||||
|
$unset: { questions: 1 },
|
||||||
|
});
|
||||||
|
const action = ActiveActions.findOne(activeActionId);
|
||||||
|
// Permissions
|
||||||
|
if (action.userId !== this.userId) {
|
||||||
|
throw new Meteor.Error('Permission denied', 'You do not own this action');
|
||||||
|
}
|
||||||
|
return ActiveActions.update(activeActionId, {
|
||||||
|
$set: { answers },
|
||||||
|
$unset: { questions: 1 },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const removeAction = new ValidatedMethod({
|
||||||
|
name: 'activeActions.remove',
|
||||||
|
validate: new SimpleSchema({
|
||||||
|
activeActionId: SimpleSchema.RegEx.Id,
|
||||||
|
}).validator(),
|
||||||
|
mixins: [RateLimiterMixin],
|
||||||
|
rateLimit: {
|
||||||
|
numRequests: 5,
|
||||||
|
timeInterval: 5000,
|
||||||
|
},
|
||||||
|
run({ activeActionId }) {
|
||||||
|
const action = ActiveActions.findOne(activeActionId);
|
||||||
|
// Permissions
|
||||||
|
if (action.userId !== this.userId) {
|
||||||
|
throw new Meteor.Error('Permission denied', 'You do not own this action');
|
||||||
|
}
|
||||||
|
return ActiveActions.remove(activeActionId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO remove this
|
||||||
|
export const removeAllActions = new ValidatedMethod({
|
||||||
|
name: 'activeActions.removeAll',
|
||||||
|
validate: null,
|
||||||
|
mixins: [RateLimiterMixin],
|
||||||
|
rateLimit: {
|
||||||
|
numRequests: 5,
|
||||||
|
timeInterval: 5000,
|
||||||
|
},
|
||||||
|
run() {
|
||||||
|
return ActiveActions.remove({});
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import { getCreature } from '/imports/api/engine/loadCreatures';
|
||||||
|
|
||||||
export default function getRootCreatureAncestor(property){
|
export default function getRootCreatureAncestor(property) {
|
||||||
return Creatures.findOne(property.ancestors[0].id);
|
return getCreature(property.ancestors[0].id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import {
|
|||||||
import { groupBy, remove } from 'lodash';
|
import { groupBy, remove } from 'lodash';
|
||||||
|
|
||||||
export default class ActionContext {
|
export default class ActionContext {
|
||||||
constructor(creatureId, targetIds = [], method) {
|
constructor(creatureId, targetIds = [], method, activeActionId) {
|
||||||
// Get the creature
|
// Get the creature
|
||||||
this.creature = getCreature(creatureId)
|
this.creature = getCreature(creatureId)
|
||||||
|
// Store an active action ID for pausing/resuming this action
|
||||||
|
this.activeActionId = activeActionId
|
||||||
|
|
||||||
if (!this.creature) {
|
if (!this.creature) {
|
||||||
throw new Meteor.Error('No Creature', `No creature could be found with id: ${creatureId}`)
|
throw new Meteor.Error('No Creature', `No creature could be found with id: ${creatureId}`)
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ const applyPropertyByType = {
|
|||||||
toggle,
|
toggle,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function applyProperty(node, actionContext, ...rest) {
|
export default async function applyProperty(node, actionContext, ...rest) {
|
||||||
if (node.node.deactivatedByToggle) return;
|
if (node.node.deactivatedByToggle) return;
|
||||||
actionContext.scope[`#${node.node.type}`] = node.node;
|
actionContext.scope[`#${node.node.type}`] = node.node;
|
||||||
applyPropertyByType[node.node.type]?.(node, actionContext, ...rest);
|
console.log('start apply props by type', node.node.type)
|
||||||
|
await applyPropertyByType[node.node.type]?.(node, actionContext, ...rest);
|
||||||
|
console.log('end apply prop by type', node.node.type)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import numberToSignedString from '/imports/api/utility/numberToSignedString.js';
|
|||||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||||
import { resetProperties } from '/imports/api/creature/creatures/methods/restCreature.js';
|
import { resetProperties } from '/imports/api/creature/creatures/methods/restCreature.js';
|
||||||
|
|
||||||
export default function applyAction(node, actionContext) {
|
export default async function applyAction(node, actionContext) {
|
||||||
applyNodeTriggers(node, 'before', actionContext);
|
await applyNodeTriggers(node, 'before', actionContext);
|
||||||
const prop = node.node;
|
const prop = node.node;
|
||||||
if (prop.target === 'self') actionContext.targets = [actionContext.creature];
|
if (prop.target === 'self') actionContext.targets = [actionContext.creature];
|
||||||
const targets = actionContext.targets;
|
const targets = actionContext.targets;
|
||||||
@@ -24,7 +24,7 @@ export default function applyAction(node, actionContext) {
|
|||||||
if (!prop.silent) actionContext.addLog(content);
|
if (!prop.silent) actionContext.addLog(content);
|
||||||
|
|
||||||
// Spend the resources
|
// Spend the resources
|
||||||
const failed = spendResources(prop, actionContext);
|
const failed = await spendResources(prop, actionContext);
|
||||||
if (failed) return;
|
if (failed) return;
|
||||||
|
|
||||||
const attack = prop.attackRoll || prop.attackRollBonus;
|
const attack = prop.attackRoll || prop.attackRollBonus;
|
||||||
@@ -32,18 +32,18 @@ export default function applyAction(node, actionContext) {
|
|||||||
// Attack if there is an attack roll
|
// Attack if there is an attack roll
|
||||||
if (attack && attack.calculation) {
|
if (attack && attack.calculation) {
|
||||||
if (targets.length) {
|
if (targets.length) {
|
||||||
targets.forEach(target => {
|
for (const target of targets) {
|
||||||
applyAttackToTarget({ attack, target, actionContext });
|
await applyAttackToTarget({ attack, target, actionContext });
|
||||||
// Apply the children, but only to the current target
|
// Apply the children, but only to the current target
|
||||||
actionContext.targets = [target];
|
actionContext.targets = [target];
|
||||||
applyChildren(node, actionContext);
|
await applyChildren(node, actionContext);
|
||||||
});
|
}
|
||||||
} else {
|
} else {
|
||||||
applyAttackWithoutTarget({ attack, actionContext });
|
await applyAttackWithoutTarget({ attack, actionContext });
|
||||||
applyChildren(node, actionContext);
|
await applyChildren(node, actionContext);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
applyChildren(node, actionContext);
|
await applyChildren(node, actionContext);
|
||||||
}
|
}
|
||||||
if (prop.actionType === 'event' && prop.variableName) {
|
if (prop.actionType === 'event' && prop.variableName) {
|
||||||
resetProperties(actionContext.creature._id, prop.variableName, actionContext);
|
resetProperties(actionContext.creature._id, prop.variableName, actionContext);
|
||||||
@@ -189,7 +189,7 @@ function applyCrits(value, scope) {
|
|||||||
return { criticalHit, criticalMiss };
|
return { criticalHit, criticalMiss };
|
||||||
}
|
}
|
||||||
|
|
||||||
function spendResources(prop, actionContext) {
|
async function spendResources(prop, actionContext) {
|
||||||
// Check Uses
|
// Check Uses
|
||||||
if (prop.usesLeft <= 0) {
|
if (prop.usesLeft <= 0) {
|
||||||
if (!prop.silent) actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
@@ -297,9 +297,9 @@ function spendResources(prop, actionContext) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Apply the ammo children
|
// Apply the ammo children
|
||||||
ammoToApply.forEach(node => {
|
for (const node of ammoToApply) {
|
||||||
applyProperty(node, actionContext);
|
await applyProperty(node, actionContext);
|
||||||
});
|
}
|
||||||
|
|
||||||
// Log all the spending
|
// Log all the spending
|
||||||
if (gainLog.length && !prop.silent) actionContext.addLog({
|
if (gainLog.length && !prop.silent) actionContext.addLog({
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import recalculateCalculation from './shared/recalculateCalculation.js';
|
|||||||
import rollDice from '/imports/parser/rollDice.js';
|
import rollDice from '/imports/parser/rollDice.js';
|
||||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||||
|
import getUserInput from '/imports/api/engine/actions/getUserInput';
|
||||||
|
|
||||||
export default function applyBranch(node, actionContext) {
|
export default async function applyBranch(node, actionContext) {
|
||||||
applyNodeTriggers(node, 'before', actionContext);
|
applyNodeTriggers(node, 'before', actionContext);
|
||||||
const scope = actionContext.scope;
|
const scope = actionContext.scope;
|
||||||
const targets = actionContext.targets;
|
const targets = actionContext.targets;
|
||||||
@@ -76,5 +77,20 @@ export default function applyBranch(node, actionContext) {
|
|||||||
applyChildren(node, actionContext);
|
applyChildren(node, actionContext);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'choice': {
|
||||||
|
console.log('paused waiting for user input');
|
||||||
|
let { index } = await getUserInput({
|
||||||
|
index: 'number',
|
||||||
|
}, actionContext);
|
||||||
|
console.log('resuming with input ' + index);
|
||||||
|
if (!isFinite(index) || index < 0) index = 0;
|
||||||
|
if (index > node.children.length - 1) index = node.children.length - 1;
|
||||||
|
applyNodeTriggers(node, 'after', actionContext);
|
||||||
|
console.log('applying child ', index);
|
||||||
|
console.log(node.children[index]);
|
||||||
|
applyProperty(node.children[index], actionContext);
|
||||||
|
applyNodeTriggers(node, 'afterChildren', actionContext);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import recalculateInlineCalculations from './shared/recalculateInlineCalculation
|
|||||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||||
|
|
||||||
export default function applyNote(node, actionContext) {
|
export default async function applyNote(node, actionContext) {
|
||||||
applyNodeTriggers(node, 'before', actionContext);
|
await applyNodeTriggers(node, 'before', actionContext);
|
||||||
const prop = node.node;
|
const prop = node.node;
|
||||||
|
|
||||||
// Log Name, summary
|
// Log Name, summary
|
||||||
@@ -21,5 +21,5 @@ export default function applyNote(node, actionContext) {
|
|||||||
actionContext.addLog({ value: prop.description.value });
|
actionContext.addLog({ value: prop.description.value });
|
||||||
}
|
}
|
||||||
// Apply children
|
// Apply children
|
||||||
applyChildren(node, actionContext);
|
await applyChildren(node, actionContext);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||||
import applyProperty from '/imports/api/engine/actions/applyProperty.js';
|
import applyProperty from '/imports/api/engine/actions/applyProperty.js';
|
||||||
|
|
||||||
export default function applyChildren(node, actionContext) {
|
export default async function applyChildren(node, actionContext) {
|
||||||
applyNodeTriggers(node, 'after', actionContext);
|
applyNodeTriggers(node, 'after', actionContext);
|
||||||
node.children.forEach(child => applyProperty(child, actionContext));
|
for (const child of node.children) {
|
||||||
|
await applyProperty(child, actionContext);
|
||||||
|
}
|
||||||
applyNodeTriggers(node, 'afterChildren', actionContext);
|
applyNodeTriggers(node, 'afterChildren', actionContext);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,25 +6,25 @@ import applyProperty from '/imports/api/engine/actions/applyProperty.js';
|
|||||||
import { difference, intersection } from 'lodash';
|
import { difference, intersection } from 'lodash';
|
||||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
|
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
|
||||||
|
|
||||||
export function applyNodeTriggers(node, timing, actionContext) {
|
export async function applyNodeTriggers(node, timing, actionContext) {
|
||||||
const prop = node.node;
|
const prop = node.node;
|
||||||
const type = prop.type;
|
const type = prop.type;
|
||||||
const triggers = actionContext.triggers?.doActionProperty?.[type]?.[timing];
|
const triggers = actionContext.triggers?.doActionProperty?.[type]?.[timing];
|
||||||
if (triggers) {
|
if (triggers) {
|
||||||
triggers.forEach(trigger => {
|
for (const trigger of triggers) {
|
||||||
applyTrigger(trigger, prop, actionContext);
|
await applyTrigger(trigger, prop, actionContext);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyTriggers(triggers = [], prop, actionContext) {
|
export async function applyTriggers(triggers = [], prop, actionContext) {
|
||||||
// Apply the triggers
|
// Apply the triggers
|
||||||
triggers.forEach(trigger => {
|
for (const trigger of triggers) {
|
||||||
applyTrigger(trigger, prop, actionContext)
|
await applyTrigger(trigger, prop, actionContext)
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyTrigger(trigger, prop, actionContext) {
|
export async function applyTrigger(trigger, prop, actionContext) {
|
||||||
// If there is a prop we are applying the trigger from,
|
// If there is a prop we are applying the trigger from,
|
||||||
// don't fire if the tags don't match
|
// don't fire if the tags don't match
|
||||||
if (prop && !triggerMatchTags(trigger, prop)) {
|
if (prop && !triggerMatchTags(trigger, prop)) {
|
||||||
@@ -71,9 +71,9 @@ export function applyTrigger(trigger, prop, actionContext) {
|
|||||||
const properties = getPropertyDecendants(actionContext.creature._id, trigger._id);
|
const properties = getPropertyDecendants(actionContext.creature._id, trigger._id);
|
||||||
properties.sort((a, b) => a.order - b.order);
|
properties.sort((a, b) => a.order - b.order);
|
||||||
const propertyForest = nodeArrayToTree(properties);
|
const propertyForest = nodeArrayToTree(properties);
|
||||||
propertyForest.forEach(node => {
|
for (const node of propertyForest) {
|
||||||
applyProperty(node, actionContext);
|
await applyProperty(node, actionContext);
|
||||||
});
|
}
|
||||||
|
|
||||||
trigger.firing = false;
|
trigger.firing = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
|||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import applyProperty from './applyProperty.js';
|
import applyProperty from './applyProperty.js';
|
||||||
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
||||||
|
import ActiveActions from '/imports/api/creature/actions/ActiveActions';
|
||||||
|
|
||||||
const doAction = new ValidatedMethod({
|
const doAction = new ValidatedMethod({
|
||||||
name: 'creatureProperties.doAction',
|
name: 'creatureProperties.doAction',
|
||||||
@@ -36,11 +37,18 @@ const doAction = new ValidatedMethod({
|
|||||||
numRequests: 10,
|
numRequests: 10,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({ actionId, targetIds = [], scope }) {
|
async run({ actionId, targetIds = [], scope }) {
|
||||||
// Get action context
|
// Get action context
|
||||||
let action = CreatureProperties.findOne(actionId);
|
let action = CreatureProperties.findOne(actionId);
|
||||||
const creatureId = action.ancestors[0].id;
|
const creatureId = action.ancestors[0].id;
|
||||||
const actionContext = new ActionContext(creatureId, targetIds, this);
|
// TODO remove this
|
||||||
|
// For testing, remove all other active actions before inserting this one
|
||||||
|
ActiveActions.remove({});
|
||||||
|
const activeActionId = await ActiveActions.insertAsync({
|
||||||
|
creatureId,
|
||||||
|
userId: this.userId,
|
||||||
|
});
|
||||||
|
const actionContext = new ActionContext(creatureId, targetIds, this, activeActionId);
|
||||||
|
|
||||||
// Check permissions
|
// Check permissions
|
||||||
assertEditPermission(actionContext.creature, this.userId);
|
assertEditPermission(actionContext.creature, this.userId);
|
||||||
@@ -56,7 +64,7 @@ const doAction = new ValidatedMethod({
|
|||||||
properties.sort((a, b) => a.order - b.order);
|
properties.sort((a, b) => a.order - b.order);
|
||||||
|
|
||||||
// Do the action
|
// Do the action
|
||||||
doActionWork({ properties, ancestors, actionContext, methodScope: scope });
|
await doActionWork({ properties, ancestors, actionContext, methodScope: scope });
|
||||||
|
|
||||||
// Recompute all involved creatures
|
// Recompute all involved creatures
|
||||||
Creatures.update({
|
Creatures.update({
|
||||||
@@ -69,7 +77,7 @@ const doAction = new ValidatedMethod({
|
|||||||
|
|
||||||
export default doAction;
|
export default doAction;
|
||||||
|
|
||||||
export function doActionWork({
|
export async function doActionWork({
|
||||||
properties, ancestors, actionContext, methodScope = {},
|
properties, ancestors, actionContext, methodScope = {},
|
||||||
}) {
|
}) {
|
||||||
// get the docs
|
// get the docs
|
||||||
@@ -84,7 +92,9 @@ export function doActionWork({
|
|||||||
|
|
||||||
// Apply the top level property, it is responsible for applying its children
|
// Apply the top level property, it is responsible for applying its children
|
||||||
// recursively
|
// recursively
|
||||||
applyProperty(propertyForest[0], actionContext);
|
console.log('start apply properties')
|
||||||
|
await applyProperty(propertyForest[0], actionContext);
|
||||||
|
console.log('end apply properties')
|
||||||
|
|
||||||
// Insert the log
|
// Insert the log
|
||||||
actionContext.writeLog();
|
actionContext.writeLog();
|
||||||
|
|||||||
28
app/imports/api/engine/actions/getUserInput.js
Normal file
28
app/imports/api/engine/actions/getUserInput.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import ActiveActions from '/imports/api/creature/actions/ActiveActions';
|
||||||
|
|
||||||
|
export default async function getUserInput(questions, actionContext) {
|
||||||
|
const activeActionId = actionContext.activeActionId;
|
||||||
|
// Set the questions on the active action
|
||||||
|
ActiveActions.update(activeActionId, {
|
||||||
|
$set: { questions },
|
||||||
|
$unset: { answers: 1 },
|
||||||
|
});
|
||||||
|
// Wait for answers
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const observerHandle = ActiveActions.find({
|
||||||
|
_id: activeActionId
|
||||||
|
}).observeChanges({
|
||||||
|
changed(id, fields) {
|
||||||
|
// Only watch for answers
|
||||||
|
if (!fields.answers) return;
|
||||||
|
// Stop watching
|
||||||
|
observerHandle.stop();
|
||||||
|
// Give answers
|
||||||
|
resolve(fields.answers);
|
||||||
|
},
|
||||||
|
removed() {
|
||||||
|
reject('Active action was deleted')
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ let BranchSchema = createPropertySchema({
|
|||||||
'index',
|
'index',
|
||||||
// if it has option children, asks to select one
|
// if it has option children, asks to select one
|
||||||
// Otherwise presents its own text with yes/no
|
// Otherwise presents its own text with yes/no
|
||||||
//'choice',
|
'choice',
|
||||||
//'option',
|
//'option',
|
||||||
],
|
],
|
||||||
defaultValue: 'if',
|
defaultValue: 'if',
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ export default {
|
|||||||
{ value: 'eachTarget', text: 'Apply to each target' },
|
{ value: 'eachTarget', text: 'Apply to each target' },
|
||||||
{ value: 'random', text: 'Random' },
|
{ value: 'random', text: 'Random' },
|
||||||
{ value: 'index', text: 'Calculated index' },
|
{ value: 'index', text: 'Calculated index' },
|
||||||
|
{ value: 'choice', text: 'User choice' },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -79,6 +80,7 @@ export default {
|
|||||||
case 'eachTarget': return 'Applies each child property once per target';
|
case 'eachTarget': return 'Applies each child property once per target';
|
||||||
case 'random': return 'Chooses one child property at random and applies it';
|
case 'random': return 'Chooses one child property at random and applies it';
|
||||||
case 'index': return 'Chooses one child property to apply based on the given index';
|
case 'index': return 'Chooses one child property to apply based on the given index';
|
||||||
|
case 'choice': return 'Pause the action and let the user choose which child to apply';
|
||||||
default: return '';
|
default: return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export default {
|
|||||||
case 'eachTarget': return 'Each target';
|
case 'eachTarget': return 'Each target';
|
||||||
case 'random': return 'Pick one at random';
|
case 'random': return 'Pick one at random';
|
||||||
case 'index': return 'Pick one by index';
|
case 'index': return 'Pick one by index';
|
||||||
|
case 'choice': return 'User choice';
|
||||||
default: return '';
|
default: return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export default {
|
|||||||
case 'eachTarget': return 'Each target';
|
case 'eachTarget': return 'Each target';
|
||||||
case 'random': return 'Pick one at random';
|
case 'random': return 'Pick one at random';
|
||||||
case 'index': return 'Pick one by index';
|
case 'index': return 'Pick one by index';
|
||||||
|
case 'choice': return 'User choice';
|
||||||
default: return '';
|
default: return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
|
|||||||
import { assertViewPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
import { assertViewPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||||
import computeCreature from '/imports/api/engine/computeCreature.js';
|
import computeCreature from '/imports/api/engine/computeCreature.js';
|
||||||
import VERSION from '/imports/constants/VERSION.js';
|
import VERSION from '/imports/constants/VERSION.js';
|
||||||
|
import ActiveActions from '/imports/api/creature/actions/ActiveActions';
|
||||||
import { loadCreature } from '/imports/api/engine/loadCreatures.js';
|
import { loadCreature } from '/imports/api/engine/loadCreatures.js';
|
||||||
|
|
||||||
let schema = new SimpleSchema({
|
let schema = new SimpleSchema({
|
||||||
@@ -54,6 +55,11 @@ Meteor.publish('singleCharacter', function (creatureId) {
|
|||||||
limit: 20,
|
limit: 20,
|
||||||
sort: { date: -1 },
|
sort: { date: -1 },
|
||||||
}),
|
}),
|
||||||
|
ActiveActions.find({
|
||||||
|
creatureId,
|
||||||
|
}, {
|
||||||
|
limit: 10,
|
||||||
|
}),
|
||||||
// Also publish the owner's username
|
// Also publish the owner's username
|
||||||
Meteor.users.find(permissionCreature.owner, {
|
Meteor.users.find(permissionCreature.owner, {
|
||||||
fields: {
|
fields: {
|
||||||
|
|||||||
Reference in New Issue
Block a user