Jump to content
Zenophon

Zenophon's ArmA 3 Co-op Mission Making Framework

Recommended Posts

Hi Zen thanks for your reply. I have had a look at escape and evade as you suggested and it mostly does want I want it to do. I have been trying to isolate the script so that I can implement hostile units spawning around the player, using give loadouts etc. I have succeeded in this but using the 'Escape and Evade; Init as a base I used the script you provided..

 

while {true} do {
    _pos = [_player, [400, 800]] call Zen_FindGroundPosition;
    _group = [_pos, ...] call Zen_SpawnInfantry;
    0 = [_group, ...] call Zen_GiveLoadout;

    sleep 120;
};

 

 

and then modified it to

 

f_PatrolThread = {
    _group = _this select 0;
    _AISkill = _this select 1;
    _AISquadRange = _this select 2;
    _maxIndfor = _this select 3;
    _IndforPatrolGroups = [];
   


while {true} do {
    _pos = [_X, [400, 800]] call Zen_FindGroundPosition;
    _IndforGroup = [_spawnPos, resistance, _AISkill, _AISquadRange] call Zen_SpawnInfantry;
    0 = [_IndforGroup] call Zen_GiveLoadoutIndfor;

    sleep 120;

 

        0 = [allUnits, true] call Zen_MultiplyDamage;
        0 = [_IndforPatrolGroups, _IndforGroup] call Zen_ArrayAppend;
        0 = [_IndforPatrolGroups] call Zen_ArrayRemoveDead;
        _patrolThread = [_IndforPatrolGroups, ([Cur_Supply_Point, _group] call Zen_FindAveragePosition), [1, 700], 0, "limited", "safe"] spawn Zen_OrderInfantryPatrol;

        waitUntil {
            sleep 10;
            (({side _x == resistance} count allUnits) < _maxIndfor)
        };

        terminate _patrolThread;
    };
};

 

 

 

......................................

 

aiming to retain the clean up function. The problem is that I can get units to spawn with the random load out I want (Via Giveloadoutindfor arrays) apart from  random fully militarised units that occasionally spawn, CQB units etc. I cannot find any mention of these units anywhere to tell it to not spawn.

 

My main problem is that the hostel units spawning around the player are spawning in the sea at the edge of the map with the error stating undefined variable_X (being the player variable name). So have I got this correct that in this script _pos =  [ _player, should be replaced with the player variable name so, _pos =  [ _x,        So does that represent the player variable name? If not can you clarify this for me as I have spent a day on this and cannot get it to work. Thanks in advance for your help. As ever really appreciate your work and the framework. Awesome tool!

while {true} do {
    _pos = [_player, [400, 800]] call Zen_FindGroundPosition;
    _group = [_pos, ...] call Zen_SpawnInfantry;
    0 = [_group, ...] call Zen_GiveLoadout;

    sleep 120;
};

Share this post


Link to post
Share on other sites

Awesome answer! It is that obvious but I dug too deep into the scripts and functions. Thanks... you made my day :D

Share this post


Link to post
Share on other sites

Husker-71, Zen_GiveLoadoutIndfor is essentially a lot of preset loadouts with slight randomizations in each one; they're all based upon one faction in vanilla ArmA, the AAF (same for the Opfor and Blufor functions, those are the basic CSAT and NATO equipment).  Those functions exist to provide a quick way to change/randomize loadouts for the common default factions.

 

If you want full control over the units' equipment, you need to use Zen_CreateLoadout, Zen_GiveLoadoutCustom, etc. to manually specify all of the items the units will use.  The custom loadout system also gives you a lot of randomization options within each loadout; however, you'll need to learn the formatting to create them (there is a demonstration for this).  Alternatively, you can write your own loadout scripts (e.g. export from virtual arsenal) or use another system that gives you detailed control.

 

The '_x' is a special variable name used to mean the current element in a forEach loop (as well as in the condition of the 'count' command); since there is no forEach loop, _x has no value.  If you are in singleplayer, just use the 'player' command; for multiplayer, you'll need to define the players as a group, an array of objects/groups, etc. and be prepared for players leaving/dying or, if you plan to support JIP, joining.

 

  • Like 1

Share this post


Link to post
Share on other sites

Hi Zen, I am hoping for some insight on how to overcome or workaround a problem I have encountered with using the excellent Framework. It relates to JIP players and objectives in multiplayer games with AI disabled. I have come across this in my own mission but tested the same issue in the Zen_InfantryPatrol.Altis sample mission (but with disabledAI = 1 in description.ext).

 

Basically - for certain types of objective have been created, JIP players then joining the mission are assigned the tasks for those objectives but don't seem able to complete them.

 

For the test in Zen_InfantryPatrol.Altis, I have added this code to the init.sqf, just before // the main loop, infinite yet simple

sleep 4;
private _objPos = [x21, 60 + random 40, random 360] call Zen_ExtendVector;
_a_ret_objective = [_objPos, west, west,"Box","reach"] call Zen_CreateObjective;

 

If I start the mission with two just the two team leaders being played (units X11 and X21) when the Zen_CtreateObjective is called the _rangers array used in this code

    case "reach": {
        0 = [_rangers, _taskUniqueName, "succeeded", _objects] spawn Zen_TriggerAreNear;
    };

is [X11,X21], so the player that now joins the mission as a JIP player is rightly assigned the 'Reach the box task' by JIPSync.sqf, but when they get to the box they are not able to satisfy the conditions in Zen_TriggerAreNear as they were not in the _rangers array when the objective was created. I can't think of a way of putting code into JIPSync that will update the contents of the _rangers array as I am not clear how I would detect the thread that Zen_TriggerAreNear is running in and change the data being tested in the call to Zen_AreInArea. If you have suggestions on a solution I will be most grateful.

 

[ps - Update] Lines 55,56 of Zen_TriggerAreNear - should it select 0 rather than select 1?

_rangers = [(_this select 0)] call Zen_ConvertToObjectArray;

 

Edited by killick
Add related query

Share this post


Link to post
Share on other sites

For your specific case, you could do what I'm going to do: change the units update in all the trigger functions to not include the 'if (typeName (_this select 0) == "SIDE")' check (yes, Zen_TriggerAreNear had the wrong index), so that they always update (e.g. if an array of groups is given) and make Zen_CreateObjective give the true argument to its trigger functions so it can use this feature as well.  This will fix the problem without having to change how any code uses these functions.

 

However, the above assumes that we want all units of the side to be able to complete the objective or (if we use groups instead of sides) that the JIP unit will join a defined group.  These assumptions cover a large number of possible usages.  In a more general case, there are a few things you can do to change the units the trigger is considering.  One solution is to use the return values of Zen_CreateObjective to spawn a new thread to check for the JIP player.  This is fine for JIP players; the trigger functions can run on any machine as well as have duplicates running (across machines or on the same machine) without issue.

// create the objective the same way
_a_ret_objective = [_objPos, west, west,"Box","reach"] call Zen_CreateObjective;

// and put it in Zen_JIP_Args_Server (I think it should be 11 in Infantry Patrol)
Zen_JIP_Args_Server set [11, _a_ret_objective];

// ...
// and in the JIP sync code, spawn the trigger for the player using this information
_a_ret_objective = Zen_JIP_Args_Server select 11;
0 = [player, _a_ret_objective select 1, "succeeded", _a_ret_objective select 0] spawn Zen_TriggerAreNear;

However, this method only allows for adding players, not removing them.  You could modify Zen_CreateObjective to return the thread

_h_trigger =  scriptNull;
switch {// ...
    // ...
    case "reach": {
        _h_trigger = [_rangers, _taskUniqueName, "succeeded", _objects] spawn Zen_TriggerAreNear;
    };
};

// ...
call Zen_StackRemove;
([_objects, _taskUniqueName, _h_trigger])

// ...
// note that this is now global
a_ret_objective = [_objPos, west, west,"Box","reach"] call Zen_CreateObjective;

then terminate and update the thread when a JIP client joins

// in the server's JIP code (Zen_SyncJIPServer)
// All global variables on the server are accessible in this scope
terminate (a_ret_objective select 2);

// I've put west here just to match the code you've given
// The units given to the trigger could be found in any way you want
0 = [west, a_ret_objective select 1, "succeeded", a_ret_objective select 0] spawn Zen_TriggerAreNear;

See the JIP demo's changing of Zen_TrackInfantry threads for a similar example (that one uses a group instead of a side).

Share this post


Link to post
Share on other sites
19 hours ago, Zenophon said:

For your specific case, you could do what I'm going to do: change the units update in all the trigger functions to not include the 'if (typeName (_this select 0) == "SIDE")' check (yes, Zen_TriggerAreNear had the wrong index), so that they always update (e.g. if an array of groups is given) and make Zen_CreateObjective give the true argument to its trigger functions so it can use this feature as well.  This will fix the problem without having to change how any code uses these functions.

 

Thank you this help, amazing as always.

 

As I am currently planning to set up the missions I am very likely to want all Zen_CreateObjective tasks to be capable of being achieved by 'allPlayers' or anyone on a side - as they will be co-op missions for a group all playing as Blue or Opfor. So the first option you have given will I think work fine for me. Although I also think the spawning of a new trigger to include the JIP player is a very clever solution, one I would never have got to. I was stumped on trying to think how to change the existing trigger. 

 

Thanks again.

 

Share this post


Link to post
Share on other sites

Hi Zen,

 

Getting back to learning this framework. The included sample missions don't seem to work for me. I import them into Eden, make no changes and then export them to multiplayer but when I run them not much seems to happen other than me spawning in at the start point. Any ideas?

 

Share this post


Link to post
Share on other sites

Hello Tuskegee

 

I believe some of the sample multiplayer missions need at least two players at the start to run, that is both squad leader roles to be populated with players or with AI. If it is just you in the mission then the scripts will report errors (if error reporting is turned on).

Share this post


Link to post
Share on other sites

First, did you put the framework code in the mission folder (I know this sounds ridiculous, but I have to start here)?

 

Which mission(s) in particular don't work for you?  As killick says, some (at least 'Airbase Assault') require two or more people.  I picked one at random ('Clean Sweep') and didn't see any issues.

 

Do the missions work in the editor?  If it doesn't work there, it's certainly not going to work in MP.

 

Do you have '-showScriptErrors' (or the launcher's equivalent setting) enabled, or are you checking the .rpt logs for script errors?  These sample missions are fairly old and there might be a compatibility issue with the latest framework code.

Share this post


Link to post
Share on other sites
5 hours ago, Zenophon said:

First, did you put the framework code in the mission folder (I know this sounds ridiculous, but I have to start here)?

 

Which mission(s) in particular don't work for you?  As killick says, some (at least 'Airbase Assault') require two or more people.  I picked one at random ('Clean Sweep') and didn't see any issues.

 

Do the missions work in the editor?  If it doesn't work there, it's certainly not going to work in MP.

 

Do you have '-showScriptErrors' (or the launcher's equivalent setting) enabled, or are you checking the .rpt logs for script errors?  These sample missions are fairly old and there might be a compatibility issue with the latest framework code.

 

Hi Zenophon,

 

Yes I put the framework code in the mission folder. I know I tried Airbase Assault but didn't know about the requirement for more than one player. I will try that one again with friends. I will try Clean Sweep and the rest as well again and look for errors. Thanks for responding and for the great framework.

 

Share this post


Link to post
Share on other sites

Hi! Is there any code example for the map control (dialog)? I'd like to know how to click on the map and add the selected point coordinates to a list control.

Share this post


Link to post
Share on other sites
10 hours ago, Dolf86 said:

Hi! Is there any code example for the map control (dialog)? I'd like to know how to click on the map and add the selected point coordinates to a list control.

 

Ok, i managed to get the x and y when i click on the map. How can i convert screen coordinates to world coordinates since it seems i can't use ctrlMapScreenToWorld?

Share this post


Link to post
Share on other sites

The 'MAP' control of the dialog system is essentially the same as the 'real' map.  Thus, it functions and responds as the real map does.  You can use

0 = ["Dialog_Map_Click", "onMapSingleClick", {Map_Pos = _pos}, []] call BIS_fnc_addStackedEventHandler;

once at the beginning of the mission.  When you prompt the user to click:

Map_Pos = 0;
waitUntil {
    (typeName Map_Pos == "ARRAY")
};

_local_Map_Pos =+ Map_Pos;

You would then use the local copy of the position in your code.  This ensures we are using the correct position for that click; you can run multiple functions in parallel that are storing and using different click position instances (assuming the waitUntil of each does not run concurrently).

 

If you need to prevent input from the real map, you can either remove the stacked EH with a BIS function or have your functions check that your dialog and map are open rather than the main map.

  • Like 1

Share this post


Link to post
Share on other sites
5 hours ago, Zenophon said:

The 'MAP' control of the dialog system is essentially the same as the 'real' map.  Thus, it functions and responds as the real map does.  You can use


0 = ["Dialog_Map_Click", "onMapSingleClick", {Map_Pos = _pos}, []] call BIS_fnc_addStackedEventHandler;

once at the beginning of the mission.  When you prompt the user to click:


Map_Pos = 0;
waitUntil {
    (typeName Map_Pos == "ARRAY")
};

_local_Map_Pos =+ Map_Pos;

You would then use the local copy of the position in your code.  This ensures we are using the correct position for that click; you can run multiple functions in parallel that are storing and using different click position instances (assuming the waitUntil of each does not run concurrently).

 

If you need to prevent input from the real map, you can either remove the stacked EH with a BIS function or have your functions check that your dialog and map are open rather than the main map.

 

Ok, now i get it! I will try soon, thank you! And congratulations for the framework, it became essential to me. Great work!

Share this post


Link to post
Share on other sites

Are there any YouTube tutorials on how to use these?

 

I'm reading the FAQ and other documentation but there is SO MUCH text that I think I'm not fully understanding it. This is fricken 200 page operating manual haha

Share this post


Link to post
Share on other sites

I assume there are no such videos. But its realy not that hard to understand.

Your problem is that you trying to understand whole functionality at once.

You really not need to do this in that way.

Try little step, start with default framework init.sqf.

The framework is a bunch of functions which can do for you all what you need in your mission.

All what you need is call it with desired parameters ,like position, count, and others params.

 

Share this post


Link to post
Share on other sites

 

Hi Zenophon.

I got problem with your Zen_SpawnAmbientVehicles function as there some problem with valid position of vehicles.

Using this function some of the cars are being destroyed because obstacle at spawn. Others are placed in the middle of road.

[[worldsize/2,worldsize/2,0],worldsize/2,[5,10]] call Zen_SpawnAmbientVehicles;

For the scenario goals spawning ambient vehicles on road is not good idea.

 

I decided to create walk around and do this manually. But there i have  big problem with  Zen_FindGroundPosition function.

I'm trying to avoid roads and this works most likely but i also trying to avoid near building and this is not working for me.

Huge amount of vehicles being spawned inside  buildings , in rocks and others obstacles.

After innumerable combination and attempts i came back to ask for help.

Are there a better  call syntax for Zen_FindGroundPosition for secure position or I'm fighting with windmills?

 

{
	for "_i" from 1 to floor (random AMBIENTVEHCOUNT) do {
			
		private _carType = selectRandom _carClasses;
		private _objsize = (floor (sizeOf _carType))*2;
		private _carPos = [_x, 0, [], 1, [3,_objsize], [0,360,'compass'], [1,0,_objsize], [0,0,0], [0,0], [0,0,-1], [1,[0,0,-1],_objsize], [0,0,0],0] call Zen_FindGroundPosition;
		private _car = [_carPos,_carType,0,random 360,true] call Zen_SpawnVehicle;
	};
		
} forEach _oamarkers;

 

 

 

Share this post


Link to post
Share on other sites

Zen_FindGroundPosition does not account for the size of buildings, rocks, etc when avoiding them; it treats them as point objects.  This is done for efficiency, since using sizeOf (or even worse using boundingBox and then getting the rotation/orientation of the box) for every object will add another nested loop over all nearby objects (and that's assuming sizeOf/boundingBoxReal are correct for all static terrain objects).  Currently the code finds all objects of a certain type within the given radius from a point and just counts them.

 

This means that the avoidance distance must account for the size of the objects you are trying to avoid, e.g. if we estimate roads to have a width of 5 meters, buildings a radius of 15, and large rocks at 10, that can be added that to the size of the object you're trying to fit

private _carPos = [_x, 0, [], 1, [3 , _objsize + 5], [0,360,'compass'], [1,0,_objsize + 15], [0,0,0], [0,0], [0,0,-1], [1,[0,0,-1], _objsize + 10], [0,0,0],0] call Zen_FindGroundPosition;

You can tweak those tolerances for a certain map/area.  It may also be helpful to use blacklist markers to remove any difficult areas (e.g. very large buildings, a very dense city, etc.); that will probably speed up the search as well (it's faster to find if a point is in an ellipse/rectangle than to check for nearby objects, particularly for larger avoidance distances).

  • Thanks 1

Share this post


Link to post
Share on other sites

hi Zenophon, this framework is great. Haviing seen it when you first released I am please to know you are still working on things.

 

Can you please advise when using Zen_FindGroundPosition what kind of data I would need to put in to find a valley on Altis.

Given most are in the North-West and West a marker (or two) in that area would speed up the search - I can do that.

I would imagine that the check would be for the steeper gradients in the marker area. 

Once located how do I find the direction the valley takes so I could then spawn a few small camps there?

 

thanks for your assitance.

Share this post


Link to post
Share on other sites

To find a good place for a camp, you would use the slope argument (the 10th) to find a flat area, which leaves the terrain height above sea level (12th) argument.  You can check the heights of the hills and valleys on the map and set a limiting value that only chooses the valleys; this would include a tolerance above the valley floor based upon their variations.

 

If there is a overall tilt to the terrain (e.g. the valley and ridge elevations are all increasing/decreasing), you'll need to split the region into different areas with their own elevation limit (the number depending upon terrain tilt and the tolerance).  Basically, any area in which the highest valley point is above the lowest ridge elevation needs to be divided.

 

Depending on how well this works out, check out Zen_FindNearHeight and Zen_IsHillArea.  A ridge looks like a hill on 2 of 4 sides (Zen_IsHillArea might return around 0.5), but Zen_IsHillArea will have a lower value for a valley (assuming the ridges and valley slope up/down equally lengthwise).  Zen_FindNearHeight can be used iteratively to find the minimum of terrain (repeat Zen_FindNearHeight in an e.g. 100m radius of each point it finds until the elevation is low enough); this will move from a ridge to a valley, then down the slope of a valley (but not at random) until the minimum is local by the radius used.

 

Once you know you're in a valley, Zen_FindTerrainGradient with a reasonable scan radius will tell you its slope.  If the slope along the valley is weak, a large radius might result in errors from the valley walls (they can be asymmetric in slope magnitude), but a radius too small will not reflect the overall terrain features.  About the width of the flatter part of the valley (assuming the point is centered in the valley) should work well (unfortunately this varies by valley).

 

As you can see, scanning terrain dynamically is difficult.  If nothing's working, placing several dozen preset locations manually and choosing from them is less random (also tedious to port your mission to other maps, if you're planning to do that), but runs faster and guarantees quality placement.

  • Thanks 1

Share this post


Link to post
Share on other sites

Guys hello again Im back with a new Machine to run A3 and ofc make new missions and now I find myself right back where I left off in Arma 2 struggling again with the stupid helicopter insert extract. I just want one to work in multiplayer not a dedicated just on my own lan or internet server. For the love of god I had so many failed attempts in Arma2 where I would make missions and use scripts and the heli would show up to just hover or fly away...

Now I am faced with the same dilemma in Arma3 so here is the situation after hours of youtube video watching of videos that clearly are out of date since some update NONE of them are working.
So I went to Armaholic checked their forums out no option to download their example and again 2014... Its 2018 now alot has been updated in those 4 years. So I used the search function and found

another really great script with a beautiful option of pop smoke or IR strobe for Pickup. Downloaded... It was greyed out, imported , BROKE. loads intro but no option to use the Radio Alpha... No option to 0-0-1 since it just swaps weapon
tilda key brings up menu with support greyed out and Supports in white but empty when chosen.... Found this thread in the search... Now here I am I flipped through the forum pages to this number 30
since the past pages were from older problems, hoping to see an updated script to grab of demo mission I end here.

Can one of you help me out with an up to date as 2018 working script for heli insert/extract? I went to you Zenophons library and saw that the post was from 2014 So im just asking for some help that we all know is working.
I tried also the built in support modules that should work yet don't. I know you can use Zues but I dont like the idea of using Zues for that I like the old way of building missions so that you can stay immersed and have the heli show up and boom mission ends.
Yet nothing is working. So can anyone help me out here? Thanks. I am aiming to do an up to date How to video on Insert/Extract if at all possible because the ones on youtube are all out of date and do not work in MP.

Share this post


Link to post
Share on other sites

Oh come on is not that bad.

Al of those are working well.

After all you can script self one to make sure that will  be exactly what you need.

You need use waypoints and its types to manipulate heli ordnance.

Last i even found an trick to simplify stuff by manipulate groups between cargo and crew

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×