Making Missions: Difference between revisions

From FPArma Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(6 intermediate revisions by the same user not shown)
Line 6: Line 6:
* [https://community.bistudio.com/wiki/Main_Page Bohemia Wiki]
* [https://community.bistudio.com/wiki/Main_Page Bohemia Wiki]


===Types of Missions===
=Foreword=
Sunday
The reason why this mission making tutorial exists is two fold.
- Talk about Repos
- Platoons structure
- Mission guide lines


Other Ops
The first reason lies in the fact that with the changes of the rules that we’ve imposed over the community, particularly the somewhat stringent testing requirements and commentaries, as well as the paradoxical relaxing of standards for sundays, we’ve effectively caused some degree of confusion on what we consider “sunday-appropriate” and what we consider viable to play at all.
  -Weekday Ops
 
-TvTs
The second reason is that I feel like I’ve personally failed with the original mission making seminar. At its heart the advice given was in good faith, but in the long term I feel like it has given poor directions and not explained enough of mission making in depth to rookie mission makers, relying too much on a rudimentary framework and rudimentary practices, some of which are generally frowned upon nowadays. A particularly big problem was the lack of optimization and an overreliance on zeusing, instead of relying on tougher automation of enemies.
 
By learning the chapters outlined on this wiki page you will master the skills required to craft a mission that can by all accounts be vetted for the Sunday prime-time slot, or really for any other time slot that you find appropriate or your heart desires. Not only will you create one through these guidelines, but in doing so we will add another mission to the lineup for future play.
 
=Development environment setup=
For the purposes of optimal mission making you will need to install a code editor or two. I personally recommend installing:
Visual Studio Code w/ SQF Language & SQF Wiki extensions https://code.visualstudio.com/
Further recommended addons for VSCode are:
https://marketplace.visualstudio.com/items?itemName=Armitxes.sqf
 
If you are scared by Visual Studio Code you can opt in to use the far more rudimentary Notepad++
 
After this, you should set this as your launch parameters in Swifty or launcher of choice:
-noSplash -skipIntro -noPause -showScriptErrors
 
The ShowScriptErrors flag is essential for any work on mission making. More on that later.
 
Ensure that you are running in a way that you can easily access the code editors, e.g. windowed or fullscreen windowed. You may also want to turn your settings down to save on performance.
 
For the power user, you may also download Arma 3 Tools from Steam for tools like Texview, which let you generate .paa images that can be put in the game.
 
Finally, disable optional / client side mods. This is so we don’t need to mess with removing dependencies our mission file may have assigned further down the line.
 
With that being said, if all the software is installed, you’re good to go.
 
=Mission Setup=
==Eden Editor Basics==
Click on the editor and load up a map of your choice. It can be any map you’d wish, really. And then AHHH! It’s so scary, my camera is hovering and what is going on?
 
Don’t worry, let's go through this really quickly. First of all, WASD moves you around. Shift makes you go fast. Right click pans the camera.
 
Top left covers everything you’ll need. You’ve got:
* Scenario tab which you use to save load and export the mission.
* Edit tab, which consolidates the transformation widgets, grid options, object snapping, and asset categories
* View tab, which toggles UI, map textures, night vision, foliage, labels, and night lamp
* Tools, which is scary and has the debug console, func viewer, config viewer, and camera
* Settings, which is just the settings menu really
* Play, which starts your mission, and
* Help, which has useful links to the BI wiki.
 
On the right side, you have the context panel listing a bunch of units. These can be shifted with the function keys or by clicking on them.
 
Now, pick a side, pick a soldier, and plop him down. Great, you can see him and his collision box, but more importantly… You see his class name. Every object in the game has a class name. You use these while programming. Case sensitive, so don’t mess that up. You can also copy the object's position and even class name in the following menu.
 
Right click on our guy and you get the pop up menu. Quite a lot of buttons, but you should only focus on a couple.
* Connect links a guy to a trigger, module, or group.
* Find in Config directs you to his config class.
* Edit loadout opens the ACE / vanilla arsenals.
* Attributes opens the attribute menu.
* Log is for copying location or class name directly to clipboard.
 
Open the attribute menu, and hey, you’ve got some pretty good stuff there. You’re going to be staring at this bad boy for the better part of a day.
 
Let’s cover some of its contents:
Variable name is the unique name it has assigned
Init field runs code when object is loaded at start
Role assigns the unit status and name
Special states set its dynsim status, as well as if it is simulated or visible, or damage enabled.
ACE options determine special values, like if the unit is an engineer, a medic, wears handcuffs, etc. You should research these values before putting them to use.
 
The other properties you do not need to particularly pay attention to.
 
Select our guy, set his variable as  gameMaster and name him “Zeus”.  Add a game master module from the F5 (module) menu,  set owner as gameMaster. Select “All addons (including unofficial ones)” else you will not see any ZEN or mod modules. Repeat this with #adminLogged and #adminVoted.
 
== Bohemia Wiki ==
As stated earlier, the Bohemia Wiki ( https://community.bistudio.com/wiki/Main_Page ) is the most important resource that you will have to utilize throughout all your time spent mission making, modding, and developing content for Arma 3. There is nothing else that gives you such an expansive array of understanding as this wiki. Besides the useful links accessible via homepage, you can also use the search bar to navigate everything you need. One warning tho, this wiki sometimes has bogus information in it, its content populated by mostly players which do not have access to Arma’s engine.
 
Some useful BI Wiki URLs:
https://community.bistudio.com/wiki/Category:Scripting_Commands_by_Functionality
https://community.bistudio.com/wiki/Description.ext
https://community.bistudio.com/wiki/Multiplayer_Scripting
https://community.bistudio.com/wiki/Event_Scripts
https://community.bistudio.com/wiki/Code_Optimisation
 
==Mission Configuration==
With your Zeus unit now placed down in the mission, you can freely press ctrl + S to save and thus generate a new mission folder in your working directory. In most cases, the mission directory will be in your other profiles folder, something like this:
  C:\Users\User\Documents\Arma 3\missions
 
Or if you have created a new profile in the main menu:
  C:\Users\User\Documents\Arma 3 - Other Profiles\Name\missions
 
Open that sucker up and open your mission folder. Inside, you’ll find a lone file, mission.sqm.
 
Now, mission.sqm contains all the data you generate in the editor, but we do not want to put all data inside the editor. Things like loadouts, objective logic, spawners, and most important of all, mission configuration, should be tweaked and set inside mission files instead.
Why don’t we want the mission configuration in the sqm? That one is simple, if you intend to make another mission, you can copy paste those configuration files just easily over, no need to go through the whole configuration part in the editor again.
 
The core vanilla configuration parameters are set inside description.ext. Create a new file called that in the same location as mission.sqm.
 
While you can see the full list of things you can set in description.ext on the BI wiki, let’s focus on the most rudimentary settings instead:
 
  //heading
  class Header {
    gameType = Coop;
    minPlayers = 1;
    maxPlayers = 99;
  };
  disabledAI = 1;
   
  //title params
  onLoadName = "Mission loading name";
  onLoadMission= "Mission loading description";
  briefingName = "Briefing";
  author = "Croguy";
 
The header (which is optional but is OK to put in there because the server will complain otherwise) sets your game type and player count permitted. disabledAI is a flag which forces AI playable slots to disappear during multiplayer. 0 is disabled and default, 1 is enabled.
 
Title parameters are used to set your mission name and loading screen information, as well as your authorship.
 
While these settings are really the most basic values you need, there’s also some others that you should set in as default values in description.ext. They are listed below, add them to your description.ext.
 
  //MISSION SETTINGS
  respawnTemplates[] = {"Base"};
  respawnDelay = 2;
  respawnDialog = 0;
  respawnOnStart = 0;
  respawn = "BASE";
  joinUnassigned = 1;
 
  //DEBUG
  enableDebugConsole = 1;
  enableTargetDebug = 1;
  allowFunctionsLog = 0;
 
  //CLEANUP
  corpseManagerMode = 1;
  corpseLimit = 20;
  corpseRemovalMinTime = 300;
  corpseRemovalMaxTime = 600;
 
  wreckManagerMode = 1;
  wreckLimit = 10;
  wreckRemovalMinTime = 300;
  wreckRemovalMaxTime = 1200;
 
You can read more about them and their importance on the description.ext page. Another important thing that description.ext allows is the possibility to add new classes. More on that later.
 
Keep description.ext in your mind throughout this tutorial, as crucial data will be added to it later.
 
Finally, open up your systems tab and add a Headless Client. Give it a unique variable name HC, and make it playable. This will allow our server’s Headless Client to get into the slot and manage any AI behavior it is required to do. More on that also later.
 
==CBA Settings==
So, let's consider the following: description.ext allows you to change the base settings. What about CBA? To put it simply, CBA settings (mostly known as “Addon options”)  allow us to set custom parameters for a ton of user mods, which let’s us tweak fine functionalities within a mission. Specifically, ACE uses it for just about everything, DUI uses it, EM uses it, your mom and dog use it, etc. You can read more about CBA settings on their github page. The world is your oyster with configuring these settings, so let’s make good use of them.
 
Create a new file called cba_settings.sqf in the same location as mission.sqm and description.ext. Great, we can start adding our customized settings… but, wait, what settings do we have? You can find out these settings through two ways. Either open up eden, open up addon configuration, and hover over the labels of individual settings for whatever mod you’re using, or look them up in their respective mod repositories. For example, ACE documents some of their features and settings on the wiki page.
You can also export the settings from the Addon settings menu and selectively pick the settings out of that.
Make sure to not hit save on the CBA settings menu, else all that info will be stored within the mission.sqm and it becomes a nightmare to figure out what you accidentally set.
 
So, add the settings in, start the mission, and… some of the settings aren’t changed properly. What gives? Well, as it turns out, we have our own server settings to account for. FPA has its own listing of forced and hard-forced CBA settings which can be found on the group’s github page.
In general just add a force in front of your settings, as it will overwrite the clients settings and in mission set settings set in your mission.sqm. If you see force force in the FPA settings that means that these settings are hard enforced by the server and cannot be overwritten.
 
So, let's assume that we want to force the server to accept our mission settings. To put it relatively simply, you have to populate your cba_settings file like this:
  force acex_headless_enabled = false;
  force ace_medical_fatalDamageSource = 0;
  force ace_medical_playerDamageThreshold = 1;
  force ace_medical_statemachine_fatalInjuriesPlayer = 0;
  force ace_medical_AIDamageThreshold = 1;
  force ace_map_BFT_Enabled = false;
  force ace_medical_fractures = 1;
  force ace_medical_limping = 1;
 
Now we’ve disabled ACEX headless auto transfer, enabled lethal mode, set all resistances to 1, disabled the cursor grid coordinates, and enabled bone fractures and limping.
 
== Init files & Locality ==
There are 3 files which will auto run if present inside of the same folder as mission.sqm on every mission start. They are split up between who runs them. In SP they all run on your machine, but in MP it will only run 2 files per machine. Sometimes it is also good to know in which order these script files are being executed. So the following sub chapters are ordered in the execution order.
 
===Init.sqf===
This script runs for everyone who is loading into the mission. This means the server and all players will run this file. The player command word might not be defined just yet, so do not use it here.
 
===InitPlayerLocal.sqf===
Only runs for clients (and server if hosted as listen server). Player command word will be defined here, so it is safe to use. Headless client will also execute this file! Make sure to not run things that need an interface on it. You can simply make the HC exit the script by adding
  if !(hasInterface) exitWith {};
at the top of the file. Remember this for later.
 
===InitServer.sqf===
Only run by the machine that is hosting the mission, so as the name implies, the server. Do not use the player command here!
 
===Locality===
If you are confused about why these have to be separate, let’s have a quick chat about locality.
 
When a command runs in Arma 3 it will have a different activation and effect locality. The effect of these commands and their propagation can be determined by checking the Multiplayer Scripting page on the BI wiki. But, for a quick example, assume the following:
 
You have a script using the createVehicle command, and the command is called inside init.sqf. Because the effect of this command is global it influences all devices globally when it runs. Thus, if you have 20 players and the server loading into the mission that same command will be called 21 (or 22, depending if you have an HC) times and more for any join-in-progress players. In this example createVehicle will create some vehicle for all machines, most likely resulting in a firework of explosions as the vehicles are all spawned on the same location.
 
Equally so, you can revert globally executed code to make it run local to only the device it concerns. An example of this in practice is the FP safezone trigger.
What might confuse you additionally is the fact that executing particular code will not necessarily have an equal effect. Some commands may only affect devices that they are local to, but not necessarily propagate these changes to other devices.
 
To make such behavior of local and global scripts fool-proof, consult the client-server target booleans in the BI wiki, and wrap any code that might cause trouble in an if statement. Additionally, take note of where you execute code and the wiki’s locality flags on commands.
 
=Actually Making The Mission=
==Planning The Mission==
So, with the foundation out of the way, now comes the time to plan out a mission for your game. Missions in FPA are typically unique, movie-like experiences, with elaborate backgrounds and complex events which play out in a non-linear fashion, and…
 
Stop right there. We’re making our first mission here. We just want something quick and dirty where we can all hang out together, shoot some e-soldiers, encounter an unexpected turn of events, and pull out with a good time and all. There’s also an advantage to missions that are small in scope: they’re easy to make, easy to test, easy to balance, and easy on the system. And, you can make them difficult, without straining the players. Taking that into consideration, we’ll make something simple: a mission where the players have to clear an area, destroy a target, and exfiltrate to a location, while enemies attempt to counter-attack them.
 
So, in this example, I’ll load up Virolahti, and pick Ojala and Lansikyla as my AO. We’re keeping it simple: the players are Finnish special forces which have to clear the two villages of enemies which will act as one task, and also have a task to search and destroy a SAM site which has been stored and concealed in their vicinity. Once both tasks are completed, the players have to get out of dodge by exfiltrating North to the small hamlet of Onnela.
Open up the assets menu, open the markers tab, and place some down to signify these objectives in a way that makes sense. The map is, for all intents and purposes, the most vital part of any mission: the most players will look at it, the best information can be discerned for it. And, as an added bonus, it lets you plan out the mission.
 
But, sometimes, for convenience’s sake, you want to plan out that little bit more extra in a way that’s convenient to the mission maker. So, what we’re going to is, we’re going to add some comments sections that are only visible to the mission maker in the editor. Right click on the map, and plop down comments that allow you to plot out what you want where, in fine detail.
 
And presto, you have a mission and a general scope of the operation.
 
==Triggers and Logic==
So, with the foundation out of the way, now comes the question of how the hell do you make some change happen in the game, such as loading enemies or completing tasks?
 
The easiest way to organize that is to set up triggers. Open up the assets context menu to the right, and press F3 or open triggers. You’ll get 4 options, pick whatever you deem appropriate. Triggers can have as big of an activation area as you’d like, or no area at all, and you can manipulate this with either the transformation tab or the transformation widget.
 
Add the trigger into your area. Click on it and you’ll see key attributes:
Variable name assigns it a unique name, shape determines its area, type is regular, guarded, or waypoint skipper (ignore the other ones), activation determines who triggers the activation, type determines how they trigger it, repeatable determines if it can switch back to deactivated, and server only determines if it is only runs on the server.
 
It also has three code fields: condition, activation, and deactivation. Condition uses this to state that it is using its properties to determine their state, but you can really put any boolean expression in there. Activation is the code that is executed when you activate the trigger.
 
So, for experiment’s sake, we’ll be using three triggers for objectives: taskTriggerEnemiesCleared, which will fire when all OPFOR in the trigger are dead, taskTriggerSamDestroyed, which will fire when the SAM is destroyed, and
taskTriggerExfil, which will fire when the other two are completed AND a player reaches the exfil zone.
The first one is very rudimentary, you can just set it up by switching on server only execution, setting the activation to OPFOR, and setting the type to NOT PRESENT. By default, this trigger should fire the moment you run the mission as is - spawn a random opfor soldier, and it won’t fire.
 
The second one is a bit trickier. First, create the target that must be destroyed. In our case, it’s a SAM system. Open up its attribute menu, and assign it a variable name such as samTarget. Now, instead of using the standard condition field, remove the this statement and replace it with !alive samTarget. Now the trigger will activate only when samTarget is destroyed. Interestingly enough, this trigger can have a size of 0, as it does not check an area.
 
Alright, what about taskTriggerExfil? If we just use the standard activation, it will fire even if the prior two objectives are not completed. And we don’t want to have our mission end alert fire when we’re still in progress - you have to consider all edge cases.
So, we’re going to set the activation values similar to the first one: activation ANY PLAYER, type PRESENT. condition this && triggerActivated taskTriggerEnemiesCleared && triggerActivated taskTriggerSamDestroyed.
Now the trigger will activate only when a player reaches the area, and when the other two triggers are active.
 
And here we go, now we have all the essential objective triggers for the mission set up.
 
Consider the multiple ways you can change trigger behavior to account for player behavior, sequence breaking, and, most importantly, calling of code execution.
 
==Debugging, Errors, and Logs==
So, an important thing to understand is that during development you’ll see a ton of errors and have to fix a ton of errors. A matter of fact, fixing errors is going to be the majority of your work while making missions. With that in mind it’s best to develop an understanding of what kinds of errors we need to account for while making missions.
 
So, the first and the most common ones are syntax errors. These are very broad and can happen for a lot of reasons, but it always boils down to something being incorrectly written. They are also the most easily noticed by debugging tools and both the report log which you can access in your arma 3’s appdata records as well as the script debugger. The report logs will be RPT files named after the Arma binary and time you started the game. Eden will also stop you from saving code which has blatant syntax errors in it.
 
An error similar to syntax errors are invalid type errors. Specific commands and functions may often require specific types of parameters to be declared. Numbers, boolean, string, text, objects, etc. These parameters cannot just be supplanted by a different type, so the code will stop executing and throw an error. You can simply fix this by reworking your code so it does not use incorrect types wherever necessary.
 
Finally, there is also the worst kind of error you want to face: exceptions. These are by and large the most insidious errors, as they are not created from errors in the literal sense, but instead are caused by deeper mistakes made by the programmer while setting them up. In some cases, these types of errors will not even throw a script error, and will make a piece of code look like it is malfunctioning. The only way to correct these is to extensively handle unwanted parameters, use system chat alerts to notify players and mission makers about errors, and so forth.
 
Always refer to using your report logs while analyzing errors. It is not unusual that there is extensive documentation about an error, nor that a person has not already ran into it. Thus, the easiest way to handle the fixing of an error is to check your report logs, run the code in a controlled environment, and ideally have someone else look at it.
 
==FPA Functions - Respawns, safe zones & caching==
Now, the triggers are very good, but on their own they do very little: we want them to execute some complex thing for us, like management of enemies, respawning of players, and most important of all, preventing the players from slipping their finger during mission prep.
 
Thankfully Cuel & a good chunk of the modding team has generously provided us with a lot of boilerplate functions, whose purpose is to cover some of these things.
 
First, we’ll look at respawning. Simple enough. The first thing you need to do is open up init.sqf and initialize the JRM framework using the following line, where the number counts respawns:
  [0] call fpa_jrm_fnc_init;
 
Now that the framework has been initialized, place an empty marker on the map where the player staging area is and call it “respawn_west”. Then, inside the objective triggers, you can place a block of code that looks something like this:
  ["respawn_west"] remoteExec ["fpa_jrm_fnc_forceRespawn"];
 
Where “respawn_west” can be any marker name of a marker you’ve placed down. Arma 3 uses several default marker names(which you still need to place down in the editor), but ideally what you want to do for respawns which move their positions is to have multiple markers and simply call the respawns to new marker names. So, if we want to respawn players after the target is destroyed, we’d have a “respawn_targetDestroyed” marker, which is then called in the taskTriggerSamDestroyed trigger.
 
Safe zones are more tricky. You need to create a new trigger and set its values as follows:
 
Caching, meanwhile, is a unique little thing that serves to freeze and unfreeze temporarily spawned objects in the editor. To an extent it sort of functions like the boilerplate dynamic simulation, but in a way that gives the mission maker more direct control over what is cached and rendered for the players.
 
That being said, caching enemy units with FPA caching is prone to severe performance reduction if you cache hundreds of AI, They might be not simulated and hidden, yet they still occupy ram and cpu of the server, which needs to sync all of that to each player. Use it sparingly. In the case of our mission, we’ll use it for 2 things: caching 3 foot patrol teams around the objective area, and a truck that we’ve spawned next to the MacGuffin objective.
 
First, open up the compositions tab and spawn a group of enemy soldiers. In my case, it will be an MSV infantry section. For ease of generating patrol waypoints, you can simply open the F5 Systems assets tab, spawn a CBA Patrol module, sync it to the group leader, then tweak accordingly. Select their group icon, and enable Delete when empty and Headless blacklist. Then select their leader and paste this block of code into his object init field:
  [group this, "footPatrolNorth"] call fpa_ai_fnc_cache;
 
Then, create a trigger and stretch it broadly around the area where they will be active. Set their activation as ANY PLAYER, PRESENT, REPEATABLE, SERVER ONLY. In the activation field, add the following value:
  ["footPatrolNorth"] call fpa_ai_fnc_unCache;
And in deactivation:
  ["footPatrolNorth"] call fpa_ai_fnc_cache;
 
Copy and paste these units and triggers 2 more times and simply rename footPatrolNorth to whatever you see fit. Now you will have small groups of enemies whose simulation disables relative to the nearby presence of players.
 
Remember that taskTriggerEnemiesCleared which keeps activating? You can fool-proof it by spawning another group inside the trigger and caching them with a trigger of their own. That way it won’t fire before enemies are cleared.
 
This can also extend to individual units or empty vehicles by simply using this instead:
  [this, "emptyVehicleCacheExample"] call fpa_ai_fnc_cache;
 
However, for more optimal use, resort to caching interactive and simulated static or unmanned objects with Dynamic Simulation.
 
==Custom Functions - Enemy Spawner==
At this point you might wonder yourself - well, this is all well and good, we have our basic spawning setup with Dynsym and FP Cache, and there's an entire FP Spawn function available in our modpack that can be used anywhere, anytime, and is significantly more aggressive than FP Cache in optimization.
 
Well, that's great and all. You can absolutely utilize these to construct a functional mission. However, we must also consider how to consistently execute code over and over, where necessary, and where it is not provided by frameworks.
 
When we have a lot of samey code routines, some which are too complex to cram into the activation field of a trigger, we can consolidate said routines into function files instead. The function system allows us to make more practical, plugin-like coding for our missions.
 
this begs the question of what do we do when we want to run a bunch of really complicated stuff like loadout manipulation, spawning, auto-logistics, etc?
 
With that in mind, we’ll focus on something we need a lot in this mission: an enemy group spawner.
===Class Declaration===
We’ll set up our function's environment first. Open your description.ext file, and add the following class into it:
  //Functions
  class CfgFunctions {
    class FP {
      tag = "FP";
      class functions {
        file = "functions";
      class spawnGroup;
      };
    };
  };
 
Then create a folder called “functions” and create a new file in it called fn_spawnGroup.sqf.
 
This is the most barebones way of setting up a custom function. You’ve declared it in description.ext, and now you have a file which it calls whenever you use FP_fnc_spawnGroup inside mission code.
 
Next, we’ll need to figure out what our enemy spawner needs to do. Let’s outline it:
The spawner must accept custom parameters for location and group.
It must spawn groups only if it is called on the server or the headless client.
It must correctly orient the group and set their state to a fixed or flexible value if desired.
Optionally, it should accept a type of waypoint and destination location.
 
===Function Parameters===
First, we want to define the parameters for our function. Parameters are the input values of a function, if you’ve ever done math and had to dabble with functions it’s exactly the same but you get to make all of it so you keep it simple and feel way smarter as players.
So, what do we need for our parameters? It’s quite simple:
* A spawn marker.
* Our group’s class path.
* Their intended behavior / combat state.
* The squad formation.
* A destination marker.
* The type of the waypoint they’re heading towards.
 
The markers are pretty straightforward, we’ll call them _spawnMarker & _waypointMarker respectively. Why the underscore? It helps us separate private scope variables from global scope variables. If you’re using values that are internal to a block of code, always prefix them with an underscore.
 
We end up with something that looks like this.
  params [
    "_spawnMarker",
    "_waypointMarker"
  ];
 
_waypointType will act as our parameter for the type we assign to the waypoint. If you look at the BI wiki waypoint commands you’ll notice that you have to assign a waypoint to a group and then assign a type to a said waypoint. But, let's consider something. What if I just don’t wanna use anything? Well, on the params page, it offers us a special way of setting up each parameter we list in our array block:
 
[variableName, defaultValue, expectedDataTypes]
 
So what we have here is a block where we realize we can list our parameter name, what we want the default value to be if we don’t list the parameter, and what we want the accepted data type to be. So, if we’re doing it for our waypoint type, we can set the values as follows:
  ["_waypointType", "MOVE", [""]]
 
And now fully convert our params statement so it looks like this:
  params [
    ["_spawnMarker", "", [""]],
    ["_waypointMarker", "", [""]],
    ["_waypointType", "MOVE", [""]]
  ];
 
 
OK, so that’s our three required values with one value which we assigned as default. What about spawning groups? Well, if we look on the BI wiki, there is an official function which spawns a group for us, called BIS_fnc_spawnGroup. We see it accepts a variety of values to allow a degree of randomization and flavor to the spawned group. We also see it allows us to use a config path of a group to reference the group. With that in mind, if we open the config viewer and look through cfgGroups, continually clicking on subclasses, we’ll eventually reach a group. The path listed is what serves as a path for us.
First we want to arrange default values so that if we have a scenario where we do not include a unit a default one is picked. For that one we can use the stock CSAT rifle squad. Their class path is ConfigFile >> “East” >> “OPF_F” >> “Infantry” >> “OIA_InfSquad”. These four values will be defined by four string parameters. Only thing that’s left now is the behavior and formation, which if we look at the BI wiki pages we discern that they have clear string inputs for their command, so we can just default them to COMBAT and LINE, and force the AI to use RED as their engagement rule.
 
With all that in mind, our params and default values will look like this:
  params [
    ["_spawnMarker", "", [""]],
    ["_waypointMarker", "", [""]],
    ["_waypointType", "MOVE", [""]],
    ["_side", "East", [""]],
    ["_faction", "OPF_F", [""]],
    ["_category", "Infantry", [""]],
    ["_type", "OIA_InfSquad", [""]],
    ["_behaviour", "COMBAT", [""]],
    ["_formation", "LINE", [""]]
  ];
 
===Make a Function do Stuff===
Now we want to actually make the function do something useful with these parameters. So, let’s focus back on that BIS_fnc_spawnGroup function. If we look at its properties we notice that it has a lot of very nice overrides and optional values.
 
However, we need to secure our function to not fail if we have a spawn marker that doesn’t exist. That’s done by adding the following if statement immediately after the params:
 
  if(_spawnMarker == "") exitWith {};
 
We now can assign the values from our own params statement into the parameters of the function, but this begs the question of how do we make the config path look good and fit into the file? We can’t just stretch the method to the brim. What we’ll do instead is, we’ll create a private variable called _config which we’ll turn into our config path and use our params in the declaration. It’ll look like this:


== Mission Making Basics for 100% Beginners ==
== Mission Making Basics for 100% Beginners ==
Line 28: Line 387:
  -Respawn systems and how to set them up (Time Based, Objective Based, Rolling Respawn)
  -Respawn systems and how to set them up (Time Based, Objective Based, Rolling Respawn)
  -Difficulty balancing
  -Difficulty balancing


===Using the Template===
===Using the Template===

Latest revision as of 11:40, 27 April 2022

Making Missions for FPArma

WORK IN PROGRESS

Overview

Quick Links:

Foreword

The reason why this mission making tutorial exists is two fold.

The first reason lies in the fact that with the changes of the rules that we’ve imposed over the community, particularly the somewhat stringent testing requirements and commentaries, as well as the paradoxical relaxing of standards for sundays, we’ve effectively caused some degree of confusion on what we consider “sunday-appropriate” and what we consider viable to play at all.

The second reason is that I feel like I’ve personally failed with the original mission making seminar. At its heart the advice given was in good faith, but in the long term I feel like it has given poor directions and not explained enough of mission making in depth to rookie mission makers, relying too much on a rudimentary framework and rudimentary practices, some of which are generally frowned upon nowadays. A particularly big problem was the lack of optimization and an overreliance on zeusing, instead of relying on tougher automation of enemies.

By learning the chapters outlined on this wiki page you will master the skills required to craft a mission that can by all accounts be vetted for the Sunday prime-time slot, or really for any other time slot that you find appropriate or your heart desires. Not only will you create one through these guidelines, but in doing so we will add another mission to the lineup for future play.

Development environment setup

For the purposes of optimal mission making you will need to install a code editor or two. I personally recommend installing: Visual Studio Code w/ SQF Language & SQF Wiki extensions https://code.visualstudio.com/ Further recommended addons for VSCode are: https://marketplace.visualstudio.com/items?itemName=Armitxes.sqf

If you are scared by Visual Studio Code you can opt in to use the far more rudimentary Notepad++

After this, you should set this as your launch parameters in Swifty or launcher of choice: -noSplash -skipIntro -noPause -showScriptErrors

The ShowScriptErrors flag is essential for any work on mission making. More on that later.

Ensure that you are running in a way that you can easily access the code editors, e.g. windowed or fullscreen windowed. You may also want to turn your settings down to save on performance.

For the power user, you may also download Arma 3 Tools from Steam for tools like Texview, which let you generate .paa images that can be put in the game.

Finally, disable optional / client side mods. This is so we don’t need to mess with removing dependencies our mission file may have assigned further down the line.

With that being said, if all the software is installed, you’re good to go.

Mission Setup

Eden Editor Basics

Click on the editor and load up a map of your choice. It can be any map you’d wish, really. And then AHHH! It’s so scary, my camera is hovering and what is going on?

Don’t worry, let's go through this really quickly. First of all, WASD moves you around. Shift makes you go fast. Right click pans the camera.

Top left covers everything you’ll need. You’ve got:

  • Scenario tab which you use to save load and export the mission.
  • Edit tab, which consolidates the transformation widgets, grid options, object snapping, and asset categories
  • View tab, which toggles UI, map textures, night vision, foliage, labels, and night lamp
  • Tools, which is scary and has the debug console, func viewer, config viewer, and camera
  • Settings, which is just the settings menu really
  • Play, which starts your mission, and
  • Help, which has useful links to the BI wiki.

On the right side, you have the context panel listing a bunch of units. These can be shifted with the function keys or by clicking on them.

Now, pick a side, pick a soldier, and plop him down. Great, you can see him and his collision box, but more importantly… You see his class name. Every object in the game has a class name. You use these while programming. Case sensitive, so don’t mess that up. You can also copy the object's position and even class name in the following menu.

Right click on our guy and you get the pop up menu. Quite a lot of buttons, but you should only focus on a couple.

  • Connect links a guy to a trigger, module, or group.
  • Find in Config directs you to his config class.
  • Edit loadout opens the ACE / vanilla arsenals.
  • Attributes opens the attribute menu.
  • Log is for copying location or class name directly to clipboard.

Open the attribute menu, and hey, you’ve got some pretty good stuff there. You’re going to be staring at this bad boy for the better part of a day.

Let’s cover some of its contents: Variable name is the unique name it has assigned Init field runs code when object is loaded at start Role assigns the unit status and name Special states set its dynsim status, as well as if it is simulated or visible, or damage enabled. ACE options determine special values, like if the unit is an engineer, a medic, wears handcuffs, etc. You should research these values before putting them to use.

The other properties you do not need to particularly pay attention to.

Select our guy, set his variable as gameMaster and name him “Zeus”. Add a game master module from the F5 (module) menu, set owner as gameMaster. Select “All addons (including unofficial ones)” else you will not see any ZEN or mod modules. Repeat this with #adminLogged and #adminVoted.

Bohemia Wiki

As stated earlier, the Bohemia Wiki ( https://community.bistudio.com/wiki/Main_Page ) is the most important resource that you will have to utilize throughout all your time spent mission making, modding, and developing content for Arma 3. There is nothing else that gives you such an expansive array of understanding as this wiki. Besides the useful links accessible via homepage, you can also use the search bar to navigate everything you need. One warning tho, this wiki sometimes has bogus information in it, its content populated by mostly players which do not have access to Arma’s engine.

Some useful BI Wiki URLs: https://community.bistudio.com/wiki/Category:Scripting_Commands_by_Functionality https://community.bistudio.com/wiki/Description.ext https://community.bistudio.com/wiki/Multiplayer_Scripting https://community.bistudio.com/wiki/Event_Scripts https://community.bistudio.com/wiki/Code_Optimisation

Mission Configuration

With your Zeus unit now placed down in the mission, you can freely press ctrl + S to save and thus generate a new mission folder in your working directory. In most cases, the mission directory will be in your other profiles folder, something like this:

 C:\Users\User\Documents\Arma 3\missions

Or if you have created a new profile in the main menu:

 C:\Users\User\Documents\Arma 3 - Other Profiles\Name\missions

Open that sucker up and open your mission folder. Inside, you’ll find a lone file, mission.sqm.

Now, mission.sqm contains all the data you generate in the editor, but we do not want to put all data inside the editor. Things like loadouts, objective logic, spawners, and most important of all, mission configuration, should be tweaked and set inside mission files instead. Why don’t we want the mission configuration in the sqm? That one is simple, if you intend to make another mission, you can copy paste those configuration files just easily over, no need to go through the whole configuration part in the editor again.

The core vanilla configuration parameters are set inside description.ext. Create a new file called that in the same location as mission.sqm.

While you can see the full list of things you can set in description.ext on the BI wiki, let’s focus on the most rudimentary settings instead:

 //heading
 class Header {
   gameType = Coop;
   minPlayers = 1;
   maxPlayers = 99;
 };

 disabledAI = 1;

 //title params
 onLoadName = "Mission loading name";
 onLoadMission= "Mission loading description";
 briefingName = "Briefing";
 author = "Croguy";

The header (which is optional but is OK to put in there because the server will complain otherwise) sets your game type and player count permitted. disabledAI is a flag which forces AI playable slots to disappear during multiplayer. 0 is disabled and default, 1 is enabled.

Title parameters are used to set your mission name and loading screen information, as well as your authorship.

While these settings are really the most basic values you need, there’s also some others that you should set in as default values in description.ext. They are listed below, add them to your description.ext.

 //MISSION SETTINGS
 respawnTemplates[] = {"Base"};
 respawnDelay = 2;
 respawnDialog = 0;
 respawnOnStart = 0;
 respawn = "BASE";
 joinUnassigned = 1;
  
 //DEBUG
 enableDebugConsole = 1;
 enableTargetDebug = 1;
 allowFunctionsLog = 0;
  
 //CLEANUP
 corpseManagerMode = 1;
 corpseLimit = 20;
 corpseRemovalMinTime = 300;
 corpseRemovalMaxTime = 600;
  
 wreckManagerMode = 1;
 wreckLimit = 10;
 wreckRemovalMinTime = 300;
 wreckRemovalMaxTime = 1200;

You can read more about them and their importance on the description.ext page. Another important thing that description.ext allows is the possibility to add new classes. More on that later.

Keep description.ext in your mind throughout this tutorial, as crucial data will be added to it later.

Finally, open up your systems tab and add a Headless Client. Give it a unique variable name HC, and make it playable. This will allow our server’s Headless Client to get into the slot and manage any AI behavior it is required to do. More on that also later.

CBA Settings

So, let's consider the following: description.ext allows you to change the base settings. What about CBA? To put it simply, CBA settings (mostly known as “Addon options”) allow us to set custom parameters for a ton of user mods, which let’s us tweak fine functionalities within a mission. Specifically, ACE uses it for just about everything, DUI uses it, EM uses it, your mom and dog use it, etc. You can read more about CBA settings on their github page. The world is your oyster with configuring these settings, so let’s make good use of them.

Create a new file called cba_settings.sqf in the same location as mission.sqm and description.ext. Great, we can start adding our customized settings… but, wait, what settings do we have? You can find out these settings through two ways. Either open up eden, open up addon configuration, and hover over the labels of individual settings for whatever mod you’re using, or look them up in their respective mod repositories. For example, ACE documents some of their features and settings on the wiki page. You can also export the settings from the Addon settings menu and selectively pick the settings out of that. Make sure to not hit save on the CBA settings menu, else all that info will be stored within the mission.sqm and it becomes a nightmare to figure out what you accidentally set.

So, add the settings in, start the mission, and… some of the settings aren’t changed properly. What gives? Well, as it turns out, we have our own server settings to account for. FPA has its own listing of forced and hard-forced CBA settings which can be found on the group’s github page. In general just add a force in front of your settings, as it will overwrite the clients settings and in mission set settings set in your mission.sqm. If you see force force in the FPA settings that means that these settings are hard enforced by the server and cannot be overwritten.

So, let's assume that we want to force the server to accept our mission settings. To put it relatively simply, you have to populate your cba_settings file like this:

 force acex_headless_enabled = false;
 force ace_medical_fatalDamageSource = 0;
 force ace_medical_playerDamageThreshold = 1;
 force ace_medical_statemachine_fatalInjuriesPlayer = 0;
 force ace_medical_AIDamageThreshold = 1;
 force ace_map_BFT_Enabled = false;
 force ace_medical_fractures = 1;
 force ace_medical_limping = 1;

Now we’ve disabled ACEX headless auto transfer, enabled lethal mode, set all resistances to 1, disabled the cursor grid coordinates, and enabled bone fractures and limping.

Init files & Locality

There are 3 files which will auto run if present inside of the same folder as mission.sqm on every mission start. They are split up between who runs them. In SP they all run on your machine, but in MP it will only run 2 files per machine. Sometimes it is also good to know in which order these script files are being executed. So the following sub chapters are ordered in the execution order.

Init.sqf

This script runs for everyone who is loading into the mission. This means the server and all players will run this file. The player command word might not be defined just yet, so do not use it here.

InitPlayerLocal.sqf

Only runs for clients (and server if hosted as listen server). Player command word will be defined here, so it is safe to use. Headless client will also execute this file! Make sure to not run things that need an interface on it. You can simply make the HC exit the script by adding

 if !(hasInterface) exitWith {}; 

at the top of the file. Remember this for later.

InitServer.sqf

Only run by the machine that is hosting the mission, so as the name implies, the server. Do not use the player command here!

Locality

If you are confused about why these have to be separate, let’s have a quick chat about locality.

When a command runs in Arma 3 it will have a different activation and effect locality. The effect of these commands and their propagation can be determined by checking the Multiplayer Scripting page on the BI wiki. But, for a quick example, assume the following:

You have a script using the createVehicle command, and the command is called inside init.sqf. Because the effect of this command is global it influences all devices globally when it runs. Thus, if you have 20 players and the server loading into the mission that same command will be called 21 (or 22, depending if you have an HC) times and more for any join-in-progress players. In this example createVehicle will create some vehicle for all machines, most likely resulting in a firework of explosions as the vehicles are all spawned on the same location.

Equally so, you can revert globally executed code to make it run local to only the device it concerns. An example of this in practice is the FP safezone trigger. What might confuse you additionally is the fact that executing particular code will not necessarily have an equal effect. Some commands may only affect devices that they are local to, but not necessarily propagate these changes to other devices.

To make such behavior of local and global scripts fool-proof, consult the client-server target booleans in the BI wiki, and wrap any code that might cause trouble in an if statement. Additionally, take note of where you execute code and the wiki’s locality flags on commands.

Actually Making The Mission

Planning The Mission

So, with the foundation out of the way, now comes the time to plan out a mission for your game. Missions in FPA are typically unique, movie-like experiences, with elaborate backgrounds and complex events which play out in a non-linear fashion, and…

Stop right there. We’re making our first mission here. We just want something quick and dirty where we can all hang out together, shoot some e-soldiers, encounter an unexpected turn of events, and pull out with a good time and all. There’s also an advantage to missions that are small in scope: they’re easy to make, easy to test, easy to balance, and easy on the system. And, you can make them difficult, without straining the players. Taking that into consideration, we’ll make something simple: a mission where the players have to clear an area, destroy a target, and exfiltrate to a location, while enemies attempt to counter-attack them.

So, in this example, I’ll load up Virolahti, and pick Ojala and Lansikyla as my AO. We’re keeping it simple: the players are Finnish special forces which have to clear the two villages of enemies which will act as one task, and also have a task to search and destroy a SAM site which has been stored and concealed in their vicinity. Once both tasks are completed, the players have to get out of dodge by exfiltrating North to the small hamlet of Onnela. Open up the assets menu, open the markers tab, and place some down to signify these objectives in a way that makes sense. The map is, for all intents and purposes, the most vital part of any mission: the most players will look at it, the best information can be discerned for it. And, as an added bonus, it lets you plan out the mission.

But, sometimes, for convenience’s sake, you want to plan out that little bit more extra in a way that’s convenient to the mission maker. So, what we’re going to is, we’re going to add some comments sections that are only visible to the mission maker in the editor. Right click on the map, and plop down comments that allow you to plot out what you want where, in fine detail.

And presto, you have a mission and a general scope of the operation.

Triggers and Logic

So, with the foundation out of the way, now comes the question of how the hell do you make some change happen in the game, such as loading enemies or completing tasks?

The easiest way to organize that is to set up triggers. Open up the assets context menu to the right, and press F3 or open triggers. You’ll get 4 options, pick whatever you deem appropriate. Triggers can have as big of an activation area as you’d like, or no area at all, and you can manipulate this with either the transformation tab or the transformation widget.

Add the trigger into your area. Click on it and you’ll see key attributes: Variable name assigns it a unique name, shape determines its area, type is regular, guarded, or waypoint skipper (ignore the other ones), activation determines who triggers the activation, type determines how they trigger it, repeatable determines if it can switch back to deactivated, and server only determines if it is only runs on the server.

It also has three code fields: condition, activation, and deactivation. Condition uses this to state that it is using its properties to determine their state, but you can really put any boolean expression in there. Activation is the code that is executed when you activate the trigger.

So, for experiment’s sake, we’ll be using three triggers for objectives: taskTriggerEnemiesCleared, which will fire when all OPFOR in the trigger are dead, taskTriggerSamDestroyed, which will fire when the SAM is destroyed, and taskTriggerExfil, which will fire when the other two are completed AND a player reaches the exfil zone. The first one is very rudimentary, you can just set it up by switching on server only execution, setting the activation to OPFOR, and setting the type to NOT PRESENT. By default, this trigger should fire the moment you run the mission as is - spawn a random opfor soldier, and it won’t fire.

The second one is a bit trickier. First, create the target that must be destroyed. In our case, it’s a SAM system. Open up its attribute menu, and assign it a variable name such as samTarget. Now, instead of using the standard condition field, remove the this statement and replace it with !alive samTarget. Now the trigger will activate only when samTarget is destroyed. Interestingly enough, this trigger can have a size of 0, as it does not check an area.

Alright, what about taskTriggerExfil? If we just use the standard activation, it will fire even if the prior two objectives are not completed. And we don’t want to have our mission end alert fire when we’re still in progress - you have to consider all edge cases. So, we’re going to set the activation values similar to the first one: activation ANY PLAYER, type PRESENT. condition this && triggerActivated taskTriggerEnemiesCleared && triggerActivated taskTriggerSamDestroyed. Now the trigger will activate only when a player reaches the area, and when the other two triggers are active.

And here we go, now we have all the essential objective triggers for the mission set up.

Consider the multiple ways you can change trigger behavior to account for player behavior, sequence breaking, and, most importantly, calling of code execution.

Debugging, Errors, and Logs

So, an important thing to understand is that during development you’ll see a ton of errors and have to fix a ton of errors. A matter of fact, fixing errors is going to be the majority of your work while making missions. With that in mind it’s best to develop an understanding of what kinds of errors we need to account for while making missions.

So, the first and the most common ones are syntax errors. These are very broad and can happen for a lot of reasons, but it always boils down to something being incorrectly written. They are also the most easily noticed by debugging tools and both the report log which you can access in your arma 3’s appdata records as well as the script debugger. The report logs will be RPT files named after the Arma binary and time you started the game. Eden will also stop you from saving code which has blatant syntax errors in it.

An error similar to syntax errors are invalid type errors. Specific commands and functions may often require specific types of parameters to be declared. Numbers, boolean, string, text, objects, etc. These parameters cannot just be supplanted by a different type, so the code will stop executing and throw an error. You can simply fix this by reworking your code so it does not use incorrect types wherever necessary.

Finally, there is also the worst kind of error you want to face: exceptions. These are by and large the most insidious errors, as they are not created from errors in the literal sense, but instead are caused by deeper mistakes made by the programmer while setting them up. In some cases, these types of errors will not even throw a script error, and will make a piece of code look like it is malfunctioning. The only way to correct these is to extensively handle unwanted parameters, use system chat alerts to notify players and mission makers about errors, and so forth.

Always refer to using your report logs while analyzing errors. It is not unusual that there is extensive documentation about an error, nor that a person has not already ran into it. Thus, the easiest way to handle the fixing of an error is to check your report logs, run the code in a controlled environment, and ideally have someone else look at it.

FPA Functions - Respawns, safe zones & caching

Now, the triggers are very good, but on their own they do very little: we want them to execute some complex thing for us, like management of enemies, respawning of players, and most important of all, preventing the players from slipping their finger during mission prep.

Thankfully Cuel & a good chunk of the modding team has generously provided us with a lot of boilerplate functions, whose purpose is to cover some of these things.

First, we’ll look at respawning. Simple enough. The first thing you need to do is open up init.sqf and initialize the JRM framework using the following line, where the number counts respawns:

 [0] call fpa_jrm_fnc_init;

Now that the framework has been initialized, place an empty marker on the map where the player staging area is and call it “respawn_west”. Then, inside the objective triggers, you can place a block of code that looks something like this:

 ["respawn_west"] remoteExec ["fpa_jrm_fnc_forceRespawn"];

Where “respawn_west” can be any marker name of a marker you’ve placed down. Arma 3 uses several default marker names(which you still need to place down in the editor), but ideally what you want to do for respawns which move their positions is to have multiple markers and simply call the respawns to new marker names. So, if we want to respawn players after the target is destroyed, we’d have a “respawn_targetDestroyed” marker, which is then called in the taskTriggerSamDestroyed trigger.

Safe zones are more tricky. You need to create a new trigger and set its values as follows:

Caching, meanwhile, is a unique little thing that serves to freeze and unfreeze temporarily spawned objects in the editor. To an extent it sort of functions like the boilerplate dynamic simulation, but in a way that gives the mission maker more direct control over what is cached and rendered for the players.

That being said, caching enemy units with FPA caching is prone to severe performance reduction if you cache hundreds of AI, They might be not simulated and hidden, yet they still occupy ram and cpu of the server, which needs to sync all of that to each player. Use it sparingly. In the case of our mission, we’ll use it for 2 things: caching 3 foot patrol teams around the objective area, and a truck that we’ve spawned next to the MacGuffin objective.

First, open up the compositions tab and spawn a group of enemy soldiers. In my case, it will be an MSV infantry section. For ease of generating patrol waypoints, you can simply open the F5 Systems assets tab, spawn a CBA Patrol module, sync it to the group leader, then tweak accordingly. Select their group icon, and enable Delete when empty and Headless blacklist. Then select their leader and paste this block of code into his object init field:

 [group this, "footPatrolNorth"] call fpa_ai_fnc_cache;

Then, create a trigger and stretch it broadly around the area where they will be active. Set their activation as ANY PLAYER, PRESENT, REPEATABLE, SERVER ONLY. In the activation field, add the following value:

 ["footPatrolNorth"] call fpa_ai_fnc_unCache;

And in deactivation:

 ["footPatrolNorth"] call fpa_ai_fnc_cache;

Copy and paste these units and triggers 2 more times and simply rename footPatrolNorth to whatever you see fit. Now you will have small groups of enemies whose simulation disables relative to the nearby presence of players.

Remember that taskTriggerEnemiesCleared which keeps activating? You can fool-proof it by spawning another group inside the trigger and caching them with a trigger of their own. That way it won’t fire before enemies are cleared.

This can also extend to individual units or empty vehicles by simply using this instead:

 [this, "emptyVehicleCacheExample"] call fpa_ai_fnc_cache;

However, for more optimal use, resort to caching interactive and simulated static or unmanned objects with Dynamic Simulation.

Custom Functions - Enemy Spawner

At this point you might wonder yourself - well, this is all well and good, we have our basic spawning setup with Dynsym and FP Cache, and there's an entire FP Spawn function available in our modpack that can be used anywhere, anytime, and is significantly more aggressive than FP Cache in optimization.

Well, that's great and all. You can absolutely utilize these to construct a functional mission. However, we must also consider how to consistently execute code over and over, where necessary, and where it is not provided by frameworks.

When we have a lot of samey code routines, some which are too complex to cram into the activation field of a trigger, we can consolidate said routines into function files instead. The function system allows us to make more practical, plugin-like coding for our missions.

this begs the question of what do we do when we want to run a bunch of really complicated stuff like loadout manipulation, spawning, auto-logistics, etc?

With that in mind, we’ll focus on something we need a lot in this mission: an enemy group spawner.

Class Declaration

We’ll set up our function's environment first. Open your description.ext file, and add the following class into it:

 //Functions
 class CfgFunctions {
   class FP {
     tag = "FP";
     class functions {
       file = "functions";
     class spawnGroup;
     };
   };
 };

Then create a folder called “functions” and create a new file in it called fn_spawnGroup.sqf.

This is the most barebones way of setting up a custom function. You’ve declared it in description.ext, and now you have a file which it calls whenever you use FP_fnc_spawnGroup inside mission code.

Next, we’ll need to figure out what our enemy spawner needs to do. Let’s outline it: The spawner must accept custom parameters for location and group. It must spawn groups only if it is called on the server or the headless client. It must correctly orient the group and set their state to a fixed or flexible value if desired. Optionally, it should accept a type of waypoint and destination location.

Function Parameters

First, we want to define the parameters for our function. Parameters are the input values of a function, if you’ve ever done math and had to dabble with functions it’s exactly the same but you get to make all of it so you keep it simple and feel way smarter as players. So, what do we need for our parameters? It’s quite simple:

  • A spawn marker.
  • Our group’s class path.
  • Their intended behavior / combat state.
  • The squad formation.
  • A destination marker.
  • The type of the waypoint they’re heading towards.

The markers are pretty straightforward, we’ll call them _spawnMarker & _waypointMarker respectively. Why the underscore? It helps us separate private scope variables from global scope variables. If you’re using values that are internal to a block of code, always prefix them with an underscore.

We end up with something that looks like this.

 params [
   "_spawnMarker",
   "_waypointMarker"
 ];

_waypointType will act as our parameter for the type we assign to the waypoint. If you look at the BI wiki waypoint commands you’ll notice that you have to assign a waypoint to a group and then assign a type to a said waypoint. But, let's consider something. What if I just don’t wanna use anything? Well, on the params page, it offers us a special way of setting up each parameter we list in our array block:

[variableName, defaultValue, expectedDataTypes]

So what we have here is a block where we realize we can list our parameter name, what we want the default value to be if we don’t list the parameter, and what we want the accepted data type to be. So, if we’re doing it for our waypoint type, we can set the values as follows:

 ["_waypointType", "MOVE", [""]]

And now fully convert our params statement so it looks like this:

 params [
   ["_spawnMarker", "", [""]],
   ["_waypointMarker", "", [""]],
   ["_waypointType", "MOVE", [""]]
 ];


OK, so that’s our three required values with one value which we assigned as default. What about spawning groups? Well, if we look on the BI wiki, there is an official function which spawns a group for us, called BIS_fnc_spawnGroup. We see it accepts a variety of values to allow a degree of randomization and flavor to the spawned group. We also see it allows us to use a config path of a group to reference the group. With that in mind, if we open the config viewer and look through cfgGroups, continually clicking on subclasses, we’ll eventually reach a group. The path listed is what serves as a path for us. First we want to arrange default values so that if we have a scenario where we do not include a unit a default one is picked. For that one we can use the stock CSAT rifle squad. Their class path is ConfigFile >> “East” >> “OPF_F” >> “Infantry” >> “OIA_InfSquad”. These four values will be defined by four string parameters. Only thing that’s left now is the behavior and formation, which if we look at the BI wiki pages we discern that they have clear string inputs for their command, so we can just default them to COMBAT and LINE, and force the AI to use RED as their engagement rule.

With all that in mind, our params and default values will look like this:

 params [
   ["_spawnMarker", "", [""]],
   ["_waypointMarker", "", [""]],
   ["_waypointType", "MOVE", [""]],
   ["_side", "East", [""]],
   ["_faction", "OPF_F", [""]],
   ["_category", "Infantry", [""]],
   ["_type", "OIA_InfSquad", [""]],
   ["_behaviour", "COMBAT", [""]],
   ["_formation", "LINE", [""]]
 ];

Make a Function do Stuff

Now we want to actually make the function do something useful with these parameters. So, let’s focus back on that BIS_fnc_spawnGroup function. If we look at its properties we notice that it has a lot of very nice overrides and optional values.

However, we need to secure our function to not fail if we have a spawn marker that doesn’t exist. That’s done by adding the following if statement immediately after the params:

 if(_spawnMarker == "") exitWith {};

We now can assign the values from our own params statement into the parameters of the function, but this begs the question of how do we make the config path look good and fit into the file? We can’t just stretch the method to the brim. What we’ll do instead is, we’ll create a private variable called _config which we’ll turn into our config path and use our params in the declaration. It’ll look like this:

Mission Making Basics for 100% Beginners

-Figuring out your idea, Fleshing out the details
-overview of the file structure
-links to good sqf resources
-opening other missions to see
-caching units & Optimization
-setting up briefing
-using pictures in briefing and marker triggers
-Making Loadouts (Picture guide?)
-Making objectives (Multiple Ways)
-Respawn systems and how to set them up (Time Based, Objective Based, Rolling Respawn)
-Difficulty balancing


Using the Template

* FPArma Template
-how to build off of the template

Advanced Mission Making

-Using HC Effectively & Server AI