UPDATE: Use the new dedicated Arma command getMissionPath

Today I’m going to share this little trick I’ve discovered literally few hours ago. And the trick is how to find location of your mission assets so that you can access them directly from your mission. Let me explain what I mean.

By default the mission is packed into a .pbo file, which is then downloaded by anyone who connects to the server. In order to access .pbo content directly, like sounds or images you put in, you need to know the path that Arma 3 would understand.

In most cases Arma 3 would let you get away with relative path to your assets like “sounds\mysound.wss” for example, which would mean  mission root folder -> sounds folder -> mysound file.  However some commands such as drawIcon3D or playSound3D will not understand relative mission path, although these commands will work fine if you have your assets stored in an AddOn.

So how to find out the direct filepath for the mission assets? It is very simple really – ask preprocessor, preprocessor knows :). There is a macro in preprocessor called __FILE__ which gets replaced with current file path including the filename. So if you include this in init.sqf the result will be something like:

mpmissions\__CUR_MP.Stratis\init.sqf

Note that mission path contains the map name as well. Of course we can settle for manual editing of the path depending on the map, but then what is the point of me writing all these blogs if we can’t automate this simple task by now? So this is what we will do. Get the path to init.sqf, convert it to array, get rid of “init.sqf” bit by making array 8 elements smaller (length of “init.sqf”), then convert everything back to string:

MISSION_ROOT = call { private "_arr"; _arr = toArray __FILE__; _arr resize (count _arr - 8); toString _arr };

Now if we run this from init.sqf, MISSION_ROOT variable will contain our mission root

mpmissions\__CUR_MP.Stratis\

and not just on the clients but also on the server and those stubborn commands now also work.

playSound3D [MISSION_ROOT + "sounds\mysound.wss", player]; drawIcon3D [MISSION_ROOT + "icons\myicon.paa", ...];

Enjoy,
KK

EDIT: And probably even better way 🙂

MISSION_ROOT = format ["mpmissions\%1.%2\", missionName, worldName];

EDIT2: The above code is OK for MP but in SP the path is different. The first method is however universal and will work in SP too.

EDIT3: Yet another method for extracting the mission root, this time the courtesy of BIS Karel Moricky. This method relies on description.ext file, which must be present in your mission directory. Using str with missionConfigFile will give us desired path to description.ext, all we need to do is to strip off “description.ext (which is 15 characters long) just like we did with init.sqf before.

MISSION_ROOT = call { private "_arr"; _arr = toArray str missionConfigFile; _arr resize (count _arr - 15); toString _arr };

I think this is the best method of them all because it is universal, will work in SP too, you can put your root finding routing anywhere and do not need to use preprocessor. Just to remind, you do have to have description.ext file present in mission.

EDIT4: And yet another method of obtaining mission root, courtesy of another ArmA enthusiast, Kunsa. Converting the script handle returned by execVM to string with str command, reveals the path to the execVMed script:

_script = [] execVM "myScript.sqf"; hint str _script; //path to myScript.sqf waitUntil {scriptDone _script}; hint str _script; //<NULL-script>

So in order to obtain the path this way you must execute your path finding routine before the execVMed script is done and script handle became <NULL-script>. Nevertheless this is a valid method too.

EDIT5: Here is a one liner for you, using substring functionality not available at the time I was writing this blog post (You need descripiton.ext present for this to work as well):

MISSION_ROOT = str missionConfigFile select [0, count str missionConfigFile - 15];

EDIT6: So far, this is my favourite. MISSION_ROOT can be defined in description.ext. missionConfigFile just doesn’t work this early, but __FILE__ does!

//description.ext __EXEC (MISSION_ROOT = __FILE__ select [0, count __FILE__ - 15]) //from any script _root = parsingNamespace getVariable "MISSION_ROOT";