Restoring a previous state

Announcements, support questions, and discussion for Quest Machine.
GorkaGames
Posts: 178
Joined: Fri Sep 21, 2018 8:38 pm

Restoring a previous state

Post by GorkaGames »

Hi,

With the save system, is it possible to restore to a previos state without loading?
What I wont is to restore the state of all my objects to a state that I already saved reaching a node on the quest.
For instance on my game if the player dies, it destroys, but I don't load the scene again (its an open world and it takes a while to load it), so I'd like to call the save system and "load" the previous state game on the previous node on the quest.

If not, any work around you may think?
Thanks.
User avatar
Tony Li
Posts: 22104
Joined: Thu Jul 18, 2013 1:27 pm

Re: Restoring a previous state

Post by Tony Li »

Hi,

You can certainly restore quest states and GameObject positions. But if you've destroyed an object, Quest Machine's save system will not bring it back. It assumes that you're going to reload the scene to get the original objects back. After reloading the scene, the save system's Persistent Destructible component will check if the GameObject has been marked as destroyed in the saved game. If so, it will destroy the GameObject.

What if you break the scene into smaller parts? You can use something like SECTR Stream to stream in pieces of the world as the player moves around.
GorkaGames
Posts: 178
Joined: Fri Sep 21, 2018 8:38 pm

Re: Restoring a previous state

Post by GorkaGames »

Ok, if I reload the scene, I guess I need also 2 different states (I think we need therefore 2 saving slots)

- In case the player quits the game, once he comes back everything must be on the same position and the same enemies killed, so we should save the game on the slot "quit game" and load again the slot "quit game" once he is back.

- In case the player dies, the scene reloads and I want the player on the BEGGINING (Start) of the Node / Quest he was in, so once the Scene reloads everything must be at the same position and none enemies killed, as it was at the beginning of this Node. So we should save the game on the slot "player death" and load again the slot "player death" once he is back.

Is this ok? what's the code do I need and where to accomplish that? Thanks
User avatar
Tony Li
Posts: 22104
Joined: Thu Jul 18, 2013 1:27 pm

Re: Restoring a previous state

Post by Tony Li »

Hi,

Let's say you define the slot numbers like this:

Code: Select all

public const int SavedGameSlot = 0;
public const int RestartQuestSlot = 1;
When the player quits, save to SavedGameSlot. It would be best to put this in a save/load/quit menu. But as a quick test you can do this:

Code: Select all

void OnApplicationQuit() {
    PixelCrushers.SaveSystem.SaveToSlot(SavedGameSlot);
}
To resume the game:

Code: Select all

PixelCrushers.SaveSystem.LoadFromSlot(SavedGameSlot);

When a quest becomes active, save to RestartQuestSlot:

Code: Select all

using UnityEngine;
using PixelCrushers;
using PixelCrushers.QuestMachine;

public class MyScript : MonoBehaviour, IMessageHandler) {

void OnEnable() { // Let me know when quest states change.
    MessageSystem.AddListener(this, QuestMachineMessages.QuestStateChangedMessage);
}

void OnDisable() { // Unregister when disabling script.
    MessageSystem.RemoveListener(this);
}

void OnMessage(MessageArgs messageArgs) {
    var state = (QuestState)messageArgs.values[1];
    if (messageArgs.values[0] != null) {
        // Is a node. We don't care, so exit.
        return;
    }
    if (state == QuestState.Active) { // Quest became active. Save game.
        SaveSystem.SaveToSlot(RestartQuestSlot);
    }
}
When the player dies, reload RestartQuestSlot:

Code: Select all

SaveSystem.LoadFromSlot(RestartQuestSlot);
GorkaGames
Posts: 178
Joined: Fri Sep 21, 2018 8:38 pm

Re: Restoring a previous state

Post by GorkaGames »

ok, great!!

The following: if (state == QuestState.Active) I guess is only for the general quest, not for the individual nodes, is that correct? If so, how it would be for checking that a Node (And not the full Quest) is just Active.

And I'd like to do it preferibly only for the Main Quests, how can I ask if a quest is from the group "Main" ?

In case I want to craft manually which Nodes on every quest I want to save the state, is there any action on the editor to do so? I don't remember now....
User avatar
Tony Li
Posts: 22104
Joined: Thu Jul 18, 2013 1:27 pm

Re: Restoring a previous state

Post by Tony Li »

Hi,
GorkaGames wrote: Mon Oct 15, 2018 2:09 pmThe following: if (state == QuestState.Active) I guess is only for the general quest, not for the individual nodes, is that correct? If so, how it would be for checking that a Node (And not the full Quest) is just Active.
The message is sent when a quest or quest node state changes. The quest node is in messageArgs.values[0]. Its type is StringField. The example below only saves if the quest ID is "Bake Pie" and the node ID is "Get 5 Apples":

Code: Select all

public void OnMessage(MessageArgs messageArgs) {
    StringField nodeID = messageArgs.values[0] as StringField;
    if (nodeID == null) {
        return; // Is main quest. We're only looking for the "Get 5 Apples" node.
    }
    QuestNodeState state = (QuestNodeState)messageArgs.values[1];
    if (messageArgs.parameter == "Bake Pie" && nodeID == "Get 5 Apples" && state == QuestNodeState.Active) {
        SaveSystem.SaveToSlot(RestartQuestSlot);
    }
}
(Edit: Quests use QuestState. Quest nodes use QuestNodeState. I fixed my code above.)

The QuestMachineMessages class explains all of the messages. Scroll down to "Public Attributes", and click on a message to get more information. For example, this is the information for the QuestStateChangedMessage:

Sent when a quest state or quest node state changes.
  • Parameter: Quest ID.
  • Argument 0: [StringField] Quest node ID, or blank for main quest state.
  • Argument 1: [QuestState] / [QuestNodeState] New state.
GorkaGames wrote: Mon Oct 15, 2018 2:09 pmAnd I'd like to do it preferibly only for the Main Quests, how can I ask if a quest is from the group "Main" ?
Check the Quest's group property:

Code: Select all

Quest quest = QuestMachine.GetQuestInstance("Bake Pie");
if (quest.group == "Main") {
    Debug.Log("This is a Main quest.");
}
GorkaGames wrote: Mon Oct 15, 2018 2:09 pmIn case I want to craft manually which Nodes on every quest I want to save the state, is there any action on the editor to do so? I don't remember now....
No, sorry. When you save a quest, it saves the state of the quest, the states of all of the nodes, and the values of the counters.
GorkaGames
Posts: 178
Joined: Fri Sep 21, 2018 8:38 pm

Re: Restoring a previous state

Post by GorkaGames »

Ok, so I guess this is finally the code to save when a NODE gets active:
I'm getting 2 errors:

1. IMessageHandler Can't implement because its not public
2. AddListener Not possible 2 Arguments

NOTE: I think this can be a nice feature also for your product, people will love to save the game on every Node automatically (as control points automatically)

using UnityEngine;
using PixelCrushers;
using PixelCrushers.QuestMachine;

public class MyScript : MonoBehaviour, IMessageHandler) {

void OnEnable()
{ // Let me know when quest states change.
MessageSystem.AddListener(this, QuestMachineMessages.QuestStateChangedMessage);
}

void OnDisable()
{ // Unregister when disabling script.
MessageSystem.RemoveListener(this);
}

void OnMessage(MessageArgs messageArgs)
{
//Recoge Cambio en el estado de una Quest
//QuestState state = (QuestState)messageArgs.values[1];
//Recoge Cambio en el estado del nodo de una Quest
QuestNodeState state = (QuestNodeState)messageArgs.values[1];

StringField nodeID = messageArgs.values[0] as StringField;
if (nodeID == null)
{
return; // Is main quest.
}
//Comprueba si es activa la quest
//if (state == QuestState.Active)
//Comprueba si es activa el nodo de la quest
if (state == QuestNodeState.Active)
{ // Quest Node became active. Save game.
//Guardamos en el slot 0, que lo reservamos para guardar los estados de cambios de Nodos / quests
SaveSystem.SaveToSlot(0);

Debug.Log("Save Quest Node");
if (Invector.vCharacterController.vHUDController.instance)
Invector.vCharacterController.vHUDController.instance.ShowText("Save Quest Node");
}
}
}
User avatar
Tony Li
Posts: 22104
Joined: Thu Jul 18, 2013 1:27 pm

Re: Restoring a previous state

Post by Tony Li »

Hi,
GorkaGames wrote: Wed Oct 17, 2018 4:40 pm1. IMessageHandler Can't implement because its not public
IMessageHandler is public. It looks like you may have a typo. Try removing the ")" after it:

Code: Select all

public class MyScript : MonoBehaviour, IMessageHandler {
If that doesn't work, then your project may have another IMessageHandler from another asset. In this case, specify the PixelCrushers one:

Code: Select all

public class MyScript : MonoBehaviour, PixelCrushers.IMessageHandler {
GorkaGames wrote: Wed Oct 17, 2018 4:40 pm2. AddListener Not possible 2 Arguments
MessageSystem.AddListener() expects three arguments. The third argument is the message parameter. If you want to ignore the message parameter, pass null:

Code: Select all

MessageSystem.AddListener(this, QuestMachineMessages.QuestStateChangedMessage, null);
GorkaGames wrote: Wed Oct 17, 2018 4:40 pmNOTE: I think this can be a nice feature also for your product, people will love to save the game on every Node automatically (as control points automatically)
I'll make a note for the next version after 1.0.8. I'm not adding any new features to 1.0.8 because I'm trying to release it as soon as possible. Only a few third party integrations are left to complete for 1.0.8.
(fixed call to MessageSystem.AddListener, OnMessage public)

Code: Select all

using UnityEngine;
using PixelCrushers;
using PixelCrushers.QuestMachine;

public class MyScript: MonoBehaviour, PixelCrushers.IMessageHandler {

 void OnEnable() { // Let me know when quest states change.
  MessageSystem.AddListener(this, QuestMachineMessages.QuestStateChangedMessage, string.Empty);
 }

 void OnDisable() { // Unregister when disabling script.
  MessageSystem.RemoveListener(this);
 }

public  void OnMessage(MessageArgs messageArgs) {
  //Recoge Cambio en el estado de una Quest
  //QuestState state = (QuestState)messageArgs.values[1];
  //Recoge Cambio en el estado del nodo de una Quest
  QuestNodeState state = (QuestNodeState) messageArgs.values[1];

  StringField nodeID = messageArgs.values[0] as StringField;
  if (nodeID == null) {
   return; // Is main quest.
  }
  //Comprueba si es activa la quest
  //if (state == QuestState.Active)
  //Comprueba si es activa el nodo de la quest
  if (state == QuestNodeState.Active) { // Quest Node became active. Save game.
   //Guardamos en el slot 0, que lo reservamos para guardar los estados de cambios de Nodos / quests
   SaveSystem.SaveToSlot(0);

   Debug.Log("Save Quest Node");
   if (Invector.vCharacterController.vHUDController.instance)
    Invector.vCharacterController.vHUDController.instance.ShowText("Save Quest Node");
  }
 }
}
GorkaGames
Posts: 178
Joined: Fri Sep 21, 2018 8:38 pm

Re: Restoring a previous state

Post by GorkaGames »

I have updated the code but the same. Those arre the errors on the editor: (I have already updated to the beta you sent me). Add Listener is with errors also in Visual Studio-The call is ambiguous:

Assets/Scripts/Quests/QuestNodeControllerSaver.cs(11,23): error CS0121: The call is ambiguous between the following methods or properties: `PixelCrushers.MessageSystem.AddListener(PixelCrushers.IMessageHandler, string, string)' and `PixelCrushers.MessageSystem.AddListener(PixelCrushers.IMessageHandler, string, PixelCrushers.StringField)'

Assets/Scripts/Quests/QuestNodeControllerSaver.cs(6,14): error CS0737: `QuestNodeControllerSaver' does not implement interface member `PixelCrushers.IMessageHandler.OnMessage(PixelCrushers.MessageArgs)' and the best implementing candidate `QuestNodeControllerSaver.OnMessage(PixelCrushers.MessageArgs)' is not public
User avatar
Tony Li
Posts: 22104
Joined: Thu Jul 18, 2013 1:27 pm

Re: Restoring a previous state

Post by Tony Li »

Sorry, use string.Empty instead of null.
Post Reply