private async Task <bool> ExecuteActionsAsync(DialogContext dc, IEnumerable <CommandAction> actions) { var stateFlags = await _stateFlagsStateAccessor.GetAsync(dc.Context); var inventoryItems = await _inventoryItemsAccessor.GetAsync(dc.Context); // Get the room state. It always exists at this point because it's // set up in the BeginDialogAsync method. var roomStates = await _roomStateAccessor.GetAsync(dc.Context, () => new Dictionary <string, RoomState>()); var roomState = roomStates[_roomId]; var activities = new List <IActivity>(); var actionStack = new Stack <CommandAction>(actions.Reverse()); // Process each action in turn, populating the activities list. while (actionStack.TryPop(out CommandAction nextAction)) { switch (nextAction) { case AddToInventoryAction action: { if (!inventoryItems.Contains(action.InventoryItemId)) { inventoryItems.Add(action.InventoryItemId); activities.Add(_activityFactory.InventoryItemAdded(dc, action.InventoryItemId)); } break; } case ClearFlagAction action: { if (stateFlags.Contains(action.Flag)) { stateFlags.Remove(action.Flag); } break; } case GuiDelayAction action: { activities.Add(_activityFactory.Delayed(dc, action.Milliseconds)); break; } case GuiChangeActorDirectionAction action: { activities.Add(_activityFactory.ActorDirectionChanged(dc, action.ActorId, action.Direction)); break; } case GuiMoveActorAction action: { // Save the actor's new position in the room state. roomState.ActorPositions[action.ActorId] = action.Position; activities.Add(_activityFactory.ActorMoved(dc, action.ActorId, action.Position)); break; } case GuiNarratorAction action: { activities.Add(_activityFactory.Narrated(dc, action.Text)); break; } case GuiPlaceActorAction action: { // Save the actor's new position in the room state. roomState.ActorPositions[action.ActorId] = action.Position; activities.Add(_activityFactory.ActorPlacedInRoom(dc, action.ActorId, action.Position)); break; } case GuiPlaceObjectAction action: { // Save the object's new position in the room state. roomState.ObjectPositions[action.ObjectId] = action.Position; activities.Add(_activityFactory.ObjectPlacedInRoom(dc, action.ObjectId, action.Position)); break; } case GuiRemoveObjectAction action: { // Remove the object from the room state. roomState.ObjectPositions.Remove(action.ObjectId); activities.Add(_activityFactory.ObjectRemovedFromRoom(dc, action.ObjectId)); break; } case RemoveFromInventoryAction action: { if (inventoryItems.Contains(action.InventoryItemId)) { inventoryItems.Remove(action.InventoryItemId); } activities.Add(_activityFactory.InventoryItemRemoved(dc, action.InventoryItemId)); break; } case SetFlagAction action: { if (!stateFlags.Contains(action.Flag)) { stateFlags.Add(action.Flag); } break; } case SpeakAction action: { activities.Add(_activityFactory.Speak(dc, action.ActorId, action.Text)); break; } case StartConversationAction action: { // If the result of the action indicates that we need to start a new dialog // (e.g. conversation with an actor, or a switch to a new room), first send all // activities collected up to this point to the client. if (activities.Any()) { await dc.Context.SendActivitiesAsync(activities.ToArray()); } // If the player starts a conversation, save any remaining actions to the state. // These will be executed when the conversation is done and this dialog // continues. if (actionStack.Any()) { // Conver to List because Stacks don't serialize in the correct order. // See https://github.com/JamesNK/Newtonsoft.Json/issues/971. dc.ActiveDialog.State.Add(DialogStatePendingActions, actionStack.ToList()); } // Start the conversation. await dc.BeginDialogAsync(action.ConversationId); // Stop processing any further actions now that we've switched to a new dialog. return(false); } case SwitchRoomAction action: { // If the result of the action indicates that we need to start a new dialog // (e.g. conversation with an actor, or a switch to a new room), first send all // activities collected up to this point to the client. if (activities.Any()) { await dc.Context.SendActivitiesAsync(activities.ToArray()); } // Switch to the new room. await dc.ReplaceDialogAsync(action.RoomId); // Stop processing any further actions now that we've switched to a new dialog. return(false); } case TextDescribeAction action: { activities.Add(MessageFactory.Text(action.Text)); break; } } } await dc.Context.SendActivitiesAsync(activities.ToArray()); // Returning true here indicates that we can continue processing the actions for this turn. return(true); }
private async Task <DialogTurnResult> RunStepAsync(DialogContext dc, string option = null) { // Find the current conversation tree node using the saved Step state. var node = _rootNode; if (dc.ActiveDialog.State.ContainsKey(DialogStateCurrentNodeId)) { node = _rootNode.Find(Convert.ToInt32(dc.ActiveDialog.State[DialogStateCurrentNodeId])); } // Find the node that contains the actions for the reply. var nextNode = (option != null && node.ChildNodes.ContainsKey(option)) ? node.ChildNodes[option] : node; // Process the actions, creating a list of activities to send back to the player. var activities = new List <IActivity>(); var activityFactory = new ActivityFactory(_gameInfo); var stateFlags = await _stateFlagsStateAccessor.GetAsync(dc.Context); // foreach (var action in nextNode.Actions) { switch (action) { case GoToConversationTopicAction goToConversationTopicAction: if (string.Equals(goToConversationTopicAction.Topic, "root", StringComparison.OrdinalIgnoreCase)) { nextNode = _rootNode; } else if (node.ParentId.HasValue) { nextNode = _rootNode.Find(node.ParentId.Value); } break; case EndConversationAction endConversationAction: nextNode = null; break; case SpeakAction speakAction: activities.Add(activityFactory.Speak(dc, speakAction.ActorId, speakAction.Text)); break; case SetFlagAction setFlagAction: if (!stateFlags.Contains(setFlagAction.Flag)) { stateFlags.Add(setFlagAction.Flag); } break; } } // Check if the conversation tree should continue; if (nextNode != null) { // If there are no child nodes in the next node, there's nothing for the player to do. // Revert to the current node for the next turn. if (!nextNode.ChildNodes.Any()) { nextNode = node; } // List the conversation tree options for the player. var options = nextNode.ChildNodes.Select(s => s.Key).ToArray(); // Add the conversation tree options to the last outbound messages activity. var lastMessageIndex = activities.FindLastIndex(a => a.Type == ActivityTypes.Message); var lastMessageActivity = (Activity)activities[lastMessageIndex].AsMessageActivity(); var updatedActivity = (Activity)MessageFactory.SuggestedActions(options, lastMessageActivity.Text); updatedActivity.Properties = lastMessageActivity.Properties; activities[lastMessageIndex] = updatedActivity; } // Send all activities to the client. await dc.Context.SendActivitiesAsync(activities.ToArray()); // Update the state so the next turn will start at the correct location in the dialog tree. if (nextNode != null) { dc.ActiveDialog.State[DialogStateCurrentNodeId] = nextNode.Id; return(new DialogTurnResult(DialogTurnStatus.Waiting)); } // Or end the dialog tree if there's no next node. await dc.EndDialogAsync(); return(new DialogTurnResult(DialogTurnStatus.Complete)); }