Jump to content
Zenophon

Zenophon's ArmA 3 Co-op Mission Making Framework

Recommended Posts

This framework is great. I was sort of stuck using a mix of Broma and the F3 frameworks before, but they never include enough documentation to explain how or why things happen. The only thing is, obviously this is a Coop framework, so I need to either find part of it useful for TvTs (I found one of your example missions included a method of randomising the day and weather, perfect!). I guess the only thing this doesn't cover is Paramaterson the lobby screen and a Spec script, but I have already confirmed the Modified Kegety's Spectator works well with this.

All I really need now is a way to block third person and I can finally make missions without worrying about things breaking. I already use my own predefined loadouts, and weather isn't a real issue because I usually have an idea of a specific time or weather setting when I make a mission.

I get these issues aren't really things you set out to cover with this Zen, my issues above are just aspects of mission making I'm used to , I'm just whining about my own problems. You've got something here that is just absolutely wonderful. Everytime I see clean, well laid out and commented code on anything it makes me happy.

Thank you for your kind words; it's always good to know my work has made difference. As much as I would like to do everything, I have to focus on vital parts of mission making first. The more abstract features, such as repacking magazines, are greatly outnumbered by very direct functions dealing with spawning, objects, data structures, etc.

For PvP, a few categories of functions will be useless (AI spawning and orders), and there are no demonstrations or sample missions to help with TvT gamemodes. However, significant features such as the task system, fire support, and the math and data functions with work exactly the same as before. The examples of player respawn and JIP using the framework will also function the same.

Hey Zen

ive been going though your framework for the best part off a week learning how to do things

for the best part i have managed to get infantry and vehicles to spawn and patrol in a large area

to achieve this i have used the spawning code from your random battle demonstration

now my first problem seems to be getting the AI to spawn at Oreokastro and patrol in the town

for this i am using

for "_i" from 1 to 2 do {

_spawnPos = ["Oreokastro",0,0,1,[2,0]] call Zen_FindGroundPosition;

_group = [_spawnPos, east, "infantry", 4] call Zen_SpawnInfantry;

0 = [_group, east] call Zen_GiveLoadout;

0 = [_opforInf, _group] call Zen_ArrayAppend;

};

if correct this should spawn them on roads but for some reason they seem to be ignoring the roads and spawn on the outside of the town

now the second part im having trouble with is a convoy objective

on the map near sytra i have a marker named "ConvoyStart" - this is wear i would like the convoy to start

i then have another marker named "ConvoyAttack" near the dump just south of Oreokastro - this is were i would like the convoy to move to and the area i want the players to attack the convoy

to achieve this i use

_convoySpawnPos = ["ConvoyStart", [100,150],[],1,[2,0]] call Zen_FindGroundPosition;

_yourObjective = ["ConvoyAttack",(group player), east, "Convoy", "eliminate",_convoySpawnPos] call Zen_CreateObjective;

now the convoy starts near the marker "ConvoyStart"

And The Objective Location Shows up near the dump

however when i teleport to the convoy start i see the convoy sitting on the road waiting to move and after a few seconds i hear there engines turn on

now at this point i can hear an explosion but no vehicle seems to be destroyed and upon checking all the convoy vehicles for passengers i notice there all dead

Does the marker 'Oreokastro' cover the roads in the town? I have tested with a 200m circle over the town and the spawn points were always on a road. You can use Zen_SpawnMarker to quickly mark each spawn point, e.g.:

   _spawnPos = ["Oreokastro",0,0,1,[2,0]] call Zen_FindGroundPosition;
   0 = [_spawnPos] call Zen_SpawnMarker;

Also, Zen_OrderInfantryPatrol does not prefer roads, and this results in strange patrol patterns in urban areas. For the next release, Zen_OrderInfantryPatrol will move waypoints to roads less than 50 meters away. This should only affect patrols in dense urban areas by making them go around city blocks.

I cannot reproduce the convoy issue either. The sound is likely a collision sound, and a collision can injure the crew of a vehicle. This could be an engine physics bug. Try removing the randomization of the convoy start and start the convoy on an open road away from obstacles. The spawning of the vehicle should not damage it immediately.

Share this post


Link to post
Share on other sites

hey zen thanks for the reply

i have fixed the issue i had with the groups spawning at Oreokastro by replacing

_spawnPos = ["Oreokastro"] call Zen_FindGroundPosition;

with

_spawnPos = ["Oreokastro",0,0,1,[2,0]] call Zen_FindGroundPosition;

now they patrol the town as they should :D

Share this post


Link to post
Share on other sites

Zen, as usual your suggested code works like a dream, and my mission now has functional respawns. It's getting close to being done! I have a couple more niggling issues to sort out.

In order to get the insurgency feel, I decided to abandon the automatically spawned areas around towns, and instead, manually place 350+ 50m x 50m markers on the map. As per Fireball's original insurency, each marker spawns a couple of enemys, who must be neutralised to clear the grid. This works great witht he existing code, but leaves me facing a challenge re vehicle spawns. As a town can now have around 10 grid squares, I do not want to spawn one vehicle per grid. I think a good approach would be to create a small chance of a vehicle spawning - like a 10% chance. Under the Spawn Vehicles section in the code below, it starts with the line for "_i" from 1 to 1 do {. If I were to amend that to something like for "_i" from 1 to 0.1 do {, would that create a 10% chance of it happening? Or is the correct approach create an array from 1 - 10 and then if the value is 10 then spawn the vehicle?

            // Spawn 3 groups per marker
           for "_i" from 1 to 2 do {
               _spawnPos = [_x] call Zen_FindGroundPosition;
               _enemyGroup = [_spawnPos, ENEMY_SIDE, AI_SKILL, [1,2]] call Zen_SpawnInfantry;
               0 = [_groupsArray, _enemyGroup] call Zen_ArrayAppend;
           };

                       // Pick a Random Vehicle
                       _vehArray = ["O_G_Offroad_01_armed_F","O_MRAP_02_hmg_F","O_APC_Wheeled_02_rcws_F","O_APC_Tracked_02_cannon_F","O_MBT_02_cannon_F"];
                       _selectedVeh = [_vehArray] call Zen_ArrayGetRandom;


                       // Spawn VEHICLES
                       for "_i" from 1 to 1 do {
                       _spawnPos2 = ["SM7"] call Zen_FindGroundPosition;
                       _spawnedVehicle = [_spawnPos2, _selectedVeh] call Zen_SpawnVehicle;
                       0= [_spawnedVehicle, ENEMY_SIDE] call Zen_SpawnVehicleCrew;
                       0 = [_groupsArray, (crew _spawnedVehicle)] call Zen_ArrayAppend;
           };

I am also having a problem calling the custom loadout on respawn. I have it included in the coade below, I am not sure why this is not working because it looks correct to me?

// keep control over your mission by handling respawn yourself
f_HandleRespawn = {
   if (time < 5) exitWith {};
   private ["_players", "_body"];

   _players = _this select 0;
   _body = _this select 1;

   if (player == _players) then {
       titleText ["Still Alive", "BLACK FADED", 0.2];
   };

   0 = [_players] call Zen_AddGiveMagazine;
   0 = [_players] call Zen_AddRepackMagazines;
       0 = [_players, _loadout2] call Zen_GiveLoadoutCustom;  

   0 = _body spawn {
       sleep 300;
       deleteVehicle _this;
   };
};

Thanks again for all your help - I should put a photo of you in my mission load screen!

Share this post


Link to post
Share on other sites

Your loadout issue is one of locality.

_loadout2 is not known to the function most likely. It needs to be a global variable for the function to use it. Not sure if using private[] would help or not, but its definately a scope issue. My loadouts are actually defined as global.

Share this post


Link to post
Share on other sites

Ah, that makes sense - thanks m0nkey. I really like the way you handle loadouts in your example, i should try to replicate that!

Share this post


Link to post
Share on other sites

I have a prototype tool that lets you take an exported virtual armory file and convert it to zen loadout. So, you go to virtual arsenal, create a loadout, export to clipboard and save it. Use my tool, it creates a file that is 90% completed in zen format. If you are interested.

Takes time to get all this down. Its a lot of info to digest, lots of functions, lots and lots of functions lol. But in the end its made my missions better without me having to do quite so much "reinventing the wheel" haha.

Share this post


Link to post
Share on other sites

Introduction

Greetings fellow scripters and Armaholics, in this latest installment, I will continue to discuss the development of the framework and, of course, shamelessly advertise the framework in any way possible.

If this sounds boring, you can download the latest version from the original post. As always, the link to Google Drive for the .7z and .zip versions are already up to date. For those looking for older versions, go to file>revisions. The new version should be on Armaholic when Foxhound sees this or I PM him. Please bring any technical issues or mistakes to my attention, so e.g. people don't download the wrong version etc.

Changelog

Lucky release #7 has 17 changes, most of which involve the position functions. This is the first release for stable version 1.26, and will not work with 1.24. The most jarring changes are the renaming of Zen_AvoidPosition to Zen_FindValidDirection and Zen_AveragePositions to Zen_FindAveragePosition. I also apologize for a documentation oversight that let Zen_FindNearHeight have very incorrect parameter descriptions. Its documentation now fully agrees with the code.

Zen_FindValidDirection has been completely rewritten and some parameters removed. A geometric proof occurred to me, in which the angle sector blocked by each circle was actually a triangle defined by the lines tangent to the circle and the line perpendicular through the center. Knowing the distance and direction to, and radius of each circle to avoid, blocked sector angles can be defined extremely easily with some trig. The function is now 100% accurate and gives all angles (whole numbers) that would avoid the given positions.

In addition to geometric proofs, I was further embarrassed by finally realizing a simple and better algorithm for Zen_FindTerrainSlope. It now uses a simple trig algorithm to convert a surfaceNormal vector into the Z angle of the equivalent polar coordinate. Zen_FindTerrainSlope now only has one parameter, as using a radius to find an angled plane is no longer necessary. The 10th parameter for Zen_FindGroundPosition has been changed to reflect this as well. Leaving your code as is will be fine for this, extra arguments are simply ignored.

I am also a fan of the new pushBack command added in 1.26, which I have used to replace every ugly looking '_array set [(count _array ...' line. It looks elegant and works faster, so there's no reason not to use it.

A few other changes include fixing Zen_TransformObject so that it rotates the object correctly. The logic for setting the normal vector and the direction conflicted. Zen_OrderInfantryPatrol now works better in urban areas by giving the groups waypoints on a road. The AI should patrol the streets more rather than wander through backyards.

Because I read every line of every BIS changelog, I could not help but notice that createVehicleCrew now works with partially filled vehicles. Obviously, I had to improve Zen_SpawnVehicleCrew to function similarly. The crew spots will also be filled with the correct class, e.g. a UH-80 with two pilots will get two crewman on the door guns.

8/27/14

  1. Fixed: Zen_GetUnitLoadout put binoculars and similar into the wrong category, causing errors
  2. Fixed: Zen_SpawnVehicleCrew did not return a group when given an autonomous vehicle
  3. Fixed: Zen_TransformObject did not always set the object's direction
  4. Improved: Zen_ArrayAppend, Zen_ArrayAppendNested, and 45 other functions optimized using pushBack
  5. Improved: Zen_AveragePositions renamed Zen_FindAveragePosition, moved to Position Functions category
  6. Improved: Zen_AvoidPosition renamed Zen_FindValidDirection
  7. Improved: Zen_FindValidDirection moved to Position Functions category
  8. Improved: Zen_FindValidDirection rewritten and massively optimized, some parameters have been removed
  9. Improved: Zen_FindGroundPosition terrain slope parameter changed to reflect Zen_FindTerrainSlope change
  10. Improved: Zen_FindTerrainSlope optimized, and one parameter removed
  11. Improved: Zen_OrderInfantryPatrol now prefers roads within a small radius
  12. Improved: Zen_SpawnVehicleCrew can now correctly fill a vehicle that was previously partially filled
  13. Documentation: Fixed Zen_FindNearHeight was not updated correctly for last release's improvements
  14. Documentation: Fixed Zen_FindTerrainSlope was out of alphabetic order
  15. Documentation: Added 1.26 scripting commands to Notepad++ SQF language
  16. Documentation: Updated for Zen_FindAveragePosition, Zen_FindGroundPosition, Zen_FindTerrainSlope, Zen_FindValidDirection
  17. Documentation: Updated Zen_AssaultFrini sample mission using the new JIP code

Roadmap

Although I claimed there would be a Zen_FindTerrainGradient, there doesn't seem to be any use for that. For those who want the direction of a hill, here's some trig:

_normal = surfaceNormal _pos;
_dir = ((_normal select 0) atan2 (_normal select 1)) + 180;

I will probably use that in Zen_IsHillArea for next week.

The preprocessor library is being written internally, but I am delaying the release until next week so that I can use some of the macros in the framework's source code. The point of these constants is that they are actually useful, so if I can't use them anywhere in the framework's 12000 lines of code they're probably useless.

Function Spotlight

As users of my framework know, there is an enormous number of functions at your disposal. The amount of documentation that has to be sifted through can be extremely daunting. Each week I spotlight a function and talk about its usefulness. If you have found an obscure function (not in tutorials, barely seen in demonstrations or sample missions) that you think is useful, PM me and I can spotlight it.

The function chosen for this week is: Zen_ArraySortRandom. This function makes randomizations easier. Compare these two blocks:

_markers = ["mk00", "mk01", "mk02", "mk03", "mk04"];
for "_i" from 1 to 5 do {
   _randMk = [_markers] call Zen_ArrayGetRandom;
   0 = [_markers, _randMk] call Zen_ArrayRemoveValue;
   // ... do something
};

or

_markers = [["mk00", "mk01", "mk02", "mk03", "mk04"]] call Zen_ArraySortRandom;
{
   // ... do something
} forEach _markers;

Also, array indexes will not line up easily in the first block for a parallel data structure. An example: if you have 5 markers and want to spawn three squads in each marker, you can have an array of markers and an array of arrays (containing the groups).

In the first block, the array of markers would have to be reordered so that each marker aligned with the array of groups. In the second block, simply appending the groups to the groups array will keep it in synch with the markers. The difference is:

_markersNew = [];
_groupsArray = [];
_markers = ["mk00", "mk01", "mk02", "mk03", "mk04"];
for "_i" from 0 to 4 do {
   _randMk = [_markers] call Zen_ArrayGetRandom;
   0 = [_markers, _randMk] call Zen_ArrayRemoveValue;
   // ... do some spawning to define _groups;
   _markersNew pushBack _randMk;
   _groupsArray pushBack _groups;
};

or

_groupsArray = [];
_markers = [["mk00", "mk01", "mk02", "mk03", "mk04"]] call Zen_ArraySortRandom;
{
   // ... do some spawning to define _groups;
   _groupsArray pushBack _groups;
} forEach _markers;

I as much as I encourage you to code creatively using the framework, there is no doubt that the second block is simpler and more elegant.

Beta

As already stated, the framework is in the beta stage of development. I am making every effort to quality control releases, but there will be bugs. Both new features and old ones could have bugs, issues, and things people just don't like.

There is no ETA or plan for a 'final' version. The framework is a work in progress, and it will continue to progress while there are improvements to be made (there always will be).

Feedback

As expected, some of the bugs have been pointed out by users, and those fixes are included in the changelog above. I want to thank everyone who has used/supported the framework.

---------- Post added at 22:39 ---------- Previous post was at 22:25 ----------

Zen, as usual your suggested code works like a dream, and my mission now has functional respawns. It's getting close to being done! I have a couple more niggling issues to sort out.

In order to get the insurgency feel, I decided to abandon the automatically spawned areas around towns, and instead, manually place 350+ 50m x 50m markers on the map. As per Fireball's original insurency, each marker spawns a couple of enemys, who must be neutralised to clear the grid. This works great witht he existing code, but leaves me facing a challenge re vehicle spawns. As a town can now have around 10 grid squares, I do not want to spawn one vehicle per grid. I think a good approach would be to create a small chance of a vehicle spawning - like a 10% chance. Under the Spawn Vehicles section in the code below, it starts with the line for "_i" from 1 to 1 do {. If I were to amend that to something like for "_i" from 1 to 0.1 do {, would that create a 10% chance of it happening? Or is the correct approach create an array from 1 - 10 and then if the value is 10 then spawn the vehicle?

            // Spawn 3 groups per marker
           for "_i" from 1 to 2 do {
               _spawnPos = [_x] call Zen_FindGroundPosition;
               _enemyGroup = [_spawnPos, ENEMY_SIDE, AI_SKILL, [1,2]] call Zen_SpawnInfantry;
               0 = [_groupsArray, _enemyGroup] call Zen_ArrayAppend;
           };

                       // Pick a Random Vehicle
                       _vehArray = ["O_G_Offroad_01_armed_F","O_MRAP_02_hmg_F","O_APC_Wheeled_02_rcws_F","O_APC_Tracked_02_cannon_F","O_MBT_02_cannon_F"];
                       _selectedVeh = [_vehArray] call Zen_ArrayGetRandom;


                       // Spawn VEHICLES
                       for "_i" from 1 to 1 do {
                       _spawnPos2 = ["SM7"] call Zen_FindGroundPosition;
                       _spawnedVehicle = [_spawnPos2, _selectedVeh] call Zen_SpawnVehicle;
                       0= [_spawnedVehicle, ENEMY_SIDE] call Zen_SpawnVehicleCrew;
                       0 = [_groupsArray, (crew _spawnedVehicle)] call Zen_ArrayAppend;
           };

I am also having a problem calling the custom loadout on respawn. I have it included in the coade below, I am not sure why this is not working because it looks correct to me?

// keep control over your mission by handling respawn yourself
f_HandleRespawn = {
   if (time < 5) exitWith {};
   private ["_players", "_body"];

   _players = _this select 0;
   _body = _this select 1;

   if (player == _players) then {
       titleText ["Still Alive", "BLACK FADED", 0.2];
   };

   0 = [_players] call Zen_AddGiveMagazine;
   0 = [_players] call Zen_AddRepackMagazines;
       0 = [_players, _loadout2] call Zen_GiveLoadoutCustom;  

   0 = _body spawn {
       sleep 300;
       deleteVehicle _this;
   };
};

Thanks again for all your help - I should put a photo of you in my mission load screen!

I am fairly certain that a for loop will round the upper bound, so 1 to 0.1 will become 1 to 0, which won't run. That kind of for loop uses a <= test, so 0 to 0 runs once, but 0 to -1 does not. Generally, you can't go wrong with:

if (random 1 > 0.9) then {
   // ...
};

For the loadouts, m0nkey is right, _loadout2 is a local variable undefined in the eventhandler's scope. Since you can't pass arbitrary variables directly to an EH, you need to use a global variable. You could also make use of the preprocessor if the loadouts have a fixed name literal; I always do this instead of letting them get the random string names like "Zen_loadout_123abc*()&". That name will also display on the loadout dialog.

  • Like 1

Share this post


Link to post
Share on other sites

Hi, Zen. I wanted to get your expertise on a matter. I am making a hostage rescue mission and want to ensure the location of the hostage be as dynamic and unpredictable as possible. I would like to preferably:

(1) Place the hostage inside a random building within a given marker

(2) If possible, I would like to select multi-story buildings and place the hostage in any level other than the ground level

For the last part, I am desperately trying to avoid a situation in which the hostage would be placed by the doorway of the building. Choosing a building with moor than 2 floors would make the mission a little more exciting and less predictable.

How can I accomplish this, Zen? Thank, you

Share this post


Link to post
Share on other sites

For the loadouts, m0nkey is right, _loadout2 is a local variable undefined in the eventhandler's scope. Since you can't pass arbitrary variables directly to an EH, you need to use a global variable. You could also make use of the preprocessor if the loadouts have a fixed name literal; I always do this instead of letting them get the random string names like "Zen_loadout_123abc*()&". That name will also display on the loadout dialog.

Can you expand on this. Do you mean to "define" the loadout, or just use preprocessor with an include.. or just what exactly?

Share this post


Link to post
Share on other sites

hey zen

ive decided to redo the mission from scratch and start with a fresh mind

for the mission ive decided to use events to attempt to accomplish what i want

so far here is what ive got in my init.sqf

this obliviously need work but as they say fail and try again

#include "Zen_FrameworkFunctions\Zen_InitHeader.sqf"

enableSaving [false, false];
if !(isServer) exitWith {};

Event_Arguments_Array = [];
Event_Queue_Array = [];

f_HandleEventQueue = {
   while {true} do {
       sleep 5;
       {
           if (_x select 1) then {
               _eventName = (_x select 0);
               _qualifier = [_eventName, "@$"] call Zen_StringGetDelimitedPart;
               _functionName = _eventName;
               if (count toArray _qualifier > 0) then {
                   _functionName = [_eventName, "@" + _qualifier + "$", ""] call Zen_StringFindReplace;
               };

               _argsIndex = [Event_Arguments_Array, _eventName, 0] call Zen_ArrayGetNestedIndex;
               _args = (Event_Arguments_Array select _argsIndex) select 1;
               0 = _args spawn (missionNamespace getVariable _functionName);
               0 = [Event_Arguments_Array, _argsIndex] call Zen_ArrayRemoveIndex;
               Event_Queue_Array set [_forEachIndex, 0];
           };
       } forEach Event_Queue_Array;
       Event_Queue_Array = Event_Queue_Array - [0];
   };
};

f_EventTrigger_Generic = {
   _nestedArray = [Event_Queue_Array, _this, 0] call Zen_ArrayGetNestedValue;
   if (count _nestedArray > 0) then {
       _nestedArray set [1, true];
   };
};

f_RemoveEvent = {
   {
       _index = [_x, _this, 0] call Zen_ArrayGetNestedIndex;
       if (_index != -1) then {
           0 = [_x, _index] call Zen_ArrayRemoveIndex;
       };
   } forEach [Event_Arguments_Array, Event_Queue_Array];
};

f_EventIsQueued = {
   (([Event_Queue_Array, _this, 0] call Zen_ArrayGetNestedIndex) > -1)
};

0 = [] spawn f_HandleEventQueue;

f_EventAction_SpawnPatrols = {
   _groupArray = [];
   for "_i" from 1 to 3 do {
       _spawnPos = [_this] call Zen_FindGroundPosition;
       _group = [_spawnPos, east, "infantry", [2, 3]] call Zen_SpawnInfantry;
       0 = [_group, east] call Zen_GiveLoadout;
       0 = [_groupArray, _group] call Zen_ArrayAppend;
   };

   0 = [_groupArray, _this] spawn Zen_OrderInfantryPatrol;
};

f_EventAction_FireSupport = {
   _pos = [(_this select 0)] call Zen_FindGroundPosition;
   0 = [_pos, (_this select 1)] call Zen_InvokeFireSupport;    
};

_nearestObj = [["mkOreokastro", "mkCastle"], player] call Zen_FindMinDistance;	

   _nestedArray = [Event_Arguments_Array, "f_EventAction_FireSupport", 0] call Zen_ArrayGetNestedValue;
   _argsArray = _nestedArray select 1;
   _argsArray set [0, _nearestObj];    

_nextObj = [["mkOreokastro", "mkCastle"] - [_nearestObj], player] call Zen_FindMinDistance;
_centerPos = [_nextObj, _nearestObj] call Zen_FindAveragePosition;

_patrolMarker = [_centerPos, "", "colorBlack", [350, 350], "ellipse", 0, 0] call Zen_SpawnMarker;

0 = _patrolMarker call f_EventAction_SpawnPatrols;
};

{_x setMarkerAlpha 0;} forEach ["mkOreokastro", "mkCastle"];   

_players = group UnitFIA_CO;
_players = group UnitFIA_DC;

_obj1Pos = ["mkOreokastro"] call Zen_FindGroundPosition; 
_obj2Pos = ["mkCastle"] call Zen_FindGroundPosition; 
sleep 1;

_obj2Object = [_obj2Pos, "O_Mortar_01_F"] call Zen_SpawnVehicle;

0 = [Event_Queue_Array, ["f_EventAction_FireSupport", false]] call Zen_ArrayAppend;
0 = [Event_Arguments_Array, ["f_EventAction_FireSupport", ["mkOreokastro", "1"]] call Zen_ArrayAppend;

0 = [Event_Queue_Array, ["f_EventAction_SpawnPatrols@mkOreokastro$", false], ["f_EventAction_SpawnPatrols@mkCastle$", false]] call Zen_ArrayAppend;

#define SPAWN_NEAR(MK) if (([player, MK] call Zen_Find2dDistance) < 1200) then { \
   if (("f_EventAction_SpawnPatrols@" + MK + "$") call f_EventIsQueued) then { \
       0 = [Event_Arguments_Array, ["f_EventAction_SpawnPatrols", MK] call Zen_ArrayAppend; \
       0 = ("f_EventAction_SpawnPatrols@" + MK + "$") call f_EventTrigger_Generic; \
   }; \
}; \

waitUntil {

   SPAWN_NEAR("mkOreokastro")
   SPAWN_NEAR("mkCastle")

																																																	{
   if (("f_EventAction_SpawnPatrols@" + _x + "$") call f_EventIsQueued) then {
       0 = [Event_Arguments_Array, ["f_EventAction_SpawnPatrols", _x] call Zen_ArrayAppend;
       0 = ("f_EventAction_SpawnPatrols@" + _x + "$") call f_EventTrigger_Generic;
   };
} forEach ["mkOreokastro", "mkCastle"];	

if ("f_EventAction_FireSupport" call f_EventIsQueued) then {
   _fireSupport = ["r_80mm_he", 15, 2, 4, 10, 400, 20] call Zen_CreateFireSupport;
   _nestedArray = [Event_Arguments_Array, "f_EventAction_FireSupport", 0] call Zen_ArrayGetNestedValue;
   _argsArray = _nestedArray select 1;
   _argsArray set [1, _fireSupport];

   // Trigger the event itself
   0 = "f_EventAction_FireSupport" call f_EventTrigger_Generic;
};

if this is correct then this should spawn enemies when the players are 1200m away from Oreokastro and the castle

the main part im having trouble understanding is the fire support event

from what ive put and from what i understand is that a morter should spawn at the castle

and iff everything is set up correctly the morter event should only happen when the player is in Oreokasto

now i would also like to add a larger patrol area that covers an area off 1500m - 1000m

in this area i would like to spawn a definite amount of squads plus a chance for a random amount and have all the squads in the master patrol area share info between them selfs

now im guessing the best way to do this would be to exclude these groups from the event queue and have them controlled outside of the events

any help and tips would be greatly appreciated

Edited by ScrumpyJacks

Share this post


Link to post
Share on other sites
Guest

Thanks for informing us about the updated release :cool:

Release frontpaged on the Armaholic homepage.

================================================

We have also "connected" these pages to your account on Armaholic.

This means in the future you will be able to maintain these pages yourself if you wish to do so. Once this new feature is ready we will contact you about it and explain how things work and what options you have.

When you have any questions already feel free to PM or email me!

Share this post


Link to post
Share on other sites
Hi, Zen. I wanted to get your expertise on a matter. I am making a hostage rescue mission and want to ensure the location of the hostage be as dynamic and unpredictable as possible. I would like to preferably:

(1) Place the hostage inside a random building within a given marker

(2) If possible, I would like to select multi-story buildings and place the hostage in any level other than the ground level

For the last part, I am desperately trying to avoid a situation in which the hostage would be placed by the doorway of the building. Choosing a building with moor than 2 floors would make the mission a little more exciting and less predictable.

How can I accomplish this, Zen? Thank, you

Since I wrote that occupy house script, I have a few tricks for determining things about buildings. I would do something like this:

// Assume some urban area is covered by 'mkTown'
// Assume there is some unit, '_unit' we want to put in a building
_building = objNull;
for "_i" from 1 to 100 do {

   // Get a random building
   _pos = ["mkTown"] call Zen_FindGroundPosition;
   _building = nearestBuilding _pos;

   // Set two positions in the XY center of the building
   _housePos = getPosASL _building;

   // Set a test position 50 meters in the air
   _checkPos =+ _housePos;
   _checkPos set [2, (_checkPos select 2) + 50];

   // All buildings have 1 floor
   _floors = 1;

   for "_j" from 1 to 2 step 0 do {

       // To be sure, let each floor be 5 meters, and start on the 2nd floor
       _housePos set [2, (_housePos select 2) + 5];

       // If a ray from the floor to the sky hits something, count this as a floor
       if !(lineIntersects [_housePos, _checkPos, objNull, objNull]) exitWith {};
       _floors = _floors + 1;
   };

   if (_floors > 2) exitWith {};
};

// Now we have a random building with more than 2 floors

// Positions are 0-based
_buildingPositions = 0;

// Apply the standard algorithm to count its positions
for "_i" from 0 to 100 do {
   if ((_building buildingPos _buildingPositions) isEqualTo [0,0,0]) exitWith {};
   _buildingPositions = _buildingPositions + 1;
};

// Generate an array of all valid positions
_buildingPosArray = [];
for "_i" from 0 to (_buildingPositions - 1) do {
   _buildingPosArray set [_i, _i];
};

// Randomize the array and loop through
_buildingPosArray = [_buildingPosArray] call Zen_ArraySortRandom;
{
   // More than three meters ATL should be the 2nd floor or above
   if (((_building buildingPos _x) select 2) > 5) exitWith {
       // Good position found, so put the unit there
       _unit setPosATL (_building buildingPos _x);
   };
} forEach _buildingPosArray;

I have tested this in Kavala, and it detected a large office building just fine. You might need to increase the number of attempts for larger areas.

Can you expand on this. Do you mean to "define" the loadout, or just use preprocessor with an include.. or just what exactly?

I mean use the optional argument to Zen_CreateLoadout to give a meaningful name:

0 = [// ... loadout code ... //
], "MG Soldier"] call Zen_CreateLoadout;

Now you can refer to that loadout from anywhere using 'MG Soldier'. You can put that name and others into an array:

#define CUSTOM_LOADOUTS ["MG Soldier", "AT Soldier", "SF Soldier"]
// or
_loadouts = ["MG Soldier", "AT Soldier", "SF Soldier"];

You can use the preprocessor to fill in the loadouts in the entire init.sqf file, regardless of scope. You can also make different lists of loadouts, as well as shuffle and sort the loadouts.

hey zen

ive decided to redo the mission from scratch and start with a fresh mind

for the mission ive decided to use events to attempt to accomplish what i want

so far here is what ive got in my init.sqf

this obliviously need work but as they say fail and try again

#include "Zen_FrameworkFunctions\Zen_InitHeader.sqf"

enableSaving [false, false];
if !(isServer) exitWith {};

Event_Arguments_Array = [];
Event_Queue_Array = [];

f_HandleEventQueue = {
   while {true} do {
       sleep 5;
       {
           if (_x select 1) then {
               _eventName = (_x select 0);
               _qualifier = [_eventName, "@$"] call Zen_StringGetDelimitedPart;
               _functionName = _eventName;
               if (count toArray _qualifier > 0) then {
                   _functionName = [_eventName, "@" + _qualifier + "$", ""] call Zen_StringFindReplace;
               };

               _argsIndex = [Event_Arguments_Array, _eventName, 0] call Zen_ArrayGetNestedIndex;
               _args = (Event_Arguments_Array select _argsIndex) select 1;
               0 = _args spawn (missionNamespace getVariable _functionName);
               0 = [Event_Arguments_Array, _argsIndex] call Zen_ArrayRemoveIndex;
               Event_Queue_Array set [_forEachIndex, 0];
           };
       } forEach Event_Queue_Array;
       Event_Queue_Array = Event_Queue_Array - [0];
   };
};

f_EventTrigger_Generic = {
   _nestedArray = [Event_Queue_Array, _this, 0] call Zen_ArrayGetNestedValue;
   if (count _nestedArray > 0) then {
       _nestedArray set [1, true];
   };
};

f_RemoveEvent = {
   {
       _index = [_x, _this, 0] call Zen_ArrayGetNestedIndex;
       if (_index != -1) then {
           0 = [_x, _index] call Zen_ArrayRemoveIndex;
       };
   } forEach [Event_Arguments_Array, Event_Queue_Array];
};

f_EventIsQueued = {
   (([Event_Queue_Array, _this, 0] call Zen_ArrayGetNestedIndex) > -1)
};

0 = [] spawn f_HandleEventQueue;

f_EventAction_SpawnPatrols = {
   _groupArray = [];
   for "_i" from 1 to 3 do {
       _spawnPos = [_this] call Zen_FindGroundPosition;
       _group = [_spawnPos, east, "infantry", [2, 3]] call Zen_SpawnInfantry;
       0 = [_group, east] call Zen_GiveLoadout;
       0 = [_groupArray, _group] call Zen_ArrayAppend;
   };

   0 = [_groupArray, _this] spawn Zen_OrderInfantryPatrol;
};

f_EventAction_FireSupport = {
   _pos = [(_this select 0)] call Zen_FindGroundPosition;
   0 = [_pos, (_this select 1)] call Zen_InvokeFireSupport;
};

_nearestObj = [["mkOreokastro", "mkCastle"], player] call Zen_FindMinDistance;

_nestedArray = [Event_Arguments_Array, "f_EventAction_FireSupport", 0] call Zen_ArrayGetNestedValue;
_argsArray = _nestedArray select 1;
_argsArray set [0, _nearestObj];

_nextObj = [["mkOreokastro", "mkCastle"] - [_nearestObj], player] call Zen_FindMinDistance;
_centerPos = [_nextObj, _nearestObj] call Zen_FindAveragePosition;

_patrolMarker = [_centerPos, "", "colorBlack", [350, 350], "ellipse", 0, 0] call Zen_SpawnMarker;

0 = _patrolMarker call f_EventAction_SpawnPatrols;

{_x setMarkerAlpha 0;} forEach ["mkOreokastro", "mkCastle"];

_players = group UnitFIA_CO;
_players = group UnitFIA_DC;

_obj1Pos = ["mkOreokastro"] call Zen_FindGroundPosition;
_obj2Pos = ["mkCastle"] call Zen_FindGroundPosition;
sleep 1;

_obj2Object = [_obj2Pos, "O_Mortar_01_F"] call Zen_SpawnVehicle;

0 = [Event_Queue_Array, ["f_EventAction_FireSupport", false]] call Zen_ArrayAppend;
0 = [Event_Arguments_Array, ["f_EventAction_FireSupport", ["mkOreokastro", "1"]] call Zen_ArrayAppend;

0 = [Event_Queue_Array, ["f_EventAction_SpawnPatrols@mkOreokastro$", false], ["f_EventAction_SpawnPatrols@mkCastle$", false]] call Zen_ArrayAppend;

#define SPAWN_NEAR(MK) if (([player, MK] call Zen_Find2dDistance) < 1200) then { \
   if (("f_EventAction_SpawnPatrols@" + MK + "$") call f_EventIsQueued) then { \
       0 = [Event_Arguments_Array, ["f_EventAction_SpawnPatrols", MK] call Zen_ArrayAppend; \
       0 = ("f_EventAction_SpawnPatrols@" + MK + "$") call f_EventTrigger_Generic; \
   }; \
}; \

waitUntil {
   SPAWN_NEAR("mkOreokastro")
   SPAWN_NEAR("mkCastle")
};

{
   if (("f_EventAction_SpawnPatrols@" + _x + "$") call f_EventIsQueued) then {
       0 = [Event_Arguments_Array, ["f_EventAction_SpawnPatrols", _x] call Zen_ArrayAppend;
       0 = ("f_EventAction_SpawnPatrols@" + _x + "$") call f_EventTrigger_Generic;
   };
} forEach ["mkOreokastro", "mkCastle"];

if ("f_EventAction_FireSupport" call f_EventIsQueued) then {
   _fireSupport = ["r_80mm_he", 15, 2, 4, 10, 400, 20] call Zen_CreateFireSupport;
   _nestedArray = [Event_Arguments_Array, "f_EventAction_FireSupport", 0] call Zen_ArrayGetNestedValue;
   _argsArray = _nestedArray select 1;
   _argsArray set [1, _fireSupport];

   // Trigger the event itself
   0 = "f_EventAction_FireSupport" call f_EventTrigger_Generic;
};

if this is correct then this should spawn enemies when the players are 1200m away from Oreokastro and the castle

the main part im having trouble understanding is the fire support event

from what ive put and from what i understand is that a morter should spawn at the castle

and iff everything is set up correctly the morter event should only happen when the player is in Oreokasto

now i would also like to add a larger patrol area that covers an area off 1500m - 1000m

in this area i would like to spawn a definite amount of squads plus a chance for a random amount and have all the squads in the master patrol area share info between them selfs

now im guessing the best way to do this would be to exclude these groups from the event queue and have them controlled outside of the events

any help and tips would be greatly appreciated

For the first part with sorting markers, I suggest:

_nearMarkers = [["mkOreokastro", "mkCastle"], compile format ["(getMarkerPos _this) distance %1", getPosATL player]] call Zen_ArraySort;

For the patrols, you can do:

{
   SPAWN_NEAR(_x)
} forEach ["mkOreokastro", "mkCastle"];

However, that waitUntil loop doesn't have a condition to exit, so the mission won't get to check if the fire support is queued. In the demonstration, the fire support event is only removed if a certain action is completed, but I don't see any code that connects destroying the mortar object to removing that fire support.

The demonstration uses actions, but you could do something simpler:

0 = _obj2Object spawn {
   waitUntil {
       sleep 2;
       !(alive _this)
   };

   0 = "f_EventAction_FireSupport" call f_RemoveEvent;
};

If the players manage to destroy the mortar, no fire support can happen.

For spawning other patrols, you can just spawn them normally. The events are only for controlling effects with multiple causes. For sharing info, there is a short demonstration about that. You could expand who the squads can share info about and with.

Share this post


Link to post
Share on other sites

Hi Zen,

I geared up for a playtest of my insurgency mission yesterday, and it started well - everything running like clockwork. But as we played on, we hit an issue - enemy's not spawning. I have been doing some testing, and I can reproduce it, but I am not sure what is causing it.

AT the start of the mission, the squad spawns near a town. This town spawns enemies fine, and the grids can be cleared as intended. When an objective spawns in one of the locations, the squad travels across the map to the town that contains the objective (I have 12 markers labelled SM that I am using to set side objective locations). When they arrive enemies do not spawn. I then tested by flying to the other towns at the start of the mission. In my tests, the nearest town spawns enemies fine, but the other towns do not. I have a very large array at the top of the mission containing the 400x 50x50m grids to spawn the enemies. Is this too large perhaps? My desired outcome is to have the grids spawn enemies if any player is within 700m.

Another issue I noticed in testing is a dependency on some (bit not all) of the AI blufor squad members to be present. I found that if I disable all AI, the mission does not run at all (no zen elements trigger, nothing runs). I suspect this is not a Zen framework issue but more based on my inexperience of editing a mission? My ideal outcome is to have around 16 "playable" slots, but to be able to function with the AI disabled. If, to make JIP easier, leave the AI present is expedient, I can live with this. As usual, any guidance would be gratefully received.

Mission code: http://pastebin.com/6jydhTXp

Full mission files: https://drive.google.com/file/d/0B5q8Njbw82EFcmtXMlBSU3VRdTA/edit?usp=sharing

Share this post


Link to post
Share on other sites
Hi Zen,

I geared up for a playtest of my insurgency mission yesterday, and it started well - everything running like clockwork. But as we played on, we hit an issue - enemy's not spawning. I have been doing some testing, and I can reproduce it, but I am not sure what is causing it.

AT the start of the mission, the squad spawns near a town. This town spawns enemies fine, and the grids can be cleared as intended. When an objective spawns in one of the locations, the squad travels across the map to the town that contains the objective (I have 12 markers labelled SM that I am using to set side objective locations). When they arrive enemies do not spawn. I then tested by flying to the other towns at the start of the mission. In my tests, the nearest town spawns enemies fine, but the other towns do not. I have a very large array at the top of the mission containing the 400x 50x50m grids to spawn the enemies. Is this too large perhaps? My desired outcome is to have the grids spawn enemies if any player is within 700m.

Another issue I noticed in testing is a dependency on some (bit not all) of the AI blufor squad members to be present. I found that if I disable all AI, the mission does not run at all (no zen elements trigger, nothing runs). I suspect this is not a Zen framework issue but more based on my inexperience of editing a mission? My ideal outcome is to have around 16 "playable" slots, but to be able to function with the AI disabled. If, to make JIP easier, leave the AI present is expedient, I can live with this. As usual, any guidance would be gratefully received.

Mission code: http://pastebin.com/6jydhTXp

Full mission files: https://drive.google.com/file/d/0B5q8Njbw82EFcmtXMlBSU3VRdTA/edit?usp=sharing

For the markers array, I suggest:

_townMkArray = [];
for "_i" from 1 to 400 do {
   _townMkArray pushBack ("infmk" + str _i);
};

The JIP code, #include "JIPSync.sqf", (line 80), must be before if (!isServer) exitWith {}; (line 18). The JIP logic there will work for enabled AI, but not for when there are no AI. This should not interfere with players who joined at the start. Also, in JIPSync.sqf, line 19, there is no fastroping, or 11th argument as far as I can tell.

The extra weather code, lines 83-92 is not necessary. They can be put into the main loop to allow weather changes in a long mission. However, you must define (time can be different):

_fogTime = 60*30;
_overcastTime = 60*60;
_fogStart = time;
_overcastStart = time;

f_HandleRespawn must be above 'if (!isServer) exitWith {};' so that it is defined for all clients, as MPRespawn EH does not run on the server (it should, this is a bug for BIS to fix). However, until then, f_HandleRespawn will run on the client that is respawning.

In the description.ext, the framework #includes should be:

#include "Zen_FrameworkFunctions\Zen_LoadoutFunctions\Zen_LoadoutDialog.hpp"
class CfgNotifications {
   #include "Zen_FrameworkFunctions\Zen_TaskSystem\Zen_TaskNotifications.hpp"
};
class CfgCommunicationMenu {
   #include "Zen_FrameworkFunctions\Zen_FireSupportSystem\Zen_FireSupportMenu.hpp"
};

I noticed that the custom loadouts cannot give both magazines and GL shells, nor can you skip a matching magazine with []. I will make both of those improvements to the custom loadouts for next week.

Also, BIS seems to have left the textSingular config entry for the Mi-48 as gunship, so I need to update the StringTable and relevant functions. I don't know why all helicopters can't just be 'helicopter'. This vehicle now spawns without a crew, so you will have to use createVehicleCrew until the next framework release.

On line 286, waiting for the objective to complete is not necessary. It is stopping the while loop, so that it never continues to check the areas or spawn more enemies. Removing this code will allow the loop to function normally:

waitUntil {
   sleep 5;
   ([[(_Objective1 select 1)]] call Zen_AreTasksComplete)
};

Also, _sideObjTime is in seconds, so it should be e.g.:

_sideObjTime = time + (60*([20, 30] call Zen_FindInRange));

To be 20-30 minutes; as currently it creates objectives every 5-10 seconds.

Finally, the mission should not require that the AI exist, but that one player exists (it probably won't work as persistent when no one is playing). Disabling the AI removes their object from the mission, but I don't see any direct references to the players in the init.sqf.

I have tested by putting this at the very top of the init.sqf:

{
   if (_forEachIndex > 0) then {
       deleteVehicle _x;
   };
} forEach units player;

Everything seems to work fine in SP with that.

Share this post


Link to post
Share on other sites

Zen, quick question bro. Do you have a sample mission that uses a non-linear structure as described in your Event Queue Demonstration?

Share this post


Link to post
Share on other sites

ZEN, YOU ARE A LEGEND! That sorted my mission out proper!

Can I ask about the definition of global variables re Loadouts? I know I need to make them global, but I cannot seem to get it working. Code example below, definition is lines 10 - 41, calls are at lines 71 and 160.

URL REMOVED

EDIT: Figured it out! It was a syntax error, by removing the underscore in _loadout2 and the inverted commas from the calls, this now works. I have learned so much in this thread!

EDIT 2: Whoops, no I did not! It works fine when i host locally, but loadouts do not work on the dedicated server. This is twisting my melon! Any advice? Code is here: http://pastebin.com/ZVGvtHze

Edited by CallMeSarge
Going crazy

Share this post


Link to post
Share on other sites
ZEN, YOU ARE A LEGEND! That sorted my mission out proper!

Can I ask about the definition of global variables re Loadouts? I know I need to make them global, but I cannot seem to get it working. Code example below, definition is lines 10 - 41, calls are at lines 71 and 160.

URL REMOVED

EDIT: Figured it out! It was a syntax error, by removing the underscore in _loadout2 and the inverted commas from the calls, this now works. I have learned so much in this thread!

EDIT 2: Whoops, no I did not! It works fine when i host locally, but loadouts do not work on the dedicated server. This is twisting my melon! Any advice? Code is here: http://pastebin.com/ZVGvtHze

Zen_CreateLoadout should be used after both 'if !(isServer) exitWith{};' and 'sleep X;', as it needs to send the loadout information to the clients, but this is not possible (engine limitation with PVEH's) during the briefing.

I believe that using Zen_CreateLoadout before 'if !(isServer) exitWith{};' will work, provided that you give the loadout a fixed named as the second argument. This is not exactly an intended feature, but it works on the same principle as Zen_InvokeTaskBriefing does. If you have a fixed name and data, every machine will record the same values into its local array. However, allowing a random name will never work, as all clients will have the loadout under a different name.

---------- Post added at 19:56 ---------- Previous post was at 18:58 ----------

Zen, quick question bro. Do you have a sample mission that uses a non-linear structure as described in your Event Queue Demonstration?

Yes, I have the mission that inspired the event queue itself, and it is part of a campaign I have been (very slowly) working on, which is currently SP only. Because it is not the first mission in the campaign and the campaign has a little story to it, I will release just the init.sqf with all possible spoilers redacted:

http://pastebin.com/pjjzgc7C

The mission is still a bit WIP, but it is fully playable.

Share this post


Link to post
Share on other sites

Solve a problem, get a problem - no one told me mission making was gonna be so much hard work, lol!

So the loadouts work ok now, thanks Zen! They are not working on respawn, but I think I can solve that.

I hit a bigger issue when playtesting. The mission behaves differently on a dedicated server, to how it behaves when hosted locally. Hosted locally, everything works as intended (this is how I have been doing the testing). On a dedicated machine, I do not think the 10 second loop is running, as you can kill all of the enemies in a grid, but the grid does not go green. As I am pretty new to mission making, is there a difference between missions for local host vs dedicated that I need to accommodate for? As usual, any advice would be welcome.

Share this post


Link to post
Share on other sites

I have a question regarding spawning functions. Is there any possibility to add custom init to spawned units (or just to group leaders)?

Please do not ask why, I just need it.

This framework is masterpiece,thanks for Your hard work ;)

Share this post


Link to post
Share on other sites

Introduction

Greetings fellow scripters and Armaholics, in this latest installment, I will continue to discuss the development of the framework and, of course, shamelessly advertise the framework in any way possible.

If this sounds boring, you can download the latest version from the original post. As always, the link to Google Drive for the .7z and .zip versions are already up to date. For those looking for older versions, go to file>revisions. The new version should be on Armaholic when Foxhound sees this or I PM him. Please bring any technical issues or mistakes to my attention, so e.g. people don't download the wrong version etc.

Changelog

Release #8 marks the passing of 2 months of framework releases; this is also the first release for September. You might notice that amongst the 27 changes, 15 of them are for the new Preprocessor Library that is now a new feature of the framework. This is a new way of helping scripters by automating short code snippets, common actions, and helping with arguments for more complex functions. Instead of full functions, these libraries provide smaller, simpler macros that can represent values and take arguments similarly to functions.

The Preprocessor Library is divided into two parts: framework and standard. Macros in the framework category use and support framework functions, while standard macros are general to the SQF language. The starting line-up of macros has a fair number of each type, and they have been carefully chosen to be useful. These macros represent code that I have written many times and are actually useful in either coding the framework itself or making missions.

Finally, the documentation about the macros appears in its own file, alongside the function category documentation files. This documentation also explains a little about the preprocessor and how to setup and use these macros. Currently, that explanation is somewhat terse, so I might add more substantial document about the preprocessor and the libraries offered by the framework.

Moving on to more usual changes, the StringTable.xml did not have an entry for 'gunship', which I thought was eliminated from the BIS configs. The only vehicle affected is the Mi-48 (all versions); it will now spawn and be given a crew properly.

The various loadout functions tend to make the unit play some sort of animation, such as drawing their weapon etc. To put a stop to this, the loadout functions will now reset a unit's animation after giving the loadout. Regardless of the animation the unit has (e.g. sitting in a vehicle), they will always be returned to that animation without playing any other.

The JIP demonstration has been improved with what is (hopefully) the final part of the JIP jigsaw with the inclusion of a solution for tracking when using any of the framework's unit tracking functions. This is only necessary if you use those functions and have disabled friendly AI. If playable units have AI, the tracking reset will have no effect, as they are already being tracked. Like a lot of JIP, the general solution requires some customization for your mission.

9/3/14

  1. Fixed: Zen_GetUnitLoadout now removes all excess empty strings and arrays, preventing all engine config errors
  2. Fixed: Zen_SpawnVehicleCrew did not put a crew in the Mi-48
  3. Fixed: StringTable.xml now has 'gunship' translations for all languages
  4. Added: Framework and Standard Preprocessor Libraries
  5. Added: Framework macros ZEN_FMW_MP_REA, ZEN_FMW_MP_REC, ZEN_FMW_MP_RED, ZEN_FMW_MP_RES, for MP remote execution of various types
  6. Added: Framework macros ZEN_FMW_MTH_DG2, ZEN_FMW_MTH_DG3, ZEN_FMW_MTH_DL2, ZEN_FMW_MTH_DL3, for distance comparisons of various types
  7. Added: Framework macro ZEN_FMW_MTH_RPC, Random Position within radius from Center
  8. Added: Framework macros ZEN_FMW_MSC_EEV, ZEN_FMW_MSC_EEA, ZEN_FMW_MSC_ES, for error printing of various types
  9. Added: Standard macro ZEN_STD_ARR_RE, Array Random Element
  10. Added: Standard macro ZEN_STD_ARR_RI, Array Random Index
  11. Added: Standard macro ZEN_STD_ARR_UR, Array Unordered Remove
  12. Added: Standard macros ZEN_STD_OBJ_BBX, ZEN_STD_OBJ_BBY, ZEN_STD_OBJ_BBZ, for object model measurements of 3 dimensions
  13. Added: Standard macro ZEN_STD_OBJ_CBP, Count Building Positions
  14. Added: Standard macro ZEN_STD_OBJ_CVS, Count Vehicle passenger Seats
  15. Added: Standard macro ZEN_STD_OBJ_DVC, Delete Vehicle and Crew
  16. Added: Standard macro ZEN_STD_OBJ_VIA, Vehicle Is Armed
  17. Added: Standard macro ZEN_STD_PRS_GOA, Parse Get Optional Argument
  18. Improved: Zen_GiveLoadoutBlufor, Zen_GiveLoadoutCustom, Zen_GiveLoadoutIndfor, and Zen_GiveLoadoutOpfor do not allow the unit to play an animation
  19. Improved: Zen_GiveLoadoutCargo and Zen_GiveLoadoutCustom can now give multiple random matching magazines
  20. Improved: Zen_IsHillArea now uses a better method to determine how much of the area slopes downward from the center
  21. Improved: Zen_SpawnHelicopter can now spawn the Mi-48 at random
  22. Documentation: Added for Preprocessor Libraries
  23. Documentation: Improved Zen_JIP demonstration now has tracking marker reset example
  24. Documentation: Updated Zen_CustomLoadouts demonstration
  25. Documentation: Updated for Zen_ArrayFilterCondition, Zen_GetUnitLoadout
  26. Documentation: Tweaked Zen_EventQueue demonstration
  27. Documentation: Tweaked Notepad++ autocompletion file now uses '(' to open function hints

Roadmap

The only thing planned for next week is the expansion and refinement of the preprocessor library. If you've ever wanted to suggest a major function, now is the time. Mostly I will be focusing on making missions (since that is the point of all this).

Function Spotlight

As users of my framework know, there is an enormous number of functions at your disposal. The amount of documentation that has to be sifted through can be extremely daunting. Each week I spotlight a function and talk about its usefulness. If you have found an obscure function (not in tutorials, barely seen in demonstrations or sample missions) that you think is useful, PM me and I can spotlight it.

The function chosen for this week is: Zen_ReassignTask. This function can simultaneously remove a task from some units, while giving it to others.

Tasks are treated in an object-oriented way, with their names being strings. Thus, every task system function can work together to manipulate global task entities. This means that using any combination of functions will modify every local instance of that task for all objects on all machines.

You can use Zen_ReassignTask to simply add the task to another group of units. For example, if group A fails their objective, give the task to group B:

if ({alive _x} count units A == 0) then {
   0 = [_task, group B] call Zen_ReassignTask;
};

Also, in PvP style missions, if the Blufor team e.g. captures a supply depot, you could easily reverse the task so the Opfor team is told to retake it:

0 = [_taskSecureDepot, east, west] call Zen_ReassignTask;

The exact same task will them seamlessly swap sides and be shown to the players as either 'task removed' or 'task created'. This also prevents long lists of tasks from cluttering players' logs if the same objective was taken 10 times. You could go even further and have a defend the depot task that is swapped:

0 = [_taskDefendDepot, west, east] call Zen_ReassignTask;

Currently, neither task will ever be able to succeed or fail, they would always be incomplete. However, they still simply and effectively represent one of the goals of the player's team in the mission.

Beta

As already stated, the framework is in the beta stage of development. I am making every effort to quality control releases, but there will be bugs. Both new features and old ones could have bugs, issues, and things people just don't like.

There is no ETA or plan for a 'final' version. The framework is a work in progress, and it will continue to progress while there are improvements to be made (there always will be).

Feedback

As expected, some of the bugs have been pointed out by users, and those fixes are included in the changelog above. I want to thank everyone who has used/supported the framework.

---------- Post added at 01:11 ---------- Previous post was at 23:29 ----------

Solve a problem, get a problem - no one told me mission making was gonna be so much hard work, lol!

So the loadouts work ok now, thanks Zen! They are not working on respawn, but I think I can solve that.

I hit a bigger issue when playtesting. The mission behaves differently on a dedicated server, to how it behaves when hosted locally. Hosted locally, everything works as intended (this is how I have been doing the testing). On a dedicated machine, I do not think the 10 second loop is running, as you can kill all of the enemies in a grid, but the grid does not go green. As I am pretty new to mission making, is there a difference between missions for local host vs dedicated that I need to accommodate for? As usual, any advice would be welcome.

If enemies spawn, the loop is somewhat functional, so the issue could be with just turning the markers green. You can add some debug to see if the server ever runs that code:

        if ([_groupsArray, _x] call Zen_AreNotInArea) then {
           _x setMarkerColor "colorGreen";
           diag_log (format["Marker %1 clear, changing color to green", _x]);
          // ...

The server's log location depends upon how you set up the dedicated server. For me it is here (Windows 7) C:\Program Files (x86)\Steam\steamapps\common\Arma 3\Server.

Based upon these wiki pages:

https://community.bistudio.com/wiki/setMarkerColor

https://community.bistudio.com/wiki/setMarkerColorLocal

The command should have global effect.

---------- Post added at 01:19 ---------- Previous post was at 01:11 ----------

I have a question regarding spawning functions. Is there any possibility to add custom init to spawned units (or just to group leaders)?

Please do not ask why, I just need it.

This framework is masterpiece,thanks for Your hard work ;)

By custom init I assume you mean running some code on each unit. You don't need a special init field for each unit, because you can code whatever you need immediately after spawning them.

_group = [player, west, 0.5, 4] call Zen_SpawnInfantry;

{
   if (_x == leader _group) then {
       // ...
   } else {
       // ...
   };
} forEach units _group;

If you have some code that you always run for units, then make it a function:

f_InfantryInit = {
   // ...
};

then:

_group = [player, west, 0.5, 4] call Zen_SpawnInfantry;
[_group] call f_InfantryInit;

You can do anything to a spawned unit that you can do in the editor. Such as give the group leader a name that all clients can see:

X = leader _group;
publicVariable "X";

  • Like 1

Share this post


Link to post
Share on other sites
Introduction

If enemies spawn, the loop is somewhat functional, so the issue could be with just turning the markers green. You can add some debug to see if the server ever runs that code:

        if ([_groupsArray, _x] call Zen_AreNotInArea) then {
           _x setMarkerColor "colorGreen";
           diag_log (format["Marker %1 clear, changing color to green", _x]);
          // ...

The server's log location depends upon how you set up the dedicated server. For me it is here (Windows 7) C:\Program Files (x86)\Steam\steamapps\common\Arma 3\Server.

Based upon these wiki pages:

https://community.bistudio.com/wiki/setMarkerColor

https://community.bistudio.com/wiki/setMarkerColorLocal

The command should have global effect.

Thanks, I will try that out. I have been thinking about this a lot this morning (2 hour drive to the office today) and I have a theory - cos everyone loves theory crafting, right? Here goes...

So the loop is currently checking 400 individual markers to see if the player is close to them every 10 seconds, and spawning infantry if they are - 2x patrols of 1-2 men for every 50m2 grid. I notice in testing the spawning can be pretty slow. Perhaps I am asking too much of the loop every 10 seconds?

So my thinking is (after I check the logs) maybe changing the way the spawning areas work. Using a 100m2 grid would reduce me to 100 grids total, probably reducing the processing overhead, whist still maintaining similarity to the original insurgency (Pogoman & Fireball & Kol9yN) - or maybe I just go back to using the original 200m elliptical grids centered on towns. I guess I am just thinking out loud now...

Share this post


Link to post
Share on other sites

Thank You very much ;)

Answer is very simple and working flawessly :)

Share this post


Link to post
Share on other sites
Thanks, I will try that out. I have been thinking about this a lot this morning (2 hour drive to the office today) and I have a theory - cos everyone loves theory crafting, right? Here goes...

So the loop is currently checking 400 individual markers to see if the player is close to them every 10 seconds, and spawning infantry if they are - 2x patrols of 1-2 men for every 50m2 grid. I notice in testing the spawning can be pretty slow. Perhaps I am asking too much of the loop every 10 seconds?

So my thinking is (after I check the logs) maybe changing the way the spawning areas work. Using a 100m2 grid would reduce me to 100 grids total, probably reducing the processing overhead, whist still maintaining similarity to the original insurgency (Pogoman & Fireball & Kol9yN) - or maybe I just go back to using the original 200m elliptical grids centered on towns. I guess I am just thinking out loud now...

Using a more formal analysis of performance, the work done in checking and spawning enemies in the markers increases by O(n) as you add markers.

https://en.wikipedia.org/wiki/Big_O_notation

However, math functions dealing with markers (e.g. Zen_IsPointInPoly) are O(1) in regards to the size of the marker. Zen_SpawnInfantry would be nearly O(1) for small numbers (less than 10) of units, but in general it uses Zen_SpawnGroup, which is about O(n) for many units. Loadout function are also about O(n). Also, every marker spawned creates a Zen_OrderInfantryPatrol thread (unsurprisingly O(n)), which runs every ten seconds to check on the groups.

Thus, spawning fewer groups of more units in fewer markers should improve performance. Decreasing the number of markers will about linearly increase performance (at 200 markers it will take half as long, etc.). It will speed up not only the spawning, but checking for the enemy to be dead, as fewer markers means fewer arrays of groups to check on.

However, mission performance may not noticeably improve. The work done to spawn and check on the areas is done every 10 seconds, but any AI spawned will require constant management by the engine. Spawning 100-200 AI will always decrease performance more than any script with a reasonable sleep in it.

Share this post


Link to post
Share on other sites

Hardcore Math!

So, I reduced from 400 markers to 55 markers, and there was a marked improvement! I have now completed a first successful multiplayer test - and it feels great! I only have a couple more issues to resolve, and as usual, I need some advice.

I am now placing 1 or 2 markers per town, spawning 2 squads of 4-8 infantry for each marker. However, some grids are not clearing when the area is "secure". Other times the grids clear fine. My supposition is that one lone infantry survivor has run to the hills and since he cannot be located, the grid remains uncleared. This is based on seeing infantry a long way from the spawning grid. I can see a few possible solutions here. I remember in the tutorials, there was a way to attach a marker to an infantry patrol, so you can see its location - I assume if all but one were dead, then this marker would show the lone survivors position - is that correct? The other approach would be to detect if a spawned unit is > 600m from its spawn area, then to clear the grid. Finally, would it be possible to force the infantry to stay close to the spawning areas? Any opinion of which method is easiest to achieve?

In the multiplayer play test, the amount of enemies was perfect for 4 players. It would be useful if I could scale up the number of opfor in response to the number of players. Is this best achieved via a setting in description.ext that lets the admin select it at the start of the mission, or is it better handled in the init.sqf?

Finally, I am still struggling with custom load-outs for players - for some reason, when the player respawns they do not work (repack and give mag works fine though). The respawn call is in line 46 and I have declared the loadout as a global variable in line 93. Any idea how i can resolve this? Init.sqf here. Any advice would be appreciated.

Edited by CallMeSarge
Added more detail

Share this post


Link to post
Share on other sites
Hardcore Math!

So, I reduced from 400 markers to 55 markers, and there was a marked improvement! I have now completed a first successful multiplayer test - and it feels great! I only have a couple more issues to resolve, and as usual, I need some advice.

I am now placing 1 or 2 markers per town, spawning 2 squads of 4-8 infantry for each marker. However, some grids are not clearing when the area is "secure". Other times the grids clear fine. My supposition is that one lone infantry survivor has run to the hills and since he cannot be located, the grid remains uncleared. This is based on seeing infantry a long way from the spawning grid. I can see a few possible solutions here. I remember in the tutorials, there was a way to attach a marker to an infantry patrol, so you can see its location - I assume if all but one were dead, then this marker would show the lone survivors position - is that correct? The other approach would be to detect if a spawned unit is > 600m from its spawn area, then to clear the grid. Finally, would it be possible to force the infantry to stay close to the spawning areas? Any opinion of which method is easiest to achieve?

In the multiplayer play test, the amount of enemies was perfect for 4 players. It would be useful if I could scale up the number of opfor in response to the number of players. Is this best achieved via a setting in description.ext that lets the admin select it at the start of the mission, or is it better handled in the init.sqf?

Finally, I am still struggling with custom load-outs for players - for some reason, when the player respawns they do not work (repack and give mag works fine though). The respawn call is in line 46 and I have declared the loadout as a global variable in line 93. Any idea how i can resolve this? Init.sqf here. Any advice would be appreciated.

The check for grids being clear is that no enemy are inside:

if ([_groupsArray, _x] call Zen_AreNotInArea) then {

This should be true if the enemy have fled the area. You can debug where exactly they are by putting this in the spawning part below the orders to patrol:

0 = [_groupsArray] call Zen_TrackInfantry;

If all of the AI have left the area, but it doesn't complete in 10 seconds, then there is some bug that is causing some areas not to be checked. Zen_OrderInfantryPatrol should force the AI to stay in the area as much as possible, if that fails nothing can stop the AI from fleeing.

For mission params, these take longer to setup but make your mission more flexible and user friendly, but they might let players make the mission too easy or too hard. If you prefer that your mission play at a certain difficulty in all circumstances, you can scale difficulty with code. You already have these lines:

_players = [west] call Zen_ConvertToObjectArray;
_players = [_players, "!(isPlayer _x)"] call Zen_ArrayFilterCondition;

So when spawning enemy groups:

// Spawn 2 or 3 groups per marker
for "_i" from 1 to (if (count _players > 4) then {3} else {2})  do {

Also, if you want AI to be included for purposes of spawning markers, use this line:

_playersAndAI = [_players, "{isPlayer _x} count units _x == 0"] call Zen_ArrayFilterCondition;

For the loadouts, the preprocessor is top-down, so you need to put definitions above where they are used in the file:

#define WMDLOADOUT2 [loadout2]
f_HandleRespawn = {
   // ...
};

Regardless of when f_HandleRespawn is called, the preprocessor only parses it when it is defined.

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

×