Shadowrun Returns

Shadowrun Returns

39 ratings
How To Create A Save-Anywhere System In Shadowrun Returns
By Firesnakearies
This guide will explain, in detail, how I created the fully working save-anywhere system in my Shadowrun Returns content pack, Training Room and Testing Arena, and how you can build a similar system into your own content packs, or anyone else's that you wish to modify.
   
Award
Favorite
Favorited
Unfavorite
1. Introduction
This guide will explain, in detail, how I created the fully working save-anywhere system in my Shadowrun Returns content pack, Training Room and Testing Arena, and how you can build a similar system into your own content packs, or anyone else's that you wish to modify.

http://steamcommunity.com/sharedfiles/filedetails/?id=164782118

This is Version 1 of this guide, and only includes the work that I've already completed and verified. Not all topics are covered in this version, including dealing with hired Shadowrunners and dealing with the Matrix. Once I have successfully solved those, or other unincluded issues and proved that my methods work, I will update this guide to cover the latest that I've learned.

In Shadowrun Returns, the game is only saved when a new Scene is loaded. You cannot save your progress in mid-Scene. A Scene is a discrete chunk of gameplay, generally based on a single map, with its own goals and variable states that are independent of other Scenes. Essentially, it's what would be called a "level" in many video games.

Each Scene contains variables specific to that Scene, and these variables reflect what the player has done in the current instance of the Scene, the changes to the Scene's starting state that have been made, and what events and options can still happen in the Scene. These Scene Variables do not save when the Scene is exited to load a new Scene. When a Scene is loaded, it is always fresh and new. It starts off in the same state every time.

What is being recorded when the game saves is your character information, and the current state of what are called Story Variables, which are the variables shared across the entire content pack, not specific to one Scene but able to be read and changed by any Scene.

This save-anywhere system leverages the power of Story Variables to save information which would normally not be saved by the game, and then use those saved Story Variables to re-build the fresh instance of the Scene that is loaded (necessary for the game to save at all) not in its normal starting state, but in the state that the player left it. Thus, the player's progress is preserved, even mid-Scene.
2. A Note About Accessibility
From here on, this guide assumes at least a rudimentary level of understanding of how the Shadowrun Editor works, and how to use its basic functions. If you need help with this, http://shadowrun-returns.wikispaces.com/Editor+Overview is a great place to start. The wiki is far from complete, but it does contain a great deal of useful information, and the tutorials there can get you well on your way to proficiency with the Editor. I do want this to be accessible though, so I'll try to explain as much as I can, and give helpful hints for those who are not very familiar with the Editor.

For instance, in the sample Triggers provided throughout this guide, if it's the first time a specific option from the menus for conditions, prerequisites, or actions has been used in this guide, I'll include a comment in brackets and italics after that line indicating what path / specific item to choose in order to make the right kind of logic statement. (Some of them aren't obvious, or they're tricky to find.)
3. Setting Up The Core Click-To-Save Functionality, Part 1
To start with, we need to create a Conversation which will actually allow the player to save the game by re-loading their current Scene. The nice thing about Conversations in the Editor is they're automatically Story Data, not Scene Data, which means this one Conversation will work throughout our entire content pack. We won't have to make a new one for each Scene. But we WILL have to know what Scene the player is in when they initiate this Conversation. We'll also need to know whether or not the player has recently used our save system. So before we make our Conversation, we need to create our first couple of Story Variables.

Go to Edit Content Pack Properties, in the Edit menu. (There's also an icon on the quickbar for it.) In the lower-right section of that window, you'll see a list of Variable Names and Values. The list may be totally blank for you. In any case, we create new Story Variables simply by clicking on the green plus sign above the list.

The first Story Variable will be used to keep track of what Scene the player is in whenever they want to use our save system. Name it whatever you like. I'll use the names that I used in my content pack for example purposes, but of course you can name any of these things however it suits you.

VARIABLE NAME: LastScene
PROPERTY TYPE: Int

We use Int (short for Integer) as the type for this one, because that allows the Variable to be a number, and we can easily assign numbers to our Scenes. But we'll get into those Triggers later.

The next Variable we're going to make is very important, as it will be used whenever a Scene is first loaded to determine whether the Scene should be loaded fresh, or modified by all of our saved data. This Variable will be referenced often through the whole save system. I called mine PortedOut because in the first version of my system, I had the player teleporting away to a totally different Scene to save, but now thanks to a smart person on the Shadowrun.com forums, I've since made a better version which doesn't require that. A more apt name might simply be Saved or MidsceneSaved or something along those lines.

VARIABLE NAME: PortedOut
PROPERTY TYPE: Bool

We make this Variable a Bool (short for Boolean) because that makes it a simple true-false statement. Either it is, or isn't the case. Whenever we utilize the save system, this Variable will be set to true, and after we've loaded back into our Scene and the various Triggers have had their chance to check this Variable and do their magic, we'll have something set up to automatically change it back to false. (We'll get to that.)

Now that we have these two baseline Story Variables in place, we can make our Conversation that is the actual core of the save system. So create a new Conversation, I called mine Click to Save. The first blue node can be as simple as "Do you want to save the game?", maybe in GM text. Then you're going to want (1 + number of Scenes in your content pack) red nodes under it, as you'll need a separate version of the line that initiates the save for each Scene you have, and then one line to simply exit out of the Conversation without saving. So for my pack, I have three red lines:

Do nothing. [END DIALOGUE]
Save the game. [END DIALOGUE]
Save the game. [END DIALOGUE]

Yes, the last two are identical. The first blue node, and the "do nothing" line need no conditions or actions. But each instance of your "save the game" line needs to have the following setup:

PREREQUISITES FOR SELECTING THIS LINK:
( If Training Room and Testing Arena-PortedOut is false ) [AND, then Functions / Comparison (bool)]
and
( If Training Room and Testing Arena-LastScene is equal to (1) ) [AND, then Functions / Comparison (int)]
ACTIONS WHEN THIS LINK IS CHOSEN:
Set the (bool) Variable Training Room and Testing Arena-PortedOut to true [System / Set Variable (bool)]
Open Scene Trainer Room 1 [System / Open Scene]

But of course, instead of "Training Room and Testing Arena" it will be the name of your content pack that pops up there. And that (1) in the second part of the prerequisite will have to be set to a different number for each Scene in your content pack. Then under actions, which Scene you're telling it to open will be different for each Scene as well. That is why you need to have multiple versions of the line. Each version will check to see if the player is in the correct Scene (ie, what the LastScene Variable is currently set to) and then whichever one he qualifies for, that's the Scene that will be loaded. Also, click Hide if Unavailable on the bottom of the Dialogue tab on each of those "save the game" lines, so that the player only sees one version each time, and it's always the right version.
4. Setting Up The Core Click-To-Save Functionality, Part 2
Okay, so now we have a Conversation that will actually save our game. Now to give the player a way to activate this Conversation at any time they choose. For that, we'll use the On Actor Clicked condition in a Trigger. But first, you have to decide something. Do you want the player to be able to save even during combat? Or to have that Conversation pop up on them if they accidently click on their character during combat? I strongly recommend against it, but if you want that, you can ignore this next little section.

For those who DON'T want players to be able to bring up that Conversation, and thus save, in the middle of combat (and it's a very bad idea for several reasons, with a modded-in save system like this), we need to create another Story Variable. Simple enough, this one will just keep track of whether they're in combat or not. It also involves putting a couple of Triggers in each Scene, but that's no big deal.

VARIABLE NAME: InCombat
PROPERTY TYPE: Bool

Let's make those Triggers now. You'll need these Triggers in every Scene in your content pack, unless a Scene has absolutely no chance of the player entering combat.

TRIGGER NAME: In Combat
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: Yes
WHEN: When any team has entered combat [On Combat Entered]
IF: -
DO: Set the (bool) Variable Training Room and Testing Arena-InCombat to true
OTHERWISE: -

TRIGGER NAME: Out of Combat
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: Yes
WHEN: When any team has exited combat [On Combat Exited]
IF: -
DO: Set the (bool) Variable Training Room and Testing Arena-InCombat to false
OTHERWISE: -

Nice and easy. Remember that you can right-click on your Triggers and copy them, then easily paste them into other Scenes. I tried using various IF conditions on these to limit the small possibility that some other NPCs in a Scene might be fighting far away and mess up the player's ability to save, but none of them worked. So for now I left them out, and it works well. I think if something like that came up in a specific Scene, I'd just find a workaround for that one instance.

Now, finally, we'll set up the basic Trigger that initates the Conversation that allows saving for the player. You'll also need to copy this Trigger into every Scene in your content pack.

TRIGGER NAME: Click to Save
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: Yes
WHEN: When an Actor is clicked on by the player [On Actor Clicked]
IF: ( If Triggering Actor is Player Character 0 ) [AND, then Functions / Comparison (Actor)]
and
( If Training Room and Testing Arena-InCombat is false )
DO: Start conversation Click to Save with Player Character 0 without facing target. [Dialog / Start Conversation]
OTHERWISE: -

The last thing that needs to be done to make everything we've built so far "work" on a basic level (though not yet do what we actually want) is to make sure that the save Conversation knows what Scene we're in. We have the dialogue options to save the game locked behind prerequisites, remember? We already set up our Story Variable for that, but now we have to make the Triggers that put it to work.

Put the following Trigger in each Scene of your content pack:

TRIGGER NAME: LastScene 1
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: When a map is loaded and initialized [On Map Start]
IF: -
DO: Set the (int) Variable Training Room and Testing Arena-LastScene to (1)
OTHERWISE: -

Make sure each Scene has a different number, and that the version of this Trigger for each Scene uses that number in the DO line. Then you just have to ensure that the "save game" lines you set up in the Conversation have the right value of the LastScene Story Variable matched up with the correct Scene to open, and you're all set.

At this point, we have a working system that will allow the player to click on their character at any time, as long as they're not in combat, and it will bring up a Conversation that will re-load the Scene they're currently in, saving their game in the process.

But we're far from done, because with what we have so far, nothing about the player's progress in the Scene is being saved at all. We pretty much just have a click-to-restart-the-level system here, not a click-to-save. Let's see what we can do about that, eh?

This is where the hard work begins.
5. Saving The Player's Location
Well, what sort of things need to be saved in order for it to be a true save game? The first and most obvious is the player's location. Now, originally I had an extremely labor intensive and not very clever method of dealing with this particular problem. I created lots of Regions, dividing up the map into very small sections. Then for each Region, I made two Triggers, one to record that the player had entered it, and one to return the player to it on re-load if that was the last Region they'd been in. This only teleported the player close to where they'd been, not necessarily in the exact spot. And it was an awful lot of work. To give you an example, my Testing Arena map had 26 Regions devoted to this, for a total of 52 separate Triggers solely for the purpose of placing the player in the right area, on one map. It was not so good.

But then, thanks once again to a much smarter person than myself on the official forums, I was given a vastly better idea. So, fortunately for you, this step will be incredibly easy now!

First off, let's make a couple more Story Variables:

VARIABLE NAME: LocationX
PROPERTY TYPE: Float

VARIABLE NAME: LocationZ
PROPERTY TYPE: Float

These Variables are going to record the precise location of the player at the moment they initiate the save game system. Float Variables are like Int, but they can have decimals. We're not actually going to use any decimals with these, but we're choosing that type because another specific type of function in the Trigger options asks for it.

Now let's go back to our Click to Save Trigger that we made. Everything's going to stay the same, except we're going to add two extra lines to the DO section. Here's what it'll look like now:

TRIGGER NAME: Click to Save
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: Yes
WHEN: When an Actor is clicked on by the player
IF: ( If Triggering Actor is Player Character 0 )
and
( If Training Room and Testing Arena-InCombat is false )
DO:
Set the (float) Variable Training Room and Testing Arena-LocationX to ( ( Current location of Triggering Actor ) < x > ) *
Set the (float) Variable Training Room and Testing Arena-LocationZ to ( ( Current location of Triggering Actor ) < z > ) *
Start conversation Click to Save with Player Character 0 without facing target.
OTHERWISE: -
* [System / Set Variable (float)] then after "to" it's [Functions / Point Component (float)]

That's a tricky one, but it's very powerful, as it will now record to your Story Variables the exact location of the player at the time of saving. Make sure you drag those two new lines in the DO section above the line about starting the Conversation, so they happen first.

Now let's make it work for us. Create a new Trigger, which will need to be in all of your Scenes:

TRIGGER NAME: Return
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: When a map is loaded and initialized
IF: If Training Room and Testing Arena-PortedOut is true
DO: Warp Teleport ( Single Actor: Player Character 0 ) to ( < x: Training Room and Testing Arena-LocationX , z: Training Room and Testing Arena-LocationZ > ) and face No Facing when done *
Teleport the camera to Player Character 0 [Gameplay / Teleport Camera To Actor]
OTHERWISE: -
* [Actors / Teleport Actor] then after "to" it's [Functions / Specified Point (2D)]

And now, whenever a Scene loads up, if the player has just used the save function (and thus set that PortedOut Variable to true), it will teleport the player right back to the spot on the map that they were before.
6. Saving The Player's Kills
What's next? Well, if there are enemies in your Scene, you're going to need to set up Triggers that will make sure that the ones the player has killed stay dead after saving. We'll need to use two different Triggers for this, per enemy. These will be specific to each Scene of course, but you can copy and paste the template for the Triggers and just change the particulars for each hostile Actor you use.

First we need to create Story Variables tracking whether or not the characters are alive.

VARIABLE NAME: PitezelDead
PROPERTY TYPE: Bool

Repeat this for each Actor in the Scene who could be killed.

Then we make Triggers for each of those Actors as follows:

TRIGGER NAME: Death 1
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: Yes
WHEN: When (Triggering Actor) kills (Triggering Target Actor) [On Actor Death]
IF: If Triggering Target Actor is Pitezel : Actor Spawner [Comparison (Actor)]
DO: Set the (bool) Variable Training Room and Testing Arena-PitezelDead to true
OTHERWISE: -

And now that our game knows who is dead and who isn't, we can call that when we load the Scene after saving with this Trigger:

TRIGGER NAME: Spawn 1
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: When a map is loaded and initialized
IF: ( If Training Room and Testing Arena-PitezelDead is true )
and
( If Training Room and Testing Arena-Ported Out is true )
DO: Kill ( Single Actor: Pitezel : Actor Spawner ) ( Death Method: Vanish ) [Actors / Kill Actor]
OTHERWISE: Set the (bool) Variable Training Room and Testing Arena-PitezelDead to false

And again, you'll need one of those for each potentially hostile Actor in the Scene. Note that it only kills them if PortedOut is true, so it won't ever accidently fire and kill things on a real fresh load of the Scene, even if some of those Story Variables get left on somehow. That's what the OTHERWISE line is for, also. To "clean up" the Variable and make sure that it's not left on true when it shouldn't be.

With those set up, your saved game now has all of your progress in terms of enemies slain preserved. But what about other changes to the map or its states?
7. Saving General Map State Changes
In general terms, all you need to do is make a Boolean Story Variable (or an Int Variable if the thing you want to track involves a number of something) for each thing in the Scene that can be changed and is important. I'll provide an example from my Testing Arena map.

I have the map divided into one very large "Arena" region and one small "Safe Zone" region that the player starts in. While in the Safe Zone, the player is out of combat and out of turn-based mode, and all of the hostile enemies inside the Arena are rendered inert and in fact not enemies at all (their Team is set to Civilians). Only when the player crosses the threshold into the Arena do the Actors inside become hostile and put the player into turn-based mode. But by talking to the Arena Master, the player can choose to set the Arena to peaceful mode and run around safely inside. Or they can talk to him again and return the Arena to its normal, hostile state.

This is something that would need to be included in the save game, so I created a new Story Variable for it:

VARIABLE NAME: ArenaSafe
PROPERTY TYPE: Bool

So now, on the the node of dialogue in the Arena Master's Conversation where he sets the Arena to safe, I just added one item:

WHEN THIS NODE IS DISPLAYED:
Set the (bool) Variable Training Room and Testing Arena-ArenaSafe to true

And when the player chooses the dialogue option to turn the Arena back to normal, a similar action is added to set that Boolean Variable back to false.

When players activate the Arena Enter Trigger by stepping into the Arena Region, that Trigger has the IF line:

If Training Room and Testing Arena-ArenaSafe is false.

That way the Trigger which normally makes the Arena hostile only fires if they haven't enabled safe mode by flipping that Variable to true.

Because ArenaSafe is a Story Variable, it will automatically be preserved after saving. But to make sure that it doesn't stay on and disable the Arena when it's not supposed to (ie, a fresh load of the Scene), I make one more Trigger as follows:

TRIGGER NAME: Cancelling ArenaSafe
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: When a map is loaded and initialized
IF: If Training Room and Testing Arena-PortedOut is false
DO: Set the (bool) Variable Training Room and Testing Arena-ArenaSafe to false
OTHERWISE: -

That way, when the Scene is loaded fresh (hence, PortedOut not being true), ArenaSafe will be turned off immediately, and if the player is loading from a mid-Scene save, PortedOut will be true, thus the state of the Arena will be retained.

This is just one example of a general state change that may be in effect at some point during a Scene, and which would need to be preserved for a true save game. Your Scene may have many such states that can be altered by the player, so you'll have to make a separate Story Variable (remember, not a Scene Variable!) and Trigger for each one. Depending on the specifics of your Scene, you may be able to find ways to combine some of these into one Variable, or one Trigger. But the basic principle is always the same.
8. Saving Status Of Goals
Another element of player progress in a Scene is what the game calls Goals. These are the quest objectives that players are required (or have the option) to complete in the Scene. Often a Scene has several different Goals, and they can be set to one of four states: Hidden, Started, Success, or Failure. Hidden means the Goal hasn't been triggered or received yet. It's the default for any Goal that the player isn't supposed to receive immediately upon loading into the Scene. Started means the player has that Goal active, it's listed in their PDA, and at some point they saw a toast notification for it at the top of their screen. Success and Failure are exactly what they sound like, the end points of a Goal.

Each of the Goals in your Scene will always be in one of these four states, and this is something that we need to keep track of when the player saves. The way that I did it in my Arena Scene was to set up two different Boolean Story Variables for each of the seven Goals in the Scene. One for Started, and one for Success, for each Goal. So for example, Goal1Acq, Goal1Done, Goal2Acq, Goal2Done, etc. However, I realized after setting it up that way that a far better method would be to simply set up a single Integer Variable for each Goal instead. That way you could just set each Goal to 0 for Hidden, 1 for Started, 2 for Success, and 3 for Failure. Then you wouldn't need to be messing with setting Acq to true, then to false and setting Done to true, and so on. So I'll describe the way I should have done it.

For my Arena, I have seven Goals. They all default to Hidden, but the first Goal, Talk to the Arena Master, becomes set to Started at the first loading of the Scene by a complicated Trigger that we'll take a look at shortly. So the player starts with this one Goal. As soon as they speak to the Arena Master, that Goal becomes set to Success, and the player immediately receives the other six Goals, all of which become set to Started by an action attached to the first node of the Conversation. I've set it up so that this part of the Conversation (the part that gives the Goals) is only available the first time on a fresh instance of the Scene that the player talks to the Arena Master. This is how that node looks:

PREREQUISITES FOR SELECTING THIS LINK:
( If Training Room and Testing Arena-Goal1 is equal to 1 )
ACTIONS WHEN THIS LINK IS CHOSEN:
Set the status of Talk to the Arena Master. to Success [Gameplay / Set Goal Status]
Set the (int) Variable Training Room and Testing Arena-Goal1 to 2
Set the status of Kill the Emerald Ripper one more time. to Started
Set the status of Arrest the Lone Star Captain (violently). to Started
Set the status of Solve Ray's quandary (permanently). to Started
Set the status of Defeat Harlequin in a duel. to Started
Set the status of Clean up the Toxic Spirit. to Started
Set the status of Take McKlusky's bloody badge. to Started
Set the (int) Variable Training Room and Testing Arena-Goal2 to 1
Set the (int) Variable Training Room and Testing Arena-Goal3 to 1
Set the (int) Variable Training Room and Testing Arena-Goal4 to 1
Set the (int) Variable Training Room and Testing Arena-Goal5 to 1
Set the (int) Variable Training Room and Testing Arena-Goal6 to 1
Set the (int) Variable Training Room and Testing Arena-Goal7 to 1

Now, because the Goal1 (int) was automatically set to 2, the player will no longer qualify for that node of the Conversation, and thus won't be able to accidently set all of those other Goals back to Started or (int) 1. Then I set up a separate Trigger for each of Goals 2-7 that looks like this:

TRIGGER NAME: Goal 2 Done
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: When (Triggering Actor) kills (Triggering Target Actor):
IF: ( If Triggering Actor is Player Character 0 )
and
( If Triggering Target Actor is Holmes : Actor Spawner )
DO: Set the status of Kill the Emerald Ripper one more time. to Success
Set the (int) Variable Training Room and Testing Arena-Goal2 to 2
OTHERWISE: -

And five more Triggers just the same for the other five Goals. So now we have the status of our Goals locked in with Story Variables, so they'll automatically persist after saving. But just because those (int) Variables know what stage of each Goal the player is on, that doesn't mean the game itself applies it when the Scene re-loads. So we have to have Triggers in place to make that happen, just like we did for each dead Actor. In this case, I was also able to make the Trigger that sets this into motion serve double duty and also be the very Trigger that grants a new player on a fresh map their first Goal. Here's how it looks:

TRIGGER NAME: Goals Loading Trigger
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: When a map is loaded and initialized
IF: ( If Training Room and Testing Arena-Goal1 is equal to 2 )
and
( If Training Room and Testing Arena-PortedOut is true )
DO: Send out the Game Event Goals Loading [System / Send Event ]
OTHERWISE: Set the status of Talk to the Arena Master to Started
Set the (int) Variable Training Room and Testing Arena-Goal1 to 1

See what I did there? It checks to see if Goal1 is at a Success state, which means that the player has already talked to the Arena Master, and thus has to have at least started the other six Goals. But it also checks to see if the player is loading from a mid-Scene save, because if not, then we don't want the game thinking that they already have progress. So unless both of those things are true, it proceeds to launch the initial Goal, which is what's supposed to happen when a new player loads the Scene fresh.

In the DO line, I used something that we haven't talked about yet in this guide, and that's an Event. Events were a bit confusing to me at first, because I was expecting them to do something inherently, due to the name "Event". But they don't, actually. When you create an Event, it's really just a name that doesn't do anything on it's own, but can be used by Triggers as a WHEN condition. It's like a signal, from one Trigger to another. Like the first Trigger shouting "Now!" and whatever Triggers you have set to activate on that Event waiting to hear the "Now!" signal, then jumping into action.

So the previous Trigger doesn't do anything with Goals 2-7 directly, instead it simply fires off an Event, and then I have six more Triggers that are keyed to that Event, which look like this:

TRIGGER NAME: Goal 2
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: Goals Loading
IF: If Training Room and Testing Arena-Goal2 is equal to 2
DO: Set the status of Kill the Emerald Ripper one more time. to Success
OTHERWISE: Set the status of Kill the Emerald Ripper one more time. to Started

And the same for the other five Goals. Now, you'll notice that this Trigger will only set Goal 2 to Success or Started. This is because the Trigger only fires on the Goals Loading Event, and we know that that Event can only be sent if the player already has Goal 1 Success, which means that they would have had to have received Goals 2-7. There's also no need to set the (int) Variable for this Goal again, as it already has to be either 1 or 2, or else this Trigger never would have happened.

And with that, everything is in place to retain the player's progress on all of the Goals, and make sure that they get them when they're supposed to, and that none of the Story Variables get accidently carried over wrong and mess anything up. This took a little trial and error, as I had a few test runs where things weren't quite working flawlessly. But this combination of Variables and Triggers is pretty ironclad.
9. Cleaning Up The Mid-Scene Save Flag
Now, if you'll recall, way back when we set up the PortedOut Variable to keep track of when the player had actually made a mid-Scene save, and to sort of be the gatekeeper for when all of these Story Variables would apply themselves to the load of a Scene and when they wouldn't, I mentioned setting something up to remove that flag from the player after re-loading. If we didn't do this, then the next Scene they entered, even if it was supposed to be a new, fresh Scene, would act like the player was loading from a save in that Scene, and possibly fire off a bunch of Triggers and mess things up. So here's how I reset that Variable back to false, using the following two Triggers:

TRIGGER NAME: Dropping Ported Flag Setup
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: When a map is loaded and initialized
IF: If Training Room and Testing Arena-PortedOut is true
DO: Send out the Game Event No Longer Ported Out after (15) seconds [System / Send Event After Delay]
OTHERWISE: -

TRIGGER NAME: Dropping Ported Flag Execution
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: No Longer Ported Out
IF: -
DO: Set the (bool) Variable Training Room and Testing Arena-PortedOut to false
OTHERWISE: -

Why two Triggers? So that I can take advantage of the option to set a delay. That's one of the big advantages of using Events to set off Triggers, the fact that you can send Events after a delay of whatever length you want. Why a delay? Because with the amount of different Triggers and things happening and spawning or not spawning, and so forth, at the start of the map, not everything gets processed instantly. And since there could be many Triggers happening based on the fact that PortedOut is true, if we simply turn it to false right at map load, it could possibly mess up one or more of those other Triggers. So I set a nice, safe delay to make sure that doesn't happen.
10. The Trouble With Ninjas (Dealing With Special Situations), Part 1
Most of what we've covered thus far has been pretty simple, straightforward stuff. Actors alive, Actors dead, Goals finished or not, basic yes/no state changes in the map. But in the course of setting up this kind of save system for a more complicated content pack, and especially in retro-fitting Dead Man's Switch with such a system, there are going to be plenty of strange, unique challenges. Special circumstances where tracking and restoring the player's progress will be tricky, sometimes exceedingly so. While the basic principle of Set Story Variable, Use Trigger On Map Load To Check Story Variable is pretty much always going to be at the core of solving any of these problems, sometimes it might take some creative experimenting with your Triggers and Variables to make it all work as it should.

For the final lesson of this initial version of the guide, I'm going to give you an example of one of these extra-difficult problems that I faced, and how I eventually managed to figure it out. What perplexed me so? One word: ninjas.

So, in the continuing effort to improve my Training Arena mod and make it more functional, I recently added the ability, via Conversation with the Arena Master, to summon forth cloned ninjas, in any number the player desires. (The game, by the way, hates spawning new Actors on an already loaded map. Try calling up those ninjas by the 20-pack and watch the game act like it's having a heart attack.) I was happy with the way this functioned, and I even gave the option to spawn all of the ninjas in the center of the Arena, or randomly spread them out all over the whole map. Worked well, and it seemed fun. No problem, right?

But then it came time to make these infinite ninjas work with my save-anywhere system. And the problems began. First of all, this was inherently a more complicated situation than merely setting Variables for each of a group of specific, limited Actors. Easy enough to know whether or not McKlusky is dead, he's always there, and there's always only one of him. But there could be any number of ninjas, and they didn't have individual names or identities. Plus, they could be either in the middle of the map, or all over the place.

Well, I was determined to preserve this part of player progress, just like everything else. If a player spawned 17 ninjas, killed 6 of them, then saved the game, I was darn well gonna make sure there were 11 ninjas still in that Arena when they re-loaded the game. I tried several different ideas, and nothing was working. The Variable I'd set up was somehow counting the ninjas incorrectly, when I tried using Trigger Execution Count as a Function to add to an (int) Variable it wasn't coming up with the right number at all, and every time I thought for sure I'd created the perfect set of Triggers, I'd test it and there'd be entirely the wrong number of ninjas. I was making so many Triggers and Events with Ninjas in the name that I started getting frustrated, naming some of them things like "Come On With These Ninjas" and "Argh So Sick Of Ninjas".

But then I made it work. I'm not sure if this is the best or most elegant solution, I suspect not. But it works! So here it is. To start with, I created this Story Variable:

VARIABLE NAME: NinjasAlive
PROPERTY TYPE: Int

Then I just set Conversation node actions to increment that up by one every time the Arena Master spawned a ninja (or by 6, or 20, when using those options). So for however many ninjas there were, that Variable would have a count of them. This was followed by the counterpart Trigger:

TRIGGER NAME: Dead Ninja Oh No
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: Yes (important that this is yes!)
WHEN: When (Triggering Actor) kills (Triggering Target Actor):
IF: If Triggering Target Actor is Ninja Clone : Actor Spawner
DO: Add (-1) to Training Room and Testing Arena-NinjasAlive
OTHERWISE: -

So it adds negative one to the (int) Variable each time a ninja dies. So we're still keeping an accurate count of the squirrelly little devils. Good. I also needed to keep track of whether or not the player told the Arena Master to clump the ninjas up or spread them out. So I made another Story Variable:

VARIABLE NAME: NinjasSpread
PROPERTY TYPE: Bool

Then in the Conversation, if the player selected the option to randomly distribute the ninjas, I added an action on that node that set that Boolean Variable to true.
11. The Trouble With Ninjas (Dealing With Special Situations), Part 2
Tracking the state of the ninjas was the easy part. It was getting them to come back correctly that was hard. I ended up making two more Events: Ninjas Come Back and Stupid Ninja Math. And the following Triggers to use them, and make it all work:

TRIGGER NAME: Ninjas Again
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: When a map is loaded and initialized
IF: ( If Training Room and Testing Arena-PortedOut is true )
and
( If Training Room and Testing Arena-Ninjas Alive is greater than (0) )
DO: Send out the Game Event Ninjas Come Back
OTHERWISE: -

TRIGGER NAME: Ninja Loop
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: Yes
WHEN: Ninjas Come Back
IF: If Training Room and Testing Arena-Ninjas Alive is greater than (0)
DO: Activate Ninja Clone : Actor Spawner spawner [Actors / Activate Actor Spawner]
Add (-1) to Training Room and Testing Arena-NinjasAlive
Add (1) to Training Room and Testing Arena-TempNinjasAlive
Send out the Game Event Ninjas Come Back after (0.01) seconds
OTHERWISE: Send out the Game Event Stupid Ninja Math

TRIGGER NAME: Right Number of Ninjas
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: Stupid Ninja Math
IF:
DO: Add Training Room and Testing Arena-TempNinjasAlive to Training Room and Testing Arena-NinjasAlive
Set the (int) Variable Training Room and Testing Arena-TempNinjasAlive to (0)
OTHERWISE: -

TRIGGER NAME: Are Ninjas Spread
ACTIVE AT MAP START: Yes
RETAIN AFTER FIRING: No
WHEN: Stupid Ninja Math
IF: If Training Room and Testing Arena-AreNinjasSpread is true
DO: Warp Teleport ( All Actors with the tag Clone ) to ( Random Point in Arena ) and face No Facing when done
OTHERWISE: -

Now, considering how tough it was to get this working right, I'm fairly proud of those Triggers. Especially Ninja Loop, which took several different incarnations to actually do what it was supposed to. So the first Trigger checks to see if the player is loading into a save game, and whether or not there were ninjas present when they saved. Assuming those things are true, it sends out the first Event, which sets off the Ninja Loop. This Trigger checks to see that there are still ninjas that need to be made, then makes one, and reduces the count of the (int) Variable. Then it re-Triggers itself by sending out the same Event that launched it. (I had to put that tiny delay in there or it wouldn't work. Spawning things slows down the game a lot.) Since it is reducing the (int) Variable each time, eventually it gets down to zero, which means the Trigger has made all of the ninjas needed, and can send out the next Event to run the last two Triggers. It's important that these not happen until after all of the ninjas have been spawned, or else things get screwy.

You'll notice I also added another Story Variable in there, which Ninja Loop is adding to. The reason for this is that while subtracting from the NinjasAlive Variable worked well for producing the right number of ninjas, it would leave that Variable at 0, with some number of actual ninjas actually alive in the Arena. Which means that if the player saved again after this, they'd lose the rest of their ninjas. So I had to figure out how to use the Variable to spawn the right number of ninjas, while also keeping the Variable at the correct number. This was a source of some perplexity for awhile, but eventually I realized that I could simply make another, separate Variable, have the same Trigger increment it up while it incremented the first one down, then later, after the whole process was complete, add the value of the second Variable back to the first, thus making everything accurate again. So that's what the TempNinjasAlive does.

The third Trigger does just that, making sure the NinjasAlive Variable remains accurate, and then it resets the TempNinjasAlive Variable back to zero, so that when the player saves again, it will be ready for use without a wrong number already in there. And the fourth Trigger just spreads out the ninjas again, if they were spread before. And there you have it, everything works. Take that, ninjas!
12. Closing Comments
That's definitely the most difficult thing I've had to figure out so far, but I continue to be amazed at the power and possibilities of the Editor, and how accessible it is. I know nothing about programming, coding, or game modding, this is my first time doing something like this. And even without documention or more than a few days of experience, I find that I'm largely able to just experiment with the Editor's properties and eventually discover how to accomplish whatever it is I'm trying to do. The fact that setting up all of these complex scripted actions only requires the use of a bunch of menus, rather than having to learn some kind of scripting language and type a bunch of complicated stuff out, really pleases me, and makes it possible for an otherwise clueless amateur like myself to create cool stuff for Shadowrun Returns.

That wraps it up for this first version of my guide. Next I'm going to tackle hired Shadowrunners, and the Matrix, and once I've got those all figured out and know how to make them work with my save system, I'll update this guide with more details. Good luck with your creations!
22 Comments
All Thriller, No Philler May 24, 2014 @ 9:24pm 
Antiquated, but I love this. Contains more information than you'd think, and although the game comes with a "Save-Anywhere" feature now built in, this is still cool. Shows just how quickly a game can evolve and how passionately active the community is.
GUY Feb 27, 2014 @ 5:05pm 
Retr, you would make a good dev for this game.
RetroRebel Dec 30, 2013 @ 6:57pm 
Bummer, I thought it'd be some link to a download or probably a cheat code. I bet is a good guide but I'm too lazy to start digging around to understand what's what.
unwaveringresolve Nov 4, 2013 @ 7:43pm 
Much as I admire this game, I have to say that the saving limitations are easily the worst thing about it. Thank you very much for providing this workaround.
Thrawn-a-be Aug 12, 2013 @ 10:11am 
Ahhh, finally it becomes clear, thankyou. I should have clued in when I looked at you mod ( the scenes don't have numbers in the names), so quite literally the lastscene integers are the scenes actual numerical order from top to bottom, no wonder I overlooked it, it's too easy =op. Well I'm going to get some sleep and then I'm going to apply those fixes, thanks again.

The kill and goal stuff sounds a little confusing but only because i have't delved into that part yet. I'm sure once I compare your post against the guide and start implementing them it will become clear.
Firesnakearies  [author] Aug 12, 2013 @ 8:59am 
You could re-use story variables for goals, kills, etc. Because DMS is linear and you can't go back to a previous scene, as soon as you leave a scene, the state of the variables you set for goals or kills no longer matters. So you could just make those generic variables instead of specific to each scene, and re-use the same ones. Goal1, Goal2, Goal3 would just be used by whichever goals the current scene had. With kills, you could tag all of the killable actors in each scene with 1, 2, 3, 4, 5, etc. Then have the on death triggers that set (bool) variables just set generic Actor1Dead, Actor2Dead and so forth which are based on "actor with the tag 1" rather than the name of a specific actor.
Firesnakearies  [author] Aug 12, 2013 @ 8:58am 
So, each scene needs to have a trigger that, on map load, sets the LastScene (int) variable to 1, 2, 3, 4, etc. Just make it 1 for the first scene, and raise the number by one for each additional scene. DMS has like 26 scenes so that would be 26 for the final scene.

Then, the conversation has to have a prerequisite for selecting each link that has a "save the game" line. Each prerequisite needs to say if LastScene is equal to 1, 2, 3, 4, etc. So the line that is going to re-load the first scene needs a prereq of LastScene equalling 1. When someone loads into that scene, LastScene will be set to 1 by the trigger, and thus the prereq for that conversation line will be met. The prereq for the other lines won't be met, because LastScene can only equal one number at a time, so it won't equal 2 or 3 or 26. So those lines should be closed off.
Thrawn-a-be Aug 12, 2013 @ 7:54am 
Well.... not so good. I'm stuck on the conversation thing because the multiple options still pop up. I have all of the triggers set up in each scene for the one click save functionality ( a lot of copy, paste, then manually adjust accordingly), but until I get the conversation working properly I'm not going ahead with the other aspects ( kills, goals, etc).

I think I see what your getting at, but could you give me an example as to what story variables could be re-used?
Firesnakearies  [author] Aug 12, 2013 @ 4:54am 
What I was thinking for something like DMS, where you only visit each actual scene once, then never return to it, you could re-use a lot of Story Variables from scene to scene. They wouldn't have to be kept at a certain value based on the previous scene.

How's your effort to save-anywhere DMS going? That's quite an ambitious undertaking. I was planning to do it myself, after I finished implementing everything into my Arena mod that I wanted. Have you got some scenes done already?
Thrawn-a-be Aug 12, 2013 @ 2:00am 
One thing I've noticed with DMS is that a lot of scenes in different chapters use the same integers, the most common being 1 and 2. Could this cause problems? I've thought about renumbering the scenes, but I've noticed that while you can easily set double digit integers in triggers and variables, double digit scene numbers don't sort numerically, could this also cause problems?