Jump to content

Recommended Posts

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

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 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

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.

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

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

@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. 😃

 

  • Like 1

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

×