Esempio n. 1
0
        public void Tick()
        {
            QuestObjectState state;
            bool             result;

            var values = m_ActivelyChecked.Values;

            IQuestSystemObject[] objects = new IQuestSystemObject[values.Count];
            values.CopyTo(objects, 0);

            GameDebugger.Log(LogLevel.Debug, "QS: TICK");
            foreach (IQuestSystemObject obj in objects)
            {
                state = GetState(obj);
                if (state.Locked)
                {
                    GameDebugger.Log(LogLevel.Warning, "QS: {0} '0x{1:X16}' is in the list of actively checked objects while being locked", obj.GetType().Name, obj.Id);
                    m_ActivelyChecked.Remove(obj.Id);
                    continue;
                }
                GameDebugger.Log(LogLevel.Debug, "QS: Checking {0} '0x{1:X16}'", obj.GetType().Name, obj.Id);
                if (obj is QuestPin)
                {
                    QuestPin pin = (QuestPin)obj;
                    result = CheckCondition(pin, true);
                    if (result && state.Active == QuestObjectState.ActivationState.PartiallyActive)
                    {
                        state.Active = QuestObjectState.ActivationState.Active;
                        GameDebugger.Log(LogLevel.Debug, "QS: Switched {0} '0x{1:X16}' from partially activated to activated", obj.GetType().Name, obj.Id);
                        if (pin.Node is Quest)
                        {
                            Activate((BaseQuestObject)pin.Node);
                        }
                        else
                        {
                            CheckInputsOnActivation((BaseQuestObject)pin.Node);
                        }
                        foreach (QuestConnection connection in pin.Connections)
                        {
                            if (connection.Source == pin)
                            {
                                Activate(connection);
                            }
                        }
                    }
                    else if (!result && state.Active == QuestObjectState.ActivationState.Active)
                    {
                        state.Active = QuestObjectState.ActivationState.PartiallyActive;
                        GameDebugger.Log(LogLevel.Debug, "QS: Switched {0} '0x{1:X16}' from activated to partially activated", obj.GetType().Name, obj.Id);
                        Deactivate((BaseQuestObject)pin.Node);
                        foreach (QuestConnection connection in pin.Connections)
                        {
                            if (connection.Source == pin)
                            {
                                Deactivate(connection);
                            }
                        }
                    }
                }
                else if (obj is QuestCondition)
                {
                    // every tick depending on it's script execution result it switches active either output[0] or output[1] active
                    QuestCondition condition = (QuestCondition)obj;
                    result = CheckCondition(condition, true);
                    if (result)
                    {
                        if (state.CompletionOutput != 0 || state.Completion == QuestObjectState.CompletionState.Incomplete)
                        {
                            if (state.Completion != QuestObjectState.CompletionState.Incomplete)
                            {
                                Deactivate((QuestPin)condition.Outputs[1]);
                                GameDebugger.Log(LogLevel.Debug, "QS: Switching {0} '0x{1:X16}' output from FALSE to TRUE", obj.GetType().Name, obj.Id);
                            }
                            else
                            {
                                GameDebugger.Log(LogLevel.Debug, "QS: Setting {0} '0x{1:X16}' output to TRUE", obj.GetType().Name, obj.Id);
                            }
                            Activate((QuestPin)condition.Outputs[0]);
                            state.CompletionOutput = 0;
                        }
                    }
                    else
                    {
                        if (state.CompletionOutput != 1 || state.Completion == QuestObjectState.CompletionState.Incomplete)
                        {
                            if (state.Completion != QuestObjectState.CompletionState.Incomplete)
                            {
                                Deactivate((QuestPin)condition.Outputs[0]);
                                GameDebugger.Log(LogLevel.Debug, "QS: Switching {0} '0x{1:X16}' output from TRUE to FALSE", obj.GetType().Name, obj.Id);
                            }
                            else
                            {
                                GameDebugger.Log(LogLevel.Debug, "QS: Setting {0} '0x{1:X16}' output to FALSE", obj.GetType().Name, obj.Id);
                            }
                            Activate((QuestPin)condition.Outputs[1]);
                            state.CompletionOutput = 1;
                        }
                    }
                    state.Completion = QuestObjectState.CompletionState.Successful;
                }
                else if (obj is QuestObjective)
                {
                    // every tick rechecks all conditions on output pins
                    QuestObjective objective = (QuestObjective)obj;
                    int            pinIndex  = 0;
                    foreach (QuestPin pin in objective.Outputs)
                    {
                        if (pin.Script == null)
                        {
                            pinIndex++;
                            continue;
                        }
                        result = CheckCondition(pin, false);
                        if (result)
                        {
                            Activate(pin);                     // this will also deactivate previously active pin
                            break;                             // Just in case when there is more than one output that passes the check. We don't want lots of unneeded events, right?
                        }
                        if (pinIndex == state.CompletionOutput && state.Completion != QuestObjectState.CompletionState.Incomplete)
                        {
                            state.Completion = QuestObjectState.CompletionState.Incomplete;
                            GameDebugger.Log(LogLevel.Debug, "QS: event: OnQuestObjectiveIncomplete");
                            if (OnQuestObjectiveIncomplete != null)
                            {
                                OnQuestObjectiveIncomplete.Invoke(new QuestObjectiveIncompleteEventArgs(objective));
                            }
                        }
                        Deactivate(pin);
                        pinIndex++;
                    }
                }
                else
                {
                    GameDebugger.Log(LogLevel.Warning, "QS: {0} '0x{1:X16}' is not supposed to be in the list of actively checked objects", obj.GetType().Name, obj.Id);
                }
            }
        }
Esempio n. 2
0
        public static bool ImportArticyQuests(Project project, string questContainerExternalId)
        {
            ArticyFlowObject questContainer;

            try {
                questContainer = project.GetFlowObject(questContainerExternalId);
            }
            catch {
                throw new UserFriendlyException(
                          String.Format("Could not find quest container '{0}' in the project.", questContainerExternalId),
                          "One of data files does not contain required information."
                          );
            }

            IList <GraphNode> quests = questContainer.Children;

            GameDebugger.Log(LogLevel.Debug, "{0} quests found:", quests.Count);

            ArticyFlowFragment articyQuest;
            Quest quest;
            Dictionary <ulong, QuestPin> pinIndex = new Dictionary <ulong, QuestPin>();
            List <Jump> articyJumps = new List <Jump>();
            bool        badScripts  = false;

            // copy quests
            foreach (GraphNode node in questContainer.Children)
            {
                // hubs are used to join quests for further story progression: hub remains inactive until all quests connected to the hub are complete
                if (node is ArticyHub)
                {
                    ArticyHub articyHub = (ArticyHub)node;
                    QuestHub  hub       = new QuestHub(articyHub.Id, articyHub.ExternalId);
                    badScripts |= ImportArticyPins(articyHub, hub, true, true, pinIndex);
                    GameDebugger.Log(LogLevel.Debug, "Adding quest hub '0x{0:X16}'", hub.Id);
                    Add(hub);
                }

                // flow fragments are quests
                if (node is ArticyFlowFragment)
                {
                    articyQuest = (ArticyFlowFragment)node;
                    quest       = new Quest(articyQuest.Id, articyQuest.ExternalId);

                    GameDebugger.Log(LogLevel.Debug, "Importing quest '{0}'", quest.Key);
                    GameDebugger.Log(LogLevel.Debug, "Quest has {0} inputs and {1} outputs", articyQuest.Inputs.Count, articyQuest.Outputs.Count);

                    badScripts |= ImportArticyPins(articyQuest, quest, true, true, pinIndex);

                    quest.Name        = articyQuest.DisplayName;
                    quest.Description = articyQuest.Text;

                    Add(quest);

                    // copy quest fragments
                    foreach (GraphNode childNode in node.Children)
                    {
                        if (childNode is ArticyDialogue || childNode is ArticyDialogueFragment)
                        {
                            throw new UserFriendlyException(
                                      String.Format("Dialogue or dialogue fragment '0x{0:X16}' is not supposed to be a part of a quest", ((ArticyFlowFragment)childNode).Id),
                                      "One of data files is either corupt or has a version that is not supported by the game engine."
                                      );
                        }

                        if (childNode is ArticyFlowFragment)
                        {
                            ArticyFlowFragment articyFragment = (ArticyFlowFragment)childNode;
                            if (string.IsNullOrWhiteSpace(articyFragment.DisplayName))
                            {
                                QuestLogicGate gate = new QuestLogicGate(articyFragment.Id, articyFragment.ExternalId);
                                badScripts |= ImportArticyPins(articyFragment, gate, true, true, pinIndex);
                                GameDebugger.Log(LogLevel.Debug, "Adding logic gate '0x{0:X16}' to the quest", gate.Id);
                                quest.AddChild(gate);
                                Add(gate);
                            }
                            else
                            {
                                QuestObjective objective = new QuestObjective(articyFragment.Id, articyFragment.ExternalId);
                                objective.Description = articyFragment.Text;
                                badScripts           |= ImportArticyPins(articyFragment, objective, true, true, pinIndex);
                                GameDebugger.Log(LogLevel.Debug, "Adding objective '0x{0:X16}' to the quest", objective.Id);
                                objective.Name        = articyFragment.DisplayName;
                                objective.Description = articyFragment.Text;
                                quest.AddChild(objective);
                                Add(objective);
                            }
                        }

                        if (childNode is ArticyCondition)
                        {
                            ArticyCondition articyCondition = (ArticyCondition)childNode;
                            QuestCondition  condition       = new QuestCondition(articyCondition.Id, articyCondition.ExternalId);
                            if (string.IsNullOrWhiteSpace(articyCondition.Script))
                            {
                                badScripts = true;
                                GameDebugger.Log(LogLevel.Error, "Condition '0x{0:X16}' has no script", condition.Id);
                            }
                            else
                            {
                                if ((condition.Script = CompileScript(articyCondition.Script, Quests.ConditionScriptEnvironment, "condition", condition.Id)) == null)
                                {
                                    badScripts = true;
                                }
                            }
                            badScripts |= ImportArticyPins(articyCondition, condition, true, true, pinIndex);
                            GameDebugger.Log(LogLevel.Debug, "Adding condition '0x{0:X16}' to the quest", condition.Id);
                            quest.AddChild(condition);
                            Add(condition);
                        }

                        if (childNode is ArticyInstruction)
                        {
                            ArticyInstruction articyInstruction = (ArticyInstruction)childNode;
                            QuestInstruction  instruction       = new QuestInstruction(articyInstruction.Id, articyInstruction.ExternalId);
                            if (string.IsNullOrWhiteSpace(articyInstruction.Script))
                            {
                                GameDebugger.Log(LogLevel.Notice, "Instruction '0x{0:X16}' has no script", instruction.Id);
                            }
                            else
                            {
                                if ((instruction.Script = CompileScript(articyInstruction.Script, Quests.ConditionScriptEnvironment, "instruction", instruction.Id)) == null)
                                {
                                    badScripts = true;
                                }
                            }
                            badScripts |= ImportArticyPins(articyInstruction, instruction, true, true, pinIndex);
                            GameDebugger.Log(LogLevel.Debug, "Adding instruction '0x{0:X16}' to the quest", instruction.Id);
                            quest.AddChild(instruction);
                            Add(instruction);
                        }

                        if (childNode is ArticyHub)
                        {
                            ArticyHub      articyHub = (ArticyHub)childNode;
                            QuestSavePoint savePoint = new QuestSavePoint(articyHub.Id, articyHub.ExternalId);
                            badScripts |= ImportArticyPins(articyHub, savePoint, true, true, pinIndex);
                            GameDebugger.Log(LogLevel.Debug, "Adding save point '0x{0:X16}' to the quest", savePoint.Id);
                            quest.AddChild(savePoint);
                            Add(savePoint);
                        }

                        if (childNode is Jump)
                        {
                            articyJumps.Add((Jump)childNode);
                        }
                    }
                }
            }

            // jumps need postprocessing, because we must make sure all quest system nodes are already added
            // jumps must be treated as connections. they must be disallowed to connect to anything outside the scope of current quest.
            BaseQuestObject  obj;
            QuestPin         srcPin, dstPin;
            IList <GraphPin> inputs;

            foreach (Jump articyJump in articyJumps)
            {
                GameDebugger.Log(LogLevel.Debug, "Linking jump '0x{0:X16}'", articyJump.Id);

                if (articyJump.TargetPin != null)
                {
                    if (!pinIndex.TryGetValue(articyJump.TargetPin.Id, out dstPin))
                    {
                        GameDebugger.Log(LogLevel.Debug, "Ignoring jump '0x{0:X16}' since it's target pin is outside the quest container", articyJump.Id);
                        continue;
                    }
                }
                else if (articyJump.Target != null)
                {
                    inputs = articyJump.Target.Inputs;
                    if (inputs.Count == 0)
                    {
                        GameDebugger.Log(LogLevel.Debug, "Ignoring jump '0x{0:X16}' since it's target does not have input pins", articyJump.Id);
                        continue;
                    }
                    if (!pinIndex.TryGetValue(((FlowObjectPin)inputs[0]).Id, out dstPin))
                    {
                        GameDebugger.Log(LogLevel.Debug, "Ignoring jump '0x{0:X16}' since it's target is outside the quest container", articyJump.Id);
                        continue;
                    }
                }
                else
                {
                    continue;
                }

                obj = (BaseQuestObject)dstPin.Node;
                if (obj.Parent == null || !(obj.Parent is Quest) || ((Quest)obj.Parent).Id != ((ArticyFlowObject)articyJump.Parent).Id)
                {
                    GameDebugger.Log(LogLevel.Debug, "Ignoring jump '0x{0:X16}' since it's not targetting an object within the same quest", articyJump.Id);
                    continue;
                }

                foreach (FlowObjectPin pin in articyJump.Inputs)
                {
                    foreach (ArticyFlowConnection articyConnection in pin.Connections)
                    {
                        if (articyConnection.Target != pin)
                        {
                            continue;
                        }
                        if (!pinIndex.TryGetValue(((FlowObjectPin)articyConnection.Source).Id, out srcPin))
                        {
                            continue;
                        }
                        QuestConnection newConnection = new QuestConnection(articyConnection.Id, articyConnection.ExternalId);
                        newConnection.Source = srcPin;
                        newConnection.Target = dstPin;
                        GameDebugger.Log(LogLevel.Debug, "Connected '0x{0:X16}' to '0x{1:X16}' (jump replacement)", ((BaseQuestObject)newConnection.Source.Node).Id, ((BaseQuestObject)newConnection.Target.Node).Id);
                    }
                }
            }

            // recreate connections
            foreach (ArticyFlowConnection articyConnection in project.FlowConnections)
            {
                if (!pinIndex.TryGetValue(((FlowObjectPin)articyConnection.Target).Id, out dstPin))
                {
                    continue;
                }

                if (dstPin.Type == GraphPin.PinType.Input && dstPin.Node is Quest && articyConnection.Source.Node == questContainer)
                {
                    if (!m_InitiallyActiveQuestPins.Contains(dstPin))
                    {
                        m_InitiallyActiveQuestPins.Add(dstPin);
                    }
                    GameDebugger.Log(LogLevel.Debug, "Adding pin '0x{0:X16}' to list of initially active quest pins", dstPin.Id);
                    continue;
                }

                if (!pinIndex.TryGetValue(((FlowObjectPin)articyConnection.Source).Id, out srcPin))
                {
                    continue;
                }

                QuestConnection newConnection = new QuestConnection(articyConnection.Id, articyConnection.ExternalId);
                newConnection.Source = srcPin;
                newConnection.Target = dstPin;

                GameDebugger.Log(LogLevel.Debug, "Connected '0x{0:X16}' to '0x{1:X16}'", ((BaseQuestObject)newConnection.Source.Node).Id, ((BaseQuestObject)newConnection.Target.Node).Id);
            }

            return(badScripts);
        }