blackharaz 12 Posted June 14, 2022 I'm gonna be crazy with sqf. 🤯What's my goal? Spoiler I want to implement in our trainingworld a full automated shooting range. I want it to keep it as simple as possible to our user. For Example the "pistol range": The player step up to the laptop and pick exercise 1. Then he have 15 seconds to get in position. When the exercise starts it will pick one of three random targets and pop it it. When it got a hit it will pop down and the next randomly target pop up. After 5 hits the exercise stop and the player will get informations like - time used to complete - how many miss That it. After that he can pick another exercise with more targets or more hits to complete it. If he don't pick an exercise he just can shot and all targets are in "default mode" like placed without scripts etc. How did I tried to get that? I've tried it in different ways and my best try was like that: Initialization: Spoiler // array with targets in arrShootP1 = [ shootP_1, shootP_2, shootP_3 ]; // variables (global because I want to add more ranges and I want to initialize all in this file) arrShootPlayerPistol = list trgShootPistol; // "trgShootPistol" is a trigger at the point the player have to be to do this exercise shootFiredP = 0; // counter to count how often player was firing during this exercise on pistol range shootHitP = 0; // counter to count the hits at pistol range nopop = false; // deactivate targets to get up again automaticly {_x animate ["terc",0]} forEach arrShootP1; // lay targets down // add mpEventHandler to targets {_x addMPEventHandler ["MPHit", { shootHitP = shootHitP +1; // increase the hit counter } ]; } forEach arrShootPlayerPistol; Up to here it works fine. Now I want to include the exercise to the addAction of the laptop and put it in another .sqf. The Exercise (and the problem): Spoiler // variables _countdown = 16; {"Begeben Sie sich zu Ihrer Schießbahn!" remoteExec ["hintSilent", _x];} forEach arrShootPlayerPistol; sleep 5; while {_countdown > 1} do { _countdown = _countdown -1; {format ["Die Runde beginnt in %1 Sekunden",_countdown] remoteExec ["hintSilent", _x];} forEach arrShootPlayerPistol; sleep 1; }; /* * At this point it calls a stopwatch script to start. That works fine */ // add eventHandler to trigger the fire arrShootPlayerPistol select 0 addEventHandler ["Fired", { shootFiredP = shootFiredP +1; // increase counter ehFiredPlayer = _this select 0; ehFiredIndex = _thisEventHandler } ]; while {shootHitP <= 5} do { shootTargetP = selectRandom arrShootP1; // pick a new random target shootTargetP animate ["terc",0]; // pop this target up /* * And here I need something that he'l wait until the next target will been shot down. * I tried it with an waitUnti order but it seems I'm to stupid to do it that way. =( * I also tried to implement it in the mpEventHandler "MPHit" and get that * in a while {shootHitP1 <= 5} do {eventHandler} but then I also get an error. =/ */ }; ehFiredPlayer removeEventHandler ["Fired", ehFiredIndex]; // Remove eventHandler from player /* * Here will time stops and get the result of the exercise to the player. * That works */ Any idea? If I now post all my tries I think it take to long. I think I got it as compact as possible to understand what I've done. That's just to one range. There are much more in the initializing but I deleted it here because not necessary at this point. I've no errors until I try to implement a number of maximum targets. 😞 Share this post Link to post Share on other sites
blackharaz 12 Posted June 14, 2022 Okay... I tried it out again with "waitUntil" but I think I'm to stupid to get it. 😣 shootTargetP = selectRandom arrShootP1_1; shootTargetP animate ["terc",0]; while {shootHitP1 <= 4} do { waituntil {shootTargetP1 animationPhase "terc" == 1}; sleep 0.5; shootTargetP = selectRandom arrShootP1; shootTargetP animate ["terc",0]; }; But it don't wait. 😕 If I'll check this order in debug menu ingame it returns "0" or "1" but my "waitUntil" doesn't care about it and I'll get the error that there is a boolean expected. But "animationPhase" does not returns a boolean. 😓 Share this post Link to post Share on other sites
Larrow 2814 Posted June 15, 2022 Maybe you can pull some ideas from this Share this post Link to post Share on other sites
blackharaz 12 Posted June 15, 2022 Just now, Larrow said: Maybe you can pull some ideas from this I'll take a look. Thx in advance. ☺️ Share this post Link to post Share on other sites
blackharaz 12 Posted June 15, 2022 @Larrow Okay. At first I wanted to thank you again. I think this can help but I forget to mention that I'm an absolutly beginner in scripting with sqf. 😅 I have an absolutly basic knowledge about html, vba and now I'm trying me out in mission editing with sqf since a few weeks while I'm on shift work. That means: I know the most basic commands and the last days I've learned by myself how to handle eventhandlers and BIS functions. I stare on this code which only consists of functions and my brain turns to applesauce ( ⬅️ German idiom ) 🤯🤪. I understand nearly 20% of this code and another 20% I can piece together but I can't find the command to solve my issue. Or in other word: I've found what looks like that in 'rangeMacros.hpp' but I don't understand it. 😓 I think the solution to my problem is anywhere in this code here below. Spoiler #define TARGET_UP( T ) \ T animate [ "terc", 0 ]; \ T addEventHandler [ "Hit", { \ params[ "_target", "_causedBy", "_damage", "_instigator" ]; \ if ( _instigator isEqualTo ( _target getVariable "owner" ) ) then { \ TARGET_DOWN( _target ); \ _thread = [ _target ] spawn { \ params[ "_target" ]; \ waitUntil { diag_log format[ "waiting for down on %1", _target ]; IS_TARGET_DOWN( _target ) }; \ diag_log format[ "target down %1", _target ]; \ [ "STOP", _target ] call LARs_fnc_moveTarget; \ }; \ }; \ }]; \ [ "START", T ] call LARs_fnc_moveTarget; \ TARGET_WAIT( T ) //[ "STOP", T ] call LARs_fnc_moveTarget #define TARGET_DOWN( T ) \ T animate [ "terc", 1 ]; \ T setVariable [ "_owner", nil ]; \ if !( isNil "_thisEventHandler" ) then { \ T removeEventHandler [ "Hit", _thisEventHandler ]; \ } #define IS_TARGET_UP( T ) \ T animationPhase "terc" isEqualTo 0 #define IS_TARGET_DOWN( T ) \ T animationPhase "terc" isEqualTo 1 #define TARGET_WAIT( T ) \ waitUntil{ IS_TARGET_UP( T ) }; \ waitUntil{ IS_TARGET_DOWN( T ) } It seems to me that this are the defines used here: Spoiler #include "..\functions\RangeMacros.hpp" params[ "_laptop", "_owner", "_mode" ]; RANGE_VARS( _laptop ); //_rangeLogic - OBJECT master logic of range //_modeLogics - ARRAY of mode logics //_modes - ARRAY of STRINGs available range modes //_targets - ARRAY of ARRAY of OBJECTs all targets of range in mode order //_trigger - TRIGGER of shooting mat //_rangeSuffix - STRING suffix of range ie laptop is called laptop_1 suffix is "1" //_modeLogic - OBJECT logic of current range mode - OBJNULL if no logic associated with mode //_modeTargets - ARRAY of mode targets, [] if mode is not a logic mode hint format[ "Five random %1", _mode ]; sleep 3; ROUND_START(); _startTime = diag_tickTime; { _roundTargets = +_modeTargets; while { count _roundTargets > 0 } do { _target = _roundTargets deleteAt floor random count _roundTargets; TARGET_UP( _target ); sleep ( random 3 ); }; }forEach [ 1, 2, 3, 4, 5 ]; ROUND_END(); _timeTaken = diag_tickTime - _startTime; hint format[ "15 targets in %1", _timeTaken ]; RANGE_RESET(); But there is much I don't understand. 🤔 For example: In the function 'fn_modeShort.sqf' is 'TARGET_UP( _target );' used in line 27. In the 'RangeMacros.hpp' which is included in 'fn_modeShort.sqf' I find '#define TARGET_UP( T ) \'. Now I start to have a few questions. 😂 Is the '( T )' equal to '( _target )' and if so why he also use '_target' in the same scriptblock he's using 'T'? Are things like 'TARGET_UP( _target );' or 'ROUND_END();' like goTo-Points or are they function calls or anything else? 🤨 I'm really confused. 😂 Share this post Link to post Share on other sites
Larrow 2814 Posted June 15, 2022 Its something I wrote for a friend 5 years ago to allow him to easily create modes for a firing range, with very little scripting knowledge. So I hid all the code that he didn't really need to care about in macros. All the stuff in RangeMacros.hpp are, well macros. You can think of them a little like functions but when you use one ie TARGET_UP( _target ) all the code from the macro is actually placed in the place where the macro was used, when the scripts are compiled by the engine. Every where you see T in that macro, will be replaced with _target. 7 hours ago, blackharaz said: if so why he also use '_target' in the same scriptblock he's using 'T'? Its actually not, _target in use in that macro is actually defined inside an eventhandler... T addEventHandler [ "Hit", { ...so is a completely different scope of code. 7 hours ago, blackharaz said: Are things like 'TARGET_UP( _target );' or 'ROUND_END();' like goTo-Points or are they function calls or anything else? As I wrote above, when the engine encounters one of these macros when parsing the files, the code in the macro is inserted in place of the macro and any variables in the macro like T are replaced with what was passed to the macro TARGET_UP( _target ) so _target. So this... Spoiler hint format[ "Five random %1", _mode ]; sleep 3; ROUND_START(); _startTime = diag_tickTime; { _roundTargets = +_modeTargets; while { count _roundTargets > 0 } do { _target = _roundTargets deleteAt floor random count _roundTargets; TARGET_UP( _target ); sleep ( random 3 ); }; }forEach [ 1, 2, 3, 4, 5 ]; ROUND_END(); _timeTaken = diag_tickTime - _startTime; hint format[ "15 targets in %1", _timeTaken ]; RANGE_RESET(); ...once parsed by the engine would actually look like... Spoiler hint format[ "Five random %1", _mode ]; sleep 3; //ROUND_START(); is replced by below hint "Get Ready!!"; playSound "roundStart"; { hint format[ "Starting in %1", _x ]; sleep 1; }forEach [ 3, 2, 1 ]; hint "GO!!"; //End macro _startTime = diag_tickTime; { _roundTargets = +_modeTargets; while { count _roundTargets > 0 } do { _target = _roundTargets deleteAt floor random count _roundTargets; //TARGET_UP( _target ); is replaced by _target animate [ "terc", 0 ]; _target addEventHandler [ "Hit", { params[ "_target", "_causedBy", "_damage", "_instigator" ]; if ( _instigator isEqualTo ( _target getVariable "owner" ) ) then { //TARGET_DOWN( _target ); //This would also be replaced by the code given in macro TARGET_DOWN _target animate [ "terc", 1 ]; _target setVariable [ "_owner", nil ]; if !( isNil "_thisEventHandler" ) then { _target removeEventHandler [ "Hit", _thisEventHandler ]; }; //End of macro TARGET_DOWN _thread = [ _target ] spawn { params[ "_target" ]; waitUntil { diag_log format[ "waiting for down on %1", _target ]; //IS_TARGET_DOWN( _target ) replaced by _target animationPhase "terc" isEqualTo 1; //End macro }; diag_log format[ "target down %1", _target ]; [ "STOP", _target ] call LARs_fnc_moveTarget; }; }; }]; [ "START", _target ] call LARs_fnc_moveTarget; //TARGET_WAIT( T ) //This would also be replaced by the code given in macro TARGET_WAIT waitUntil{ //IS_TARGET_UP( _target ) //Which these would also get replaced _target animationPhase "terc" isEqualTo 0 //End macro }; waitUntil{ //IS_TARGET_DOWN( _target ) //Which these would also get replaced _target animationPhase "terc" isEqualTo 1 //End macro }; //End macro TARGET_WAIT //End macro TARGET_UP sleep ( random 3 ); }; }forEach [ 1, 2, 3, 4, 5 ]; //ROUND_END(); replaced by playSound "roundEnd"; //End macro _timeTaken = diag_tickTime - _startTime; hint format[ "15 targets in %1", _timeTaken ]; //RANGE_RESET(); replaced by [ _laptop, _owner, true ] call LARs_fnc_resetRange; //End macro Notice how compared to the original it removes a lot of the unnecessary code my friend had no need/willingness to deal with 😄 so I hid it behind explicitly named macros. 1 1 Share this post Link to post Share on other sites
blackharaz 12 Posted June 15, 2022 Yeah. I see if I want to code my missions better I have a lot to learn. ^^ I think I'll take a longer look at your code to learn how to do it better. =P It's a nice scripted firing range and if I understood this script right it's so easy to set up that absolutly beginner can use it to build in short time a nice firing range. You also have moving targets like the range in Old man and I search for a script to do that for long time. 😃 Share this post Link to post Share on other sites
blackharaz 12 Posted June 17, 2022 @Larrow At first: I'm really thankful. You solved my problem. 😃 I started rewriting the script from scratch to avoid previous errors and your script helped me a lot to understand what was going wrong.🤗 nopop = true; testHit = 0; testTargets = [ testTarget_1, testTarget_2, testTarget_3 ]; {_x animate ["terc", 1]} forEach testTargets; while {testHit < 5} do { testTarget = selectRandom testTargets; testTarget animate ["terc", 0]; testTarget addMPEventHandler ["MPHit", { testHit = testHit +1; hint format ["testHit = %1", testHit]; testTarget removeMPEventHandler [_thisEvent, _thisEventHandler]; }]; waitUntil {testTarget animationPhase "terc" isEqualTo 0}; waitUntil {testTarget animationPhase "terc" isEqualTo 1}; }; hint "End doWhile" It's now working like that what I wanted it to do. Thank you so much. 😃 I also thank you for your patience. I said I'm a beginner in scripting and if that bothered you, I'm sorry. 😅 Up to here I've really learned a lot of things from your code. Not only to get my targets up. 😃 1 Share this post Link to post Share on other sites