I always kind of had in mind the whole "stringing the scenarios together" concept for campaigns. It certainly is the most "doable" as you said. The load scenario function is pretty neat, but it's a bit limited since you cannot save games in scenarios (well, you can't save games at all...), nor can you split the campaign into multiple scenarios (well, technically you could, but there's no way to restrict users to certain scenarios, then). Text wall begins here:
My concept on how campaigns would work:Firstly, scenarios would need a folder structure like GAE, that is, an extra folder to "categorize" the scenarios. This means the scenarios are kept in
scenarios/[category name]/[scenario name]. The advantage of this is that it cleanly groups campaigns (necessary when we have branching campaigns, etc) and would give a good location to store "global variables" that could be passed from one scenario of a campaign to another (eg, if a character dies in part 1 of a campaign, we could use a global variable to keep them dead for part 2).
[big]
The XML tags[/big]
<unlocks>
<unlock name="scenario_part1" />
</unlocks>
The
unlock tag would be used to list the names of scenarios to unlock after the game is considered a success (that is, the player was set as winner AND the endGame() function was called - whether or not the player ended the game). Naturally, only "unlocked" scenarios are even displayed when choosing scenarios (the rest would be hidden as though they didn't exist, we don't even need the spoiler of knowing their title and whatnot visible for those who aren't "there yet"). Of course, you can unlock more than one scenario at a time. So if we have a choice of either attacking the foe head on or going around to try and ambush them, those can be two different scenarios available to the player. More on how scenarios are locked and unlocked later.
<locks>
<lock name="scenario_part1" />
</locks>
And of course, the opposite of
unlock,
lock makes a scenario once more, well, locked. As mentioned above, locked scenarios don't appear in the list of scenarios. This would be used so when we beat part 3, it could unlock part 4 and lock part 3 up (after all, we just beat it). And like
unlock, it can specify multiple scenarios, so if we decided to attack the foe head on and proceeded to the next scenario, we'd lock both of the two branches we just went past - attacking head on and ambushing. More on how scenarios are locked and unlocked later.
<lock-status value="locked" />
Nowhere in our extra tags do we actually try and specify a scenario as part of a campaign. Instead, we simply present the player with the available unlocked scenarios in the current category folder (each campaign should have its own category, and regular stand-alone scenarios would have one as well).
lock-status is the key to initiating that "campaign look". The
value attribute can be either
locked or
unlocked, and would set the state accordingly. Thus, when we distribute the campaign to players, generally the first scenario in the campaign would be unlocked and the rest would be locked. Effectively, they see but one option: to start the first scenario.
[big]
The settings XML[/big]
Now, we've been locking and unlocking files a bit so far, so it raises the question "how is a scenario considered to be locked or unlocked?". Obviously, we don't want to edit the scenario itself, but instead, we have a "settings" XML that would store global variables and the lock status of scenarios (as declared by the other scenarios in the campaign). Let's take a look at an example:
In part 1, we finish the scenario and move to part 2. We'll assume that part 1 is meant to be always unlocked (so the player can start from the beginning again at any time), thus when we finish part 1, we unlock part 2, but don't lock anything up. MegaGlest would change the settings XML to state that part 2 should now have the unlock status of "unlocked" (the XML of the actual scenario has the lock-status of "locked", but the settings XML takes priority). When we finish part 2, we'll lock part 2 and unlock part 3. So MegaGlest again changes the settings XML, this time to state that part 2 is "locked" and part 3 is "unlocked".
This settings XML would be generated automatically by MegaGlest, and would generally not be modified by users, except for testing purposes and "cheating". If it doesn't exist already (modders shouldn't distribute it), MegaGlest would create it (as it does with the INIs and logs). The sample syntax for the settings XML would be:
<scenario-settings>
<lock-statuses>
<lock-status scenario="scenario_part1" value="locked" />
<lock-status scenario="scenario_part2" value="unlocked" />
</lock-statuses>
<variables>
<variable name="survivingUnitCount" value="15" />
<variable name="kingUnitDead" value="true" />
</variables>
</scenario-settings>
Again, this XML would only be used by the engine as a means of "saving" basic data from scenarios (in the future, it could even be expanded to saving games, etc).
lock-statuses list the lock status of each scenario (of which
scenario is the name of the scenario and
value is either "locked" or "unlocked"). Lock statues specified here "overwrite" the lock status specified in the scenario itself.
Global variables are the other requirement for successful campaigns (more on them later), and would be stored by the variable name and its value (Lua doesn't have to declare the type of variable (eg, int, string), simplifying the process).
[big]
Global variables[/big]
In addition to locking and unlocking scenarios, the other new feature necessary for campaigns would be a method of passing information from one scenario to the next, global variables. These would be done with Lua
tables (basically arrays), using the table "Global". So to store some piece of information as a global variable of the name "myVar", we'd use
Global["myVar"]. MegaGlest would load all of these global variables from the settings XML (if it exists) when it starts a scenario, and saves them to the XML when the scenario is complete.
So if in part 1, I create a global variable, "unitCountPart1", to store my unit count at the end of the scenario (
Global["unitCountPart1"] = unitCount(faction)), it would be saved to the settings XML (
<variable name="unitCountPart1" value="#" />), and in the next scenario, it would be loaded automatically so I can simply use
Global["unitCountPart1"] to get the number I stored. Of course, it could also be used with strings, Boolean values, etc.
The use of global variables would be another reason why all campaigns would need their own category folder: to prevent one campaign from using the same variable name as another, which could clash.
[big]
Pseudo-example[/big]
In our three stage scenario, the player's is attacked and must fend off invaders, then launch a counter attack on the foe. In part 1, the player works solely to defend their base from the waves of attacking foes. In part 2 they can either attack the foe head on or take a longer walk through forests and past marauding crews to reach the heart of the enemy, and in part three, the player launches his final attack on the evil villain of DOOM. Part 2 is actually two different scenarios, with different maps and scripts, but both proceed to the same part 3 scenario.
Initially, part 1 has the lock-status of "unlocked", while part 2a, part 2b, and part 3 are all "locked". When we win the rather simple part 1, we unlock both part 2a and part 2b. Part 1 will remain unlocked if the player wishes to start over (though the modder could lock it if they wanted). After finishing either part 2a or part 2b, both part 2a and part 2b are locked, and part 3 is unlocked (in other words, the prerequisite for part 3 is either part 2a OR part 2b). After finishing part 3, it locks itself, since the player has completed the campaign. In this example, part 1 never locked, but if the modder did lock it, then part 3 would have to unlock it again).
[big]
Optional functions[/big]
These functions would be nice-to-haves, that could enhance this system of campaigns, but would not be necessary.
<description value="langstring" />
Scenarios, campaign or standalone, would greatly benefit from having a description on the scenario menu. This tag specifies a language string which contains the description. If possible, a scrollbar could be used for longer descriptions.
getPlayerInput(message, var)
This Lua function would open a box displaying a message (which would use the language string named by the message attribute) and have a text input box at the bottom, requiring the user to enter a value, which would be stored as a variable of the name "var"). For example, you could use
getPlayerInput(hello, Global["name"]) to display the message named "hello" (which might ask the user for their name), which would be stored in the global variable "name". As a result, you'd be able to call the player by their entered name in any scenario in the campaign.
<scenario-failed>
<unlocks>
<unlock name="scenario_part1a" />
</unlocks>
<locks>
<lock name="scenario_part1" />
</locks>
</scenario-failed>
An extension to unlocking and locking scenarios, the
scenario-failed locks and unlocks scenarios when the user fails their current scenario. So if the user fails to take the castle, they could lock that scenario and unlock an alternative scenario where they must ambush an incoming caravan, etc. The syntax is the same as the
locks and
unlocks functions, but are only called when the scenario is failed, whereas
locks and
unlocks are used when the scenario is a success. Failing a scenario is defined as NOT being set as winner by the time the
endGame() function is called (whether or not the player chooses to end the game). Quitting the game without the
endGame() function being called would not count as failing the scenario.
[big]
Interesting concepts[/big]
A list of just how much you could do with such a simple system.
- Use a global variable to store the number of units alive in one part, and pass that on to the next part, so your performance in one scenario affects how your adventure continues in the next.
- Use a global variable to store how many times the player fails the scenario, and tweak the difficulty accordingly. This could even be applied to standalone scenarios (on the side note that the variable should be very unique, to prevent it from clashing with other scenarios).
- It's possible to increase the replay value by using global variables to define the set of enemies created. In your scenario, you could spawn 2 guards and a swordman if a global variable has not been set, then proceed to set a global variable to 1. Next playthrough, the scenario could notice the variable has been set, and instead of 2 guards and a swordman, it could create a horseman and 2 swordmen.