Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Can you call the save menu in Sugarcube?

Does anyone know if it's possible to call the save menu in Sugarcube? I'd like to replicate whatever javascript function occurs when the user clicks 'Saves' on the Sugarcube sidebar.

undefinedundefined

Such that when these options are selected the window pops up. I was hoping to kind of find it in the header. I found a click handler for the saves link that runs the UISystem.buildSaves() function, but that doesn't seem to be everything. It's possible that there is no callable function for this. If I'm out of luck that's okay. Just curious.

Comments

  • You should be able to do what you want, however, saves are managed via SaveSystem.

    Here's the public API for the SaveSystem:

    /* SAVE SLOTS */
    // Returns the total number of available slots
    SaveSystem.length() :integer

    // Returns the total number of filled slots
    SaveSystem.count() :integer

    // Returns whether a slot is filled
    SaveSystem.has( slot ) :boolean
    <integer> slot: save slot index (0-indexed)

    // Returns a save object from a slot
    SaveSystem.get( slot ) :object
    <integer> slot: save slot index (0-indexed)

    // Loads a save from a slot
    SaveSystem.load( slot )
    <integer> slot: save slot index (0-indexed)

    // Saves the story/game to a slot
    SaveSystem.save( slot , title )
    <integer> slot: save slot index (0-indexed)
    <string> title: OPTIONAL title of the save, if omitted an excerpt from the passage is used

    // Deletes a save from a slot
    SaveSystem.delete( slot )
    <integer> slot: save slot index (0-indexed)


    /* AUTOSAVE */
    // Returns whether the autosave is filled
    SaveSystem.hasAuto() :boolean

    // Returns the autosave object
    SaveSystem.getAuto() :object

    // Loads the autosave
    SaveSystem.loadAuto()

    // Saves the story/game to the autosave slot (you probably don't want to call this manually, or maybe you do)
    SaveSystem.saveAuto( title )
    <string> title: OPTIONAL title of the autosave, if omitted an excerpt from the passage is used

    // Deletes the autosave
    SaveSystem.deleteAuto()


    /* DISK SAVES */
    // Saves the story/game to disk
    SaveSystem.exportSave()

    // Loads a save from disk (you do not call this manually, it must be called by the change handler of an <input type="file"> element)
    SaveSystem.importSave( event )
    <event object> event: the event object which was passed to the change handler of the associated <input type="file"> element


    /* PURGE */
    // Deletes all saves (incl. the autosave, if it's enabled)
    SaveSystem.purge()
    There are several configuration options for saves (from config.saves.autosave to config.saves.slots).

    There's currently no method built into SaveSystem which allows you to access the title and datestamp for a given save slot (which is something I should rectify).  If you wanted access to those, then you'll have to load the saves object from storage yourself (this is the way that UISystem currently does it).  Doing so would look something like this: (not including some safety boilerplate that you probably don't need)

    var saves = storage.getItem("saves");
    if (saves !== null &amp;&amp; SaveSystem.OK())
    {
    var saveSlot, saveTitle, saveDate;
    for (var i = 0; i < saves.slots.length; i++)
    {
    saveSlot = i + 1;
    if (saves.slots[i])
    {
    saveTitle = saves.slots[i].title;
    saveDate = new Date(saves.slots[i].date).toLocaleString();
    }
    else
    {
    saveTitle = "";
    saveDate = "";
    }

    /* do something UI-ish here */
    }
    }
    [EDIT] Updated the SaveSystem public API docs above.
  • Thanks Mad. This is actually much better than what I asked for since I was planning on modifying the existing style (probably at great pains) but with this I can just write my own UI. I'll end up doing something like this.

    undefined

    You continue to be a huge help.
  • undefined

    Working great so far! But I'm having some trouble using SaveSystem.save. It throws errors. Is this the correct way to use it?
    SaveSystem.save( 0 [, "Test Save Location"] )
    Even if I use variables and not the quotes for a string, I get:
    bad expression: Unexpected token ,
    EDIT: Nevermind. I just missed the obvious typo in your post. Working totally fine now.
  • CoraBlue wrote:

    Is this the correct way to use it?
    SaveSystem.save( 0 [, "Test Save Location"] )


    Drop the square-brackets, they're notational.

    CoraBlue wrote:
    EDIT: Nevermind. I just missed the obvious typo in your post. Working totally fine now.


    Assuming you mean the square-brackets, they're not a typo, they're a notation for denoting optional parameters.
  • If case anyone else digs this up later, it's also handy to know that all your save files' game variables are stored in:
    var saves = storage.getItem("saves");
    saves.slots[i].data[n].variables
    The i would be the save slot, the n is the history state.
  • For the benefit of future users who might stumble on this thread and are interested in the original question of how to bring up SugarCube's UI items from your own code (e.g., because you are not using the built-in menu or you want to offer multiple ways to get to the same menu): you need to use the SugarCube UISystem's addClickHandler method. Here's complete code for attaching the saved games menu to a new button in the upper-right of the screen:
    :: StoryInit
    /% Special code that triggers at startup %/
    <<script>>
    $("body").append("<button id='save-sub' style='position:fixed; top:0; right:0;'>Inspect saved games</button>");
    UISystem.addClickHandler("#save-sub", null, function () { UISystem.buildSaves(); });
    <</script>>
    You can see more by paging through the SugarCube source's main.js.
  • Since UISystem.addClickHandler() has been trotted out, I suppose that I should post this here:

    The UISystem "library" consists of two methods at this point (well, there are several others, but they aren't meant to be public).  The API looks like this:

    // Closes the dialog (#ui-overlay &amp; #ui-body) and removes all classes and content from the dialog container (#ui-body)
    UISyetem.close()

    // Adds a click event handler to the target element which will pop open the dialog; additionally, adds a click event handler
    // to #ui-overlay and any elements with the class .ui-close which calls UISyetem.close() and callbackFunc (if it exists)
    UISyetem.addClickHandler(target, options, startFunc, doneFunc, callbackFunc)
    target The DOM element to add the handler to (can be either the actual DOM object or a jQuery-style selector set*)
    options Options object; the currently understood properties (which are subject to change) are:
    top Top y-coordinate of the dialog (default: 50; in pixels, but without the unit)
    opacity Opacity of the overlay (default: 0.66)
    startFunc Function to execute at the start of addClickHandler(); commonly used to setup the dialog
    doneFunc Function to execute at the end of addClickHandler()
    callbackFunc Function to execute when #ui-overlay or any elements with the class .ui-close are clicked
    * Note: You can select multiple elements this way, so ensure that you use the proper selector.

    You should always specify a target and (likely) a startFunc (which is what should populate the dialog container, #ui-body).

    As an example, this is how you might setup a dialog:

    // You'll need to set up the dialog's handler (say you call the element that you want to click on "stats-dialog")
    UISystem.addClickHandler("#stats-dialog", null, setup.buildStatsDialog);

    // The dialog populating function (in jQuery and non-jQuery flavors, use only one)
    // jQuery version
    setup.buildStatsDialog = function (e)
    {
    jQuery("#ui-body")
    .empty()
    .addClass("stats");
    new Wikifier(menu, tale.get("StatsDialog").processText().trim());
    return true;
    }

    // non-jQuery version
    setup.buildStatsDialog = function (e)
    {
    var menu = document.getElementById("ui-body");
    removeChildren(menu);
    menu.classList.add("stats");
    new Wikifier(menu, tale.get("StatsDialog").processText().trim());
    return true;
    }
    Technically, there's no reason you couldn't just make startFunc completely anonymous, which would make the call to addClickHandler() look like this:

    // You'll need to set up the dialog's handler (say you call the element that you want to click on "stats-dialog")
    UISystem.addClickHandler("#stats-dialog", null, function (e)
    {
    jQuery("#ui-body")
    .empty()
    .addClass("stats");
    new Wikifier(menu, tale.get("StatsDialog").processText().trim());
    return true;
    });
    Basically, your startFunc should:
    [list type=decimal]
    Select the dialog container (#ui-body)
    Ensure that any previous contents are gone from it
    Add a class to it for selection purposes (e.g. in your CSS, #ui-body.stats { })
    Add your content to it (and you don't have to wikify it either, build its contents however you want)
  • Perfect timing, I was just wondering whether/how it would be possible to use this for my own UI elements. Thanks!
Sign In or Register to comment.