1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

DA 3.1 Global Variables and Save Games

Discussion in 'Loader' started by WeeWillie, Sep 8, 2014.

  1. WeeWillie

    WeeWillie Casual Client Content Creator

    Joined:
    Nov 8, 2013
    Messages:
    339
    Likes Received:
    13
    This is a continuation of a discussion started in the thread http://www.sdtmods.com/index.php?topic=5870.0 between me and Pim_gd. Seems like it should branch to its own thread and made public since it might be of value to others:

    My understanding of how global variables are meant to work, supported through some testing and info from Dialogue Checker is as follows:

    A global variable is first declared as any local variable in initial_settings:
    Code:
    initial_settings:{"gMyVar":0}
    The variable is then made global via a call to VA_SET_GLOBALVARIABLE, VA_SET_GLOBALVARIALBEBYNAME or VA_LOAD_GLOBALVARIABLE. For example, the following marks gMyVar as a global variable with its value set to 10 (note, this requires the fix that is going into DA 3.1. For DA 3.0 or earlier, you need to replace “10” with “+10” and have gMyVar initialized to 0)

    Code:
    "[VA_SET_GLOBALVARIABLE_gMyVar_10]"
    This variable can now be used as any other local, though any setting of the variable will not affect the stored value. For example:

    Code:
    "The value of gMyVar is *gMyVar*.” {“set”:{“gMyVar”:20}}
    Will return “The value of gMyVar is 20”. However the stored value of the global will still be 10.
    If a new dialogue is loaded, the local value of gMyVar will reset, losing the value of 20, but the following will restore it to the value of 10 in my example.

    Code:
    "[VA_LOAD_GLOBALVARIABLE_gMyVar]"
    So in summary, the global variable is used in a dialogue like a local, and has a local value, but the local value is saved in global memory during a VA_SET_GLOBALVARIABLE and it restored from local memory with a VA_LOAD_GLOBALVARIABLE.

    My new savegame functionality using [VA_SAVE_SAVEGAME_MYSAVE] and [VA_LOAD_SAVEGAME_MYSAVE] takes the values of all global variables stored in DA memory and either save or loads them to disk. As such, to avoid horrible maintenance and performance overhead of multiple VA_SET_GLOBALVARIABLE calls before making a save file, the calls do the following:

    • VA_SAVE_SAVEGAME_ goes through the list of all globals and in essence performs a [VA_SET_GLOBALVARIABLEBYNAME_varName_varName] to store all the local values that might be hanging around and move them into DA memory before making the save.
    • VA_LOAD_SAVEGAME_ loads up the saved version from disk of all the global variable, then it in essence performs a [VA_LOAD_GLOBALVARIABLE] on all variables to make sure they can be used in the dialogue.

    This seems like desired functionality, but Pim_gd, you seemed to stress that local and global need to be seperate and all global variables need to be manually handled by SETs and LOADs. Thoughts?
     
  2. Pim_gd

    Pim_gd Swell Supporter

    Joined:
    Jan 25, 2013
    Messages:
    717
    Likes Received:
    44
    Why bother with the normal sets if this change is made? Additionally, globals are not locals and by treating them as such you cause confusion, like how setting to a local copy of a global doesnt affect the globally stored value. Seems like it would be better to add a trigger for this like [VA_REGISTER_GLOBALVARIABLE_<variablename>] which makes the variable a global thats properly updated with each set. Then variables obtained with [VA_LOAD_GLOBALVARIABLE_<variablename>] could automatically give back such a variable that exists both in global and local space.

    [VA_REGISTER_GLOBALVARIABLE_<variablename>] would create a new global variable, to which writing will automatically update the global and local value.
    [VA_LOAD_GLOBALVARIABLE_<variablename>] would reattach a previously created global variable to the dialogue so that writing to the local variant would again update both values.
    This would deprecate [VA_SET_GLOBALVARIABLE_<globalVar>_<literalValue>] and [VA_SET_GLOBALVARIABLEBYNAME_<globalVar>_<localVar>]; there's no need to set explicitly to a global variable when you have a global variable that's valid as a local variable.

    I believe this change satisfies your needs (a way to set to globals without having to set to both global and local) and satisfies my needs (Previous behavior is mostly unchanged, and you have a consistent interface for things again).

    A question though... should there be a trigger to "unregister" a global?
     
  3. WeeWillie

    WeeWillie Casual Client Content Creator

    Joined:
    Nov 8, 2013
    Messages:
    339
    Likes Received:
    13
    When I first made VA_SAVE_SAVEGAME_ and VA_LOAD_SAVEGAME_, I actually thought about doing a register function. I didn't for three important reasons:
    [list type=decimal]
    [*]
    Have global values maintained on every set for a registered variable would involve me editing the guts of your global variable system. My current solution doesn't change DA GLOBAL SETs or LOADs at all, so there is zero chance that I can break it. It is only the SAVEGAME system, that I added myself, that has the copy behavior. Note that the one place I did edit your variable system, to fix the bug where integers couldn't be set with numbers not preceded by "+" or "-", was as you put it a "Nice try". I only fixed half the issue and broke a usage that I didn't even realize existed. Who knows what damage I could do trying to modify your variable system further.


    [*]Forcing a register of every global variable used in every file it is used in could be a large maintenance and performance issue. Let me illustrate my point through the example of the project I'm working on:

    The player can own multiple slave girls. Each girl has her own dialogue file. The player can purchase clothing sets for each girl, and the shopping is done in a separate dialogue file. The outfit pieces are stored in global variables, for example the first outfit is stored in gO1Headwear, gO1Top, gO1Tops, gO1Armwear, gO1Panties, gO1Legwear, gO1LegwearB, and gO1Footwear. I currently have 13 outfits to choose from, so that is 8 x 13 = 104 global variables.

    As is, each girl dialogue file can just access variables with substitution (poor mans array), like [VA_SET_VARIABLEBYNAME_clothingItem_gO*clothID*Headwear], where clothID is just an integer that is within the range of valid outfits. One dialogue file creates the global variables and saves them to the save file. Every other dialog (and there can be many, at least one for each possible slave girl) can just load the save file, and BAM, they've got all the global variables accessible with the correct values.

    With the register method, each file would have to register all 104 variables. Also, the moment I add a 14th costume, I'd have to go and edit every dialogue file. With the current method, all I do is keep the index under *numClothing* and the girls files work without edit.
    [*]Making the code change for registering variables and making the dialogue changes to add those registers is a bunch of work with no real gain in functionality. The only real advantage I see is that it would enable Dialogue Checker to more easily check for certain types of errors.


    [/list]
    Hence, you can see why I'd like to stick with the currently implemented system. Problem #1 and #3 could be solved by you implementing the system instead of me, but that would still leave problem #2, which isn't small for my project (the project that the whole save game system was actually made for)

    I'm a bit confused how the current system doesn't satisfy your needs. The global variable system and the variable arithmetic system are untouched. There is absolutely no change in behavior at all. The new behavior comes with the save game system that I added ( and I may be the only one to ever use it :) ). If you don't use VA_SAVE_SAVEGAME_ and VA_LOAD_SAVEGAME_, then nothing is different from DA 2.04.
     
  4. Pim_gd

    Pim_gd Swell Supporter

    Joined:
    Jan 25, 2013
    Messages:
    717
    Likes Received:
    44
    Im on my phone now. I'll type a proper post in a few hours, but I'd also like to discuss your specific needs tonight. I believe the best way to add new functionality is to add new triggers and variables, not by altering the existing ones. Regarding the first point you make: dont be scared of editting the code, else youll never be able to do what you want.
     
  5. Pim_gd

    Pim_gd Swell Supporter

    Joined:
    Jan 25, 2013
    Messages:
    717
    Likes Received:
    44
    I didn't mean to be negative. You shouldn't be scared of making changes or else you'll keep side-stepping the things you really wanna do.
    I know of a way to implement what I suggested and I'd be willing to add it too.

    I'm willing to discuss your needs separately. I presume your issue would be solved if I added something like [VA_LOADALL_GLOBALVARIABLES] or something like that?
    I'm not sure you got the functionality provided by register and load right:
    REGISTER would register a local variable to be a global variable as well. That is, changes to the local variable affect the value stored within DialogueActions. Presumably the reverse too, but I'm not sure about that one yet.
    LOAD_GLOBALVARIABLE would make a new local variable and give it the value of the global variable. Then, any changes to the local variable would affect the global variable too and the reverse too.

    So any variables touched by REGISTER and LOAD_GLOBALVARIABLE are kept up-to-date by DialogueActions until you load a new dialogue, at which point you have to load them again.

    I'm pretty sure there's some sort of pattern you could replace. That said, it's still creating some work for you. I don't know how to resolve that.
    One of the things I don't quite agree with is that you store ALL the global variables in a savefile. Those outfits shouldn't be part of the savefile (if you release a new version that has 18 costumes instead of 13, will that work properly with old saves?). That's a separate problem though.

    VariableArithmetic has a poor, untested interface. I don't mean it contains a lot of bugs (well, it did have one...), but I mean that it's not used a lot by people. I thus never have had feedback about the things that need improving about it. That means that you're one of the first people to make use of this! And it's likely you'll find flaws. I'm willing to help in fixing the flaws and providing you with the tools you need to build the dialogue you want to create, but it might involve changing the syntax a bit.

    If you want, I'll manually convert your dialogue to use the updated syntax via a couple replace functions.

    The change in behavior is that you just changed how globals work in a way that's not documented. I also don't agree with the change that SET_GLOBALVARIABLE functions now automatically overwrite a local variable of the same name.

    One function for one trigger.

    Personally I dislike the way I did the VA triggers; they still use "VA" in front like a scar.
    There's a underscore between SET and VARIABLE or SET and GLOBALVARIABLE, but it means nothing (and I often confuse myself with this).
    VARIABLE is one long word, and Dialogue writers do enough typing. VAR would be better.
    The underscores as a way to add arguments to a trigger is a necessary evil but it's nasty and corrupts namespaces (SET_VARIABLEBYNAME can't be SET_VARIABLE_BYNAME because it would see the BYNAME section as an argument!).

    In short; it contains some serious flaws. Flaws that I never considered; after all, most people were happy enough to just have the content. I mean, dang it, you can compare variables now! You can set one variable's value to another! Think of the possibilities!

    I'd like your help in defining a new interface. We'll get rid of the VA in front of it and integrate the triggers properly into DialogueActions, if you'll allow me to do so.
    My current plan:

    - Get your DAv3.01 (that's your current version, broken and all) source. Since it's not a public release yet, we won't label it as v3.01 (I hate version gaps). I need to look over it in full to see your changes so I can understand what features you're trying to add.
    - Draft a new interface for handling variables with triggers. Strip VA from the triggers; namespace pollution is a moot point (I can say so safely after a whole year... there's pretty much 0 chance of someone coming along and being silly enough to write dialogues that use "SET_VARIABLE_" lines. And if they do... well, you get whacked by the DialogueChecker for it (v2.15, is a new feature I added recently.))
    - Implement the new interface.
    - Convert your dialogues in progress to use the new system. If you want, I'll take care of that for you.
    - Release as DAv3.01 (even before your dialogues are done!). Take care of cleaning things up in the next versions. I'll try to comment the source of the core classes a bit more too.
     
  6. ModGuy

    ModGuy Club Regular Content Creator

    Joined:
    Feb 17, 2011
    Messages:
    1,754
    Likes Received:
    21
    Sig'd.
     
  7. WeeWillie

    WeeWillie Casual Client Content Creator

    Joined:
    Nov 8, 2013
    Messages:
    339
    Likes Received:
    13
    A few thoughts in response:

    I believe that would do the job. I just need to have a dialogue set up local versions of all the globals in a save file without having to explicitly declare them. What is in the save file can be accessed once the save game is loaded.

    I use globals in this case for two reasons. First, I don't want to have constants copied across every slave file. I hate data being in more than one place due to coherency issues. Data should be defined in one place, and dialogue's don't have shared include files. Second, as a convenience, the way I set those variables is that the very first time you play the game, I do a [LOAD_CHARCODE] behind a black screen, then load the variables off the hidden girl. This way I can design outfits in SDT and just use the Custom Save Data->Generate in the options menu to export the values. That has been a godsend for tweaking outfits. However, this method takes a bit of time when the game start for the initialization to happen, so that black screen goes on and on, but only when you start a new game. I skip that step on a continue, and the expectation is that players will usually continue from a save game, not start a new game. The reason I can skip the step is that all the data is in the savegame :)

    As for data changes, in addition to a "Continue" option at the start, there is also an "Import" option. My plan is that I'll continuously grown this set of dialogs, adding new clothing, new slaves, and new features. I would never want a player to lose their progress, so the Import feature would handle any data changes, such as the addition of a new outfit.

    As for having two classes of data, one that is global across file loads (i.e. VA globals) and one that is global across play sessions (i.e. SAVEGAME globals), I think that would just add a bunch of confusion for me. My method is to load the save on every file load, so that Continuing a game is just like loading a new file under normal gameplay.


    Ah, I'd forgotten I had made that change, so my statement about me not changing core functionality at all was false. My apologies. I currently need that for my shit to work, but I can see how that could ruffle your feathers.

    This sounds great. Let me just add a few more things to DA 3.000001 (or whatever we want to call the non-published version). Mostly I just want to add some more audio triggers. I can hand over the ownership of the code back to you, and can bundle up my current project. If you make sure the project still runs with any changes you make to DA, then that's awesome.

    It's a question mark about what we'd want to do with the auto-strand removal for frame rate mitigation. It would be nice to add a customizable user setting, but I want to get DA back into your hands asap without blocking my development on Slave Bazaar (my new project). Like I said, I don't really enjoy working on DA. I just want cool features so I can make the dialogues I want.
     
  8. WeeWillie

    WeeWillie Casual Client Content Creator

    Joined:
    Nov 8, 2013
    Messages:
    339
    Likes Received:
    13
    Pim_gd,

    You can find my version of DA 3.01 prerelease along with a bundle of my work in progress of my next project here:
    http://www.solidfiles.com/d/08367b16e4/SDTDialogueActions_WW.zip
    http://www.solidfiles.com/d/854975edfc/Loader.v5.34.WeeWillieBundle.v3.2.zip

    To try out the project that is using all the new DA features, do the following:
    1. Start up SDT and hit Play. Wait for More Clothing to finish loading (we really need a fix to this so More Clothing can work when you run a dialogue directly from Options)
    2. Bring up the Scene Option Menu (press Y) and select Slave Bazaar. You should be greeted with a black screen with menu options.
    3. Choose one of the New Game options (male, female, or shemale). You'll have to wait a bit for the game to initialize (yeah, I agree with you that need to implement the clothing data store in a better way. More to discuss there. Sadly, not all the wait if just due to the data store)
    4. You should be greeted by Talia. Choose "Buy Slave".
    5. Currently Plexia is the only slave that works at all. Pick her.
    6. When you get to Plexia's screen, pick "Purchase Her"
    7. Go back to the main menu with Talia and pick "Store", then "Clothing"
    8. In the clothing store, pick one or more outfits or costumes to buy. Purchase them for Plexia (your only option)
    9. Go back to the main menu with Talia and choose "Train Slave" then "Plexia"
    10. On Plexia's screen, pick "Clothing". Select the outfit you'd like and choose clothing pieces to dress her in. The clothing item should appear. If you click on the item again, it will toggle with the last item you had there (i.e. it will restore the last item worn in case you don't like the new choice). To remove an item Shift click it.
    11. When done return to Talia. Going back to train Plexia should restore the clothing you last had on her.
    12. You can select "Pimp Slave" to make money, and you should her audio behind a black screen when you do.
    13. You can quit from the main menu. Next time you play, you can choose "Continue" and the game should be restored with gold and clothing all as you left it. Continue should start up quickly since it bypasses the initialization. All initialization happens from just loading the save game.
    14. Selecting "Import" is similar to "Continue", but it goes through the initialization to try to repair the game. This is to be used when the clothing data base is changed, or your save game is borked at a bad menu. Import sets you back to the main menu with Talia, but you keep your slave and gold progression.

    All of these things should work with the bundles I've provided. When you make changes to DA, it would be great if you could insure my dialogs still work, and make changes to the dialogs if you need to do so to keep compatible with the new DA changes.

    At this point I will consider you having the control of DA source, and I'll make no more code changes. You have the semaphore!
     
  9. Pim_gd

    Pim_gd Swell Supporter

    Joined:
    Jan 25, 2013
    Messages:
    717
    Likes Received:
    44
    So I wanted to write a response but then you came along with source.
    I've tried it; my first reaction was "oh-wow".
    I see the scale you've got planned.

    What I want to propose is that first of all you start using Animtools v12, loader v5.39 and moreclothing v3. That might solve some of your issues and I like to keep things current.

    I can't spend time on this during weekdays. Not much, anyway. So expect it to take up to saturday before I'll actually do anything.
    I'd like to propose a couple features to you:

    First, anything starting with VA_ is deprecated. It'll work as it works now (with integer fix) but I won't make any more changes to it. This means we don't break other things when we vastly reshape a couple things.

    Then, we introduce the following new functions:
    Basic functions:
    [SETVAR_variable_value] (with [SETVAR_variable_+=value] to add and [SETVAR_variable_-=value] to substract, possibly [SETVAR_variable_@=value] and [SETVAR_variable_/=value] and [SETVAR_variable_\=value] as well, depends whether you think you need those things). Seem familiar?
    [SETVARBYNAME_variable_otherVariable] Should be familiar too.

    For each instance of [SETVAR...] there's also a [SETGLOBAL...] which does the same.

    Advanced functions:
    [COPYOBJECT_layers_variable_otherVariable] This ones's new. I'll explain it later.
    [COPYOBJECT_layers_variable_otherVariable_USING_structInfoVariable] Also new.
    [REGISTERGLOBALS_variable1_variable2_variableN] Pushes ALL of the variables listed into global space. Supports objects.
    [UNREGISTERGLOBALS_variable1_variable2_variableN] Decouples ALL of the variables listed from global space. Supports objects.
    [DELETEGLOBALS_variable1_variable2_variableN] Deletes ALL of the variables listed from global space. Supports objects.
    [DELETEVARS_variable1_variable2_variableN] Deletes ALL of the variables listed from local space. Supports objects.
    [LOADGLOBALS_variable1_variable2_variableN] Loads all of the listed variables from global space into your dialogue. Links them automatically. Supports objects.
    [GETGLOBALS_variable1_variable2_variableN] Loads all of the listed variables from global space into your dialogue, unlinked. Existing links are not erased. Supports objects.
    [SETGLOBALS_variable1_variable2_variableN] Sets all of the listed variables from local space into the global space, unlinked. Existing links are not erased.
    [SAVESETVARS_name_variable1_variable2_variableN] Creates a savefile with name if not exists and adds the variables to the savefile. Supports objects.
    [SAVEGETVARS_name_variable1_variable2_variableN] Loads from a savefile with the supplied name the listed variables to the savefile. Supports objects.
    [SAVEREMOVEVARS_name_variable1_variable2_variableN] Removes variables from a savefile. Supports objects.
    [SAVEDELETE_name] Deletes a savefile.

    About objects.
    First, some example syntax:

    Outfit1.tops
    Outfit1.bottoms
    Outfit2.tops

    [REGISTERGLOBALS_Outfit1.]

    The dot notation indicates that you wish to pass an object. Objects are collections of variables, and they're identified by the dot notation. That is, "da." is an object, "da.clothes." is an object, "da.clothes.tops." is an object. Here, "Outfit1." is an object too.
    When you perform operations on an object, you are using abstract references to a group of variables. "Outfit1." does not exist as a variable within your dialogue. It does however denote the variables that do exist; namely "Outfit1.tops" and "Outfit1.bottoms". So when you would use [REGISTERGLOBALS_Outfit1.], you register both "Outfit1.tops" and "Outfit1.bottoms". Then, in another dialogue, you could use [LOADGLOBALS_Outfit1.] and you'd have ALL of the variables denoted by the "Outfit1." object loaded into the dialogue variables. Sweet!
    A note: Basic functions do not support objects. Only advanced functions do.

    Now, about global and local space.

    The local namespace is the namespace in which you normally keep your dialogue variables. If you load a new dialogue, your local namespace is cleared, then reinitialized via initial_settings.
    The global namespace is the namespace in which you keep the dialogue variables that you want to transfer between dialogues. These only vanish when you exit SDT.


    However, as you have noted, setting to the global namespace each time you want to update a local variable (to maintain consistency) is a pain.
    Thus, I offer you methods of LINKING local and global namespaces together. When a variable is linked to the global namespace, any changes in its value in the local namespace will affect its value in the global namespace and vice versa. Please do not link mod variables; DA won't support this. More on workarounds for that later.

    REGISTERGLOBALS will set a GLOBAL variable's value to a LOCAL variable's value, then link them together. The local variable's value will overwrite whatever global value there might have been.
    UNREGISTERGLOBALS will unlink a variable. Both the global and the local variable's values will remain the same, but if a change is made, only one of the two will change.
    DELETEGLOBALS will unlink a variable, and then remove the variable from the global namespace.
    DELETEVARS will unlink a variable, and then remove the variable from the local namespace.
    LOADGLOBALS will set a LOCAL variable's value to a GLOBAL variable's value, then link them together. The global variable's value will overwrite whatever local value there might have been.
    GETGLOBALS will set a LOCAL variable's value to a GLOBAL variable's value, WITHOUT linking them together. Of course, if a link already existed, this link isn't removed.
    SETGLOBALS will set a GLOBAL variable's value to a LOCAL variable's value, WITHOUT linking them together. Of course, if a link already existed, this link isn't removed.

    (The checker is going to have hell with this; but given that globals would span over multiple files anyway it wasn't going to be supported anytime soon.)

    Basically:
    UNREGISTERGLOBALS = Unlink.
    DELETEGLOBALS = Unlink, remove globals.
    DELETEVARS = Unlink, remove locals.
    REGISTERGLOBALS = Local -> Global, link.
    LOADGLOBALS = Global -> Local, link.
    GETGLOBALS = Global -> Local
    SETGLOBALS = Local -> Global.

    So that takes care of global namespaces. Leaves us with one last tool that we need.
    Copying objects.

    You see, you use a lot of outfit modifications and whilst the idea is nice, it's just a really big hassle.
    So I thought about

    [COPYOBJECT_layers_target_source].

    Proper syntax:

    [COPYOBJECT_1_Outfit1._da.clothes.]

    What this does:
    Copy ALL variables denoted by "da.clothes.", 1 layer deep (that is, DO copy "da.clothes.tops", DONT copy "da.clothes.tops.r" - you don't need that anyway), into variables denoted by "Outfit1.".

    The result?
    Outfit1.panties
    Outfit1.bottoms
    Outfit1.legwear
    Outfit1.legwearB
    Outfit1.footwear
    Outfit1.top
    Outfit1.tops
    Outfit1.armwear
    Outfit1.headwear

    but also

    Outfit1.earring
    Outfit1.collar

    and all the other clothing variables.

    That's not quite what you're looking for, huh. So how do we get rid of the other ones we don't want?
    Enter [COPYOBJECT_layers_variable_otherVariable_USING_structInfoVariable].

    You'll have to define an object that contains the variables you want.
    So you declare OutfitObject.panties and all the other variables that make up an outfit. (Yes this is a pain, but I don't know how ELSE you're going to specify that you want A, B, D, G and H but not C, E and F from a collection)
    OutfitObject.panties
    OutfitObject.bottoms
    OutfitObject.legwear
    OutfitObject.legwearB
    OutfitObject.footwear
    OutfitObject.top
    OutfitObject.tops
    OutfitObject.armwear
    OutfitObject.headwear

    Then you use [COPYOBJECT_1_Outfit1._da.clothes._USING_OutfitObject.]

    The result?
    Outfit1.panties
    Outfit1.bottoms
    Outfit1.legwear
    Outfit1.legwearB
    Outfit1.footwear
    Outfit1.top
    Outfit1.tops
    Outfit1.armwear
    Outfit1.headwear

    are copied from da.clothes. And nothing else is.
    (You could store the OutfitObject in the globalnamespace too... it's like defining a class in flash.)

    My questions to you:
    - Do you think you understand what these triggers and so would do? (Could you use them if I made them?)
    - Do you think this provides you with the tools to do what you want? (Am I missing anything?)
    - Do you think these tools are simple enough in usage? If not, how could they be simpler?
     
  10. OzofMassage

    OzofMassage Potential Patron

    Joined:
    Aug 18, 2015
    Messages:
    11
    Likes Received:
    2
    I want to say that this was really helpful. I was pulling my hair out trying to figure out how to use the global triggers.

    Thank you.