Jump to content


  • Content count

  • Joined

  • Last visited

  • Medals

Community Reputation

91 Excellent


About Zenophon

  • Rank
    Gunnery Sergeant

Profile Information

  • Gender
  • Location
  1. 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.
  2. Having to restart the mission has been an issue for a while; the first time the mission is run there is a varying delay in the server sending messages to clients. Having the admin use #restart in the console once the briefing is loaded will fix it. As far as I can tell this is due to some quirk of dedicated servers that only BI can fix. The radioman is chosen at random from all Opfor that aren't in a vehicle. I was a bit lazy and didn't give him specific equipment; he might be patrolling the streets or in a building. I think the radioman concept was meant to make the mission a little easier by having an increasing chance of stopping the mortars as the town is cleared; the difficulty of the mission is meant to decrease as players eliminate Opfor vehicles and reinforcements and secure more of the town.
  3. 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).
  4. 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.
  5. 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.
  6. 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).
  7. 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.
  8. You would need the equations for the sides of the rectangle. I think the best way would be to step around the sides from the corners and mirror each position. Note that I'm using Zen_ExtendVector from my framework, but any equivalent vector addition command will do. Note that I did not test this, so I might have made a mistake with the angles or something. If it draws a rectangle at the wrong rotation, first try swapping _a and _b.
  9. As another reference, look at the init of my Evade and Survive mission; the mission is spawning enemies infinitely around the player with a cap on concurrent enemies. For any long-term, ambient spawning, you would take the usual spawning code and put into a loop with some timer/limit to space out the spawns. You can change the random position's parameters to suit your mission (e.g. within markers, around an object, etc.); giving an object as the parameter will use the position of that object at the time the code runs. For patrolling those groups, I suggest either terminating and recreating the patrol thread for patrols in the same area (see Multi thread management demonstration), or using a single threaded patrol manager (you can try my multi patrol function in the release #48 post as a starting point). Also, see the FAQ for an explanation of Zen_SpawnInfantry's unit selection arguments. You might also be interested in the custom loadout system; there is a demonstration for it. For the cleanup of dead bodies, you could put an EH on each unit and delete them with a timer, or you could make cleanup 'sweeps' on an interval, e.g. Many orders functions, like Zen_OrderInsertion and Zen_OrderHelicopterLand recommend that you use 'spawn' to invoke them. This is because they have a long execution time (typically dependent on how long the AI takes to complete the action) that will interrupt the current thread. This means that calling or spawning more than one of them will cause overlapping orders and conflicting decisions for the AI. For a detailed look see the multi thread mangement demonstration and the 'Multi-Threading and Spawn vs. Call' section of the SQF overview. In short, you need to use 'spawn' for the order, save the thread handle (the return value of 'spawn'), and then use 'terminate' to stop that thread from running before issuing new orders. This is true for both procedural (common top-down execution) and event-driven (i.e. the actions you're using) styles, e.g. // the player uses the insertion action/command h_heliOrders = [[Maeh_transSys_insertionHeli, [Maeh_transSys_LZMarker, "TransportSystem_Spawn"], playableUnits, "normal", 50, "land", false] spawn Zen_OrderInsertion; and in another script/thread/EH/action //... the player selects another command // note that checking for scriptDone is not strictly necessary, but allows you flexibility to e.g. let the previous script finish etc. if !(scriptDone h_heliOrders) then { terminate h_heliOrders; }; // now we have no previous conflicting orders, and the thread variable/pointer is updated h_heliOrders = [[Maeh_transSys_insertionHeli, "TransportSystem_Spawn", "normal", 50, false, false] spawn Zen_OrderHelicopterLand; Depending on your system's structure, h_heliOrders can be global, local, assigned to an object, put into a larger data structure etc. Note that putting 'h_' first is just my personal style to denote a script handle and help me keep track of them in the code. To repeat the documentation I referenced, threads are global on each machine and cannot be transfered or defined remotely. Finally, the use of spawn to get a thread is distinct from using spawn to get scheduled execution, but if you are using EH's the spawn will also be necessary for that reason (see the FAQ for a discussion of that).
  10. According to the changelog, the release on 11/20/16 included this fix to Zen_SpawnInfantryGarrison; I've checked the current release and it definitely has it (there have occasionally been code and changelog discrepancies). Thank you for the detailed bug report so I could verify that.
  11. Update Overview Greetings fellow Armaholics, this release fixes an issue with playable slots not spawning the unit in some missions (resulting in the player controlling a seagull). Changelog 8/11/17 Fixed: Non group leader slots could not be used
  12. I can reproduce this issue and I'll be releasing a fix shortly. The solution, for anyone having the same issue, is to delete and recreate every playable unit in the editor (except the group leaders). I have no idea why this fix works.
  13. You'll have to write a function that creates the jet, makes it fly over, etc. and then given the name of your custom function as the second argument to Zen_AddSupportActionCustom. The documentation for Zen_AddSupportActionCustom lists the arguments that will be passed to your function. Note that you could even call a regular fire support (from Zen_CreateFireSupport) manually in your custom function to do the actual explosion (and the jet would just be for visual effect).
  14. Update and Release #51 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 links 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 will be on Armaholic soon. Please bring any technical issues or mistakes to my attention, so e.g. people don't download the wrong version etc. Changelog This release falls on the third anniversary of the framework's first release; thusfar, I have counted 806 total changes to the framework. I am truly grateful for all of the comments and suggestions that allowed for so many fixes, improvements, and additions. The vast majority of what I had planned to do is finished; I will continue to maintain the framework with bugfixes and small improvements. Added in this release is Zen_ArrayRemoveType, which is basically the remove version of Zen_ArrayGetType. This might be a useful shortcut instead of writing a code condition for Zen_ArrayFilterCondition; it also searches recursively and should be slightly faster. Now that BIS have allowed ctrlCreate to use control classes defined in the mission's .ext file, the dialog system can take advantage of that to be on truly equal footing with tradition dialog creation. Zen_InvokeDialog will look for the type of defined control and apply properties appropriately (e.g. types 1, 11, 16, and 41 are considered buttons). The template subsystem is now tied into the action system, with Zen_CreateTemplate and Zen_SpawnTemplate being able to copy-paste existing actions onto the new objects. This greatly reduces the hassle of manually applying your actions to the correct spawned object. 7/10/17 New Function: Zen_ArrayRemoveType Fixed: Zen_ExtendVector did not use relative height if its second argument was a vector Fixed: Zen_RefreshDialog did not remove controls properly in some cases Improved: Zen_CreateControl now allows mission defined control classes as the first argument Improved: Zen_CreateTemplate and Zen_SpawnTemplate now copy actions added using the framework's action system Documentation: Added for Zen_ArrayRemoveType Documentation: Updated Notepad++ SQF language and autocompletion file with ArmA 1.72 stable commands
  15. The framework's task system appears to fail when all objects assigned to a task are deleted (like when the players leave) because it automatically deletes tasks for which there are no units. This is done because there is no direct command to remove the data entry itself, only units that have the task. This is most likely the issue if you are testing the JIP code alone by joining, leaving, then joining your persistent server with team AI disabled. To account for this, you need to manually create a proxy unit that will hold the task for players. Any invisible/disabled proxy AI unit (preferably local to the server) somewhere on the map will work; you would include this unit with the players when you use Zen_InvokeTask (or you could use Zen_ReassignTask to this unit when some players still exist). You can then use Zen_ReassignTask normally for this task on JIP players.