Jump to content
leecarter

Finally got Artillery working right!

Recommended Posts

EDIT: More refined and smarter script 2 posts down.

I looked high and low for a way to get Artillery to fire correctly but couldn't find it. Maybe this is somewhere else, but I don't know where.

I wanted a script that:

  • Allowed the artillery to fire on *any* enemy that strays into it's marked area of defense.
  • Made the artillery piece re-adjust it's aim before every shot for moving targets.
  • Was smart enough not to fire at aircraft.

I tried the approach most people use with mortars, but it wasn't working with artillery pieces and the MLRS. It took me hours of trial and error, but I finally figured out you have to hold the artillery piece's hand and tell it to reload and then wait for it to finish reloading.

When you run it, you'll see the piece fire, put it's cannon or MLRS launcher down while it's reloading (10 secs default), then put it back up again and fire - wash and repeat. The range has to be correct, if it's too far or too close it won't fire. You can cut the reload time down with the MLRS, but it seems to get a little buggy if you do. Actually, the MLRS just seems to be a little buggy period.

It takes a while for the rounds to reach their targets, so if you have something trucking through the target area the shells are going to lag pretty far behind it, but they will follow the target.

Here's the main script, I called it fireArty.sqf:

_arty = _this select 0;
_targetArray = _this select 1;

_target  = _targetArray select 0;
_theAmmo  = getArtilleryAmmo [_arty] select 0;
_idx = 0;

//10 seconds to reload is for the
//longest reloading artillery piece. 
//MLRS can reload faster, but they get buggy.
_reloadTime = 10;

// The number of times you want the _arty to check for range
// and fire (if in range).
_shellCount = 10;

_arty addMagazineTurret [_theAmmo, [0]];
sleep _reloadTime;

// prevents firing at aircraft
if(isTouchingGround _target ) then {	

while {(_idx < _shellCount)} do{
      _tgtPos = position _target;
      _isInRange = _tgtPos inRangeOfArtillery [[_arty], _theAmmo ];

     if (_isInRange) then {
	_arty doArtilleryFire [_tgtPos, _theAmmo, 1]; 
	_arty addMagazineTurret [_theAmmo, [0]];
               sleep _reloadTime;
           } else {
	//Try moving the target area marker	
	hint "Too close or far for artillery";
	_idx = _shellCount;				
     };

   _idx = _idx + 1;
};	
};

You can change the values at the top for whatever piece you're using if you wanted to.

To set it up, make an artillery piece and name it whatever you want. In this example I used arty1.

Then make a trigger that goes off repeatedly any time an enemy enters and put this in the on act section:

script=[arty1, thislist] execVM "fireArty.sqf"; 

You can reuse this script with as many different artillery and MLRS pieces and as many different targets as you want, so:

script=[arty1, thislist] execVM "fireArty.sqf"; 

script=[arty2, thislist] execVM "fireArty.sqf"; 

script=[arty3, thislist] execVM "fireArty.sqf"; 

Would all be valid as actions for targets assuming you created artillery pieces with those names.

Enjoy! :)

Edited by leecarter

Share this post


Link to post
Share on other sites

Thanks for letting me see this, I got it working and figured how to change how many missiles are fired to get what I need :cool:

Got a question, is there a way to tell it to fire at an exact target? (such as 'target1')

Edited by Yeti1

Share this post


Link to post
Share on other sites

Got a question, is there a way to tell it to fire at an exact target? (such as 'target1')

The script would have to be rewritten for that. I just wanted it to fire at anything that came in it's "zone" as that seemed more realistic than "fire only at this guy". I've refined the script quite a bit and am about to post it below.

---------- Post added at 17:02 ---------- Previous post was at 16:46 ----------

The script for mortars is about four posts down.

The original script above works fine, but I've refined it and made it a little "smarter".

This script will:

  • Fire on any enemy ground unit (set in the trigger) that strays into it's target area.
  • Re-adjust it's aim before each shot.
  • Doesn't fire at aircraft.
  • Acquires a new target if the current target is dead.
  • Allows for shell transit time before checking if the target is still alive and re-adjusting aim.

This is a little more complicated than the one above, but it makes for smarter AI artillery.

There is a new variable you'll pass in from the trigger for shell transit time. What this does is force the artillery piece to wait for the shell to arrive at the target before checking to see if it's still alive. You'll need to tinker around with this number to get it right for the range and artillery piece. Or just set it to one second and let it fire immediately after it reloads like before.

This involves 2 scripts. I wanted to break the functionality for finding live ground targets into another script so it could be used for mortars (or anything else).

This one I called "findLiveGroundTarget.sqf""

_targetArray = _this select 0;

// Reset the global variable to null
// to make sure we start fresh every check.
liveGroundTarget = objNull;
_idx = 0;
_targetCount = count _targetArray;

while {(_idx < _targetCount)} do{
 _someTarget = _targetArray select _idx;
 _isAlive = alive _someTarget;

  if(isTouchingGround _someTarget && _isAlive ) then {
  	//live ground target found, get out of the loop
  	liveGroundTarget = _someTarget;
  	_idx = _targetCount;
  };

  _idx = _idx +1;
};

And here is the updated "fireArty.sqf" script:


_arty = _this select 0;
_targetArray = _this select 1;
_shellTravelTime = _this select 2;

// _shellTravelTime is the third
// param passed in.
// This is how long you want the
// artillery piece to wait between 
// checking for targets. The point is
// to prevent wasting shots on dead targets.
// This wait time increases the time 
// between shots in addition to the
// _reloadTime variable below.
// Higher = slower rate of fire, more accurate
// Lower = higher rate of fire, less accurate


//This script call acquires the target
_tgtScript = [_targetArray] execVM "findLiveGroundTarget.sqf"; 
waitUntil {scriptDone _tgtScript};
_target = liveGroundTarget;

if (!isNull _target) then {  
_theAmmo  = getArtilleryAmmo [_arty] select 0;
_idx = 0;

// The number of times you want the artillery 
//to check for range and fire (if in range).
_shellCount = 10;

// 10 seconds to reload is for the
// longest reloading artillery piece. 
// MLRS reloads faster, but it gets buggy
// if you set this much lower.
_reloadTime = 10;

_arty addMagazineTurret [_theAmmo, [0]];
sleep _reloadTime;

while {(_idx < _shellCount)} do{

	_isAlive = alive _target;

	if(!_isAlive) then {
		//Target is dead, find another
		_idx = _shellCount;
		script = [_arty, _targetArray, _shellTravelTime] execVM "fireArty.sqf"; 

	} else {

		_tgtPos = position _target;
		_isInRange = _tgtPos inRangeOfArtillery [[_arty], _theAmmo ];

		if (_isInRange) then {
			_arty doArtilleryFire [_tgtPos, _theAmmo, 1]; 
			_arty addMagazineTurret [_theAmmo, [0]];
          		sleep _reloadTime;
          		sleep _shellTravelTime;
           } else {
			//The target may have moved too
			//close or too far, or you might
			//try moving the target area marker.	
			_idx = _shellCount;				
		};

	};
	_idx = _idx + 1;
};	
};


Just like above, create a named artillery piece, in this case "arty1". Then in your trigger's on act put this:


script=[arty1, thislist, 10] execVM "fireArty.sqf";

That third parameter is the shell transit time mentioned above. In this example I used 10.

And just like above, it's re-usable for as many target areas and artillery pieces you need:

script=[arty1, thislist, 10] execVM "fireArty.sqf"; 

script=[arty2, thislist, 45] execVM "fireArty.sqf"; 

script=[arty3, thislist, 25] execVM "fireArty.sqf"; 

Would be for different arty pieces with different shell transit times.

Thanks!

Edited by leecarter
  • Like 1

Share this post


Link to post
Share on other sites

Thanks bud, I'll put this to good use, quick question again.. so is this going to track my player if he is the enemy and in the trigger area?

Share this post


Link to post
Share on other sites
Thanks bud, I'll put this to good use, quick question again.. so is this going to track my player if he is the enemy and in the trigger area?

Yes, you'll get targeted like anything else. If there are multiple targets in the area you may not be the first target chosen though.

A fun way to test it is sit on a hill and put a few enemy units down below in the target zone. It's cool to see the arty pause, then start dropping shells on another target after the first is dead. Also helps figure out your shell transit times, they can take quite awhile to get there if the target area is far away.

The MLRS is pretty accurate, the big guns - not so much. :D

Edited by leecarter

Share this post


Link to post
Share on other sites
Yes, you'll get targeted like anything else. If there are multiple targets in the area you may not be the first target chosen though.

A fun way to test it is sit on a hill and put a few enemy units down below in the target zone. It's cool to see the arty pause, then start dropping shells on another target after the first is dead. Also helps figure out your shell transit times, they can take quite awhile to get there if the target area is far away.

The MLRS is pretty accurate, the big guns - not so much. :D

Nice one :), just been watching it, it's an awesome machine, I just wish I had the brains to figure out how to make it target specific targets.

Edit: Should of added, Nice code bud, it has helped me understand SQF a little more.

Edited by Yeti1

Share this post


Link to post
Share on other sites

Here's a script for mortars since they behave a little differently.

As it turns out, mortars aren't as predictable as artillery. Sometimes they just rattle off a bunch of rounds despite what the scripts tell them to do.

But regardless of that, like the artillery script above, this script does a decent job of:

  • Fire on any enemy ground unit (set in the trigger) that strays into it's target area.
  • Re-adjust it's aim between (most) shots.
  • Doesn't fire at aircraft.
  • Acquires a new target if the current target is dead.
  • Allows for shell transit time before checking if the target is still alive and re-adjusting aim (buggy).

This also uses 2 scripts, so make sure you have the "findLiveGroundTarget.sqf"" script from the post above.

Then the script I called "fireMortar.sqf"


_mortar = _this select 0;
_targetArray = _this select 1;
_shellTravelTime = _this select 2;

// _shellTravelTime is the third
// param passed in.
// This is how long you want the
// mortar to wait between 
// checking for targets. The point is
// to prevent wasting shots on dead targets.
// This doesn't work 100% with mortars like
// it does with artillery. Not sure why - might
// be a tad bugged still.
// Higher = slower rate of fire, more accurate
// Lower = higher rate of fire, less accurate


//This script call acquires the target
_tgtScript = [_targetArray] execVM "findLiveGroundTarget.sqf"; 
waitUntil {scriptDone _tgtScript};
_target = liveGroundTarget;

if (!isNull _target) then {  
_theAmmo  = getArtilleryAmmo [_mortar] select 0;
_idx = 0;

// The number of times you want the mortar 
//to check for range and fire (if in range).
_shellCount = 10;


// The number of times you want the _mortar to check for range
while {(_idx < _shellCount)} do{

	_isAlive = alive _target;

	if(!_isAlive) then {
		//Target is dead, find another
		_idx = _shellCount;
		script = [_mortar, _targetArray, _shellTravelTime] execVM "fireMortar.sqf"; 

	} else {

		_tgtPos = position _target;
		_isInRange = _tgtPos inRangeOfArtillery [[_mortar], _theAmmo  ];

		if (_isInRange) then {
			_mortar doArtilleryFire [_tgtPos, _theAmmo  , 1 ];		
			//Reload time
			sleep 5;
			sleep _shellTravelTime;	
		}else {
			//The target may have moved too
			//close or too far, or you might
			//try moving the target area marker.	
			_idx = _shellCount;				
		};
	};
	_idx = _idx + 1;
};	

};

Set it up just like the artillery script above, works exactly the same. Your target on act will look like:


script=[mortar1, thislist, 10] execVM "fireMortar.sqf"; 

For each mortar/target area you wish to use.

Enjoy!

Edited by leecarter

Share this post


Link to post
Share on other sites

This sounds fantastic. Thanks for sharing.

I'd like to ask a couple of questions if i may please:

Is it possible to link this with a man, so if i killed the man (the spotter), then the guns would stop?

If i wanted to have a battery of cannons all i would do is place the 3 guns down, group them & group the leader to the trigger?

Does the script end wen there's no more ammo left or does it replimish it self every time its triggered?

Many thanks in advance

Share this post


Link to post
Share on other sites

Beautiful.

Just a suggestion. I never tried this but could worth it, and make this script the best arty script around the scene...

Arty is useful when you fire to a bunch of enemies, not a single one. The find target script just picks the first alive ground unit in the array...

So, each round prolly will target the same guy if it is still alive?

Idea: create, via scripting a minitrigger that checks if there are more than x units around the targeted unit in certain radius (let's say 30 meters). If so, then YES, that's the target.

Never made it, but could be woth a try.

Share this post


Link to post
Share on other sites
Beautiful.

Just a suggestion. I never tried this but could worth it, and make this script the best arty script around the scene...

Arty is useful when you fire to a bunch of enemies, not a single one. The find target script just picks the first alive ground unit in the array...

So, each round prolly will target the same guy if it is still alive?

Idea: create, via scripting a minitrigger that checks if there are more than x units around the targeted unit in certain radius (let's say 30 meters). If so, then YES, that's the target.

Never made it, but could be woth a try.

Sure, you could attach a variable to the target called "currentlyTargeted" or something that the other arty pieces could do a check on in their scripts.

There's lots of room for refinement and additions here (like any script), but this makes for a nice usable "core" artillery script.

---------- Post added at 00:16 ---------- Previous post was at 00:13 ----------

This sounds fantastic. Thanks for sharing.

I'd like to ask a couple of questions if i may please:

Is it possible to link this with a man, so if i killed the man (the spotter), then the guns would stop?

If i wanted to have a battery of cannons all i would do is place the 3 guns down, group them & group the leader to the trigger?

Does the script end wen there's no more ammo left or does it replimish it self every time its triggered?

Many thanks in advance

Sure, I'd pass your "spotter" soldier in as a parameter and then do a check that he's alive before targeting. I believe it keeps reloading forever, if you wanted to get fancy you could count the number of rounds and then stop after x amount and upon reloading set the variable back to 0.

Share this post


Link to post
Share on other sites

@leecarter

Hi mate, im trying to put this into a mission im making but im having trouble getting my head round it as the guns dont fire. This is what ive done so far:

1) made guns & called them arty1 etc

2) made trigger with the script in as you said above

3) placed the whole of the script into my own arty.sqf file in the mission folder. (i copied & pasted everything in there). I now read that i need 2 scripts but im not sure what goes into what script. Can you please advise me mate?

Thanks a million

sorry for the above mate, it works amazing. Such a good script. The problem I had was I renamed the file instead of saving as so the game couldn't find the sqf's.

Thank you once again for this script

Edited by Pipyn1970
im a fool

Share this post


Link to post
Share on other sites

Hello everyone:

 

I have allowed myself to "improve" the arilleria script:

The "improvements" are:

- The artillery target is marked on the map. When it is destroyed, it marks the next one.

- The target is marked with yellow smoke.

 

script=[artilleria1, thislist, 10] execVM "fireArty+bengalasV2.sqf";

 

//Just like above, create a named artillery piece, in this case "arty1". Then in your trigger's on act put this:

//script=[artilleria1, thislist, 10] execVM "fireArty+bengalasV2.sqf"; 
//
//script=[artilleria2, thislist, 10] execVM "fireArty+bengalasV2.sqf"; 
//
//script=[artilleria3, thislist, 10] execVM "fireArty+bengalasV2.sqf"; 
//
//Would be for different arty pieces with different shell transit times.





_arty = _this select 0;
_targetArray = _this select 1;
_shellTravelTime = _this select 2;

// _shellTravelTime is the third
// param passed in.
// This is how long you want the
// artillery piece to wait between 
// checking for targets. The point is
// to prevent wasting shots on dead targets.
// This wait time increases the time 
// between shots in addition to the
// _reloadTime variable below.
// Higher = slower rate of fire, more accurate
// Lower = higher rate of fire, less accurate


//This script call acquires the target
_tgtScript = [_targetArray] execVM "findLiveGroundTarget.sqf"; 
waitUntil {scriptDone _tgtScript};
_target = liveGroundTarget;

if (!isNull _target) then {  
_theAmmo  = getArtilleryAmmo [_arty] select 0;
_idx = 0;


// The number of times you want the artillery 
//to check for range and fire (if in range).
_shellCount = 10;

// 10 seconds to reload is for the
// longest reloading artillery piece. 
// MLRS reloads faster, but it gets buggy
// if you set this much lower.
_reloadTime = 10;

_arty addMagazineTurret [_theAmmo, [0]];
sleep _reloadTime;

while {(_idx < _shellCount)} do{

	_isAlive = alive _target;

	if(!_isAlive) then {
		//Target is dead, find another
                   
                 deletemarker "ARTY";            
                
		_idx = _shellCount;
		script = [_arty, _targetArray, _shellTravelTime] execVM "fireArty+bengalasV2.sqf"; 

	} else {

		_tgtPos = position _target;
		_isInRange = _tgtPos inRangeOfArtillery [[_arty], _theAmmo ];

		if (_isInRange) then {
                        _mkr = createMarker ["ARTY",_tgtPos];
                        _mkr setMarkerShape "ICON";
                        _mkr setMarkerType "hd_destroy"; 
                        _mkr setMarkerColor "ColorRed";
                        _mkr setMarkerText "FIRE MISSION ARTILLERIA";
                        _bengala = "G_40mm_SmokeYellow" createvehicle [(_tgtPos select 0) , (_tgtPos select 1) , 100];
			_arty doArtilleryFire [_tgtPos, _theAmmo, 3]; 
			_arty addMagazineTurret [_theAmmo, [0]];
          		sleep _reloadTime;
          		sleep _shellTravelTime;
           } else {
			//The target may have moved too
			//close or too far, or you might
			//try moving the target area marker.	
			_idx = _shellCount;				
		};

	};
	_idx = _idx + 1;
};	
};

 

Share this post


Link to post
Share on other sites

morta

 

Hello everyone:

 

I have allowed myself to "improve" the  mortar script:

The "improvements" are:

- The  mortar target is marked on the map. When it is destroyed, it marks the next one.

- The target is marked with red smoke.

 

script=[mortar1, thislist, 10] execVM "fireMortar+bengalasV2.sqf";

 

// script=[mortar1, thislist, 10] execVM "fireMortar+bengalasV2.sqf"; 



_mortar = _this select 0;
_targetArray = _this select 1;
_shellTravelTime = _this select 2;



// _shellTravelTime is the third
// param passed in.
// This is how long you want the
// mortar to wait between 
// checking for targets. The point is
// to prevent wasting shots on dead targets.
// This doesn't work 100% with mortars like
// it does with artillery. Not sure why - might
// be a tad bugged still.
// Higher = slower rate of fire, more accurate
// Lower = higher rate of fire, less accurate


//This script call acquires the target
_tgtScript = [_targetArray] execVM "findLiveGroundTarget.sqf"; 
waitUntil {scriptDone _tgtScript};
_target = liveGroundTarget;

if (!isNull _target) then {  
_theAmmo  = getArtilleryAmmo [_mortar] select 0;
_idx = 0;

// The number of times you want the mortar 
//to check for range and fire (if in range).
_shellCount = 10;


// The number of times you want the _mortar to check for range
while {(_idx < _shellCount)} do{

	_isAlive = alive _target;

	if(!_isAlive) then {
		//Target is dead, find another
	      
        deletemarker "MORTERO";    
		
		_idx = _shellCount;
		script = [_mortar, _targetArray, _shellTravelTime] execVM "fireMortar+bengalasV2.sqf"; 
 

	} else {

		_tgtPos = position _target;
		_isInRange = _tgtPos inRangeOfArtillery [[_mortar], _theAmmo  ];

		if (_isInRange) then {
		                _mkr = createMarker ["MORTERO",_tgtPos];
                        _mkr setMarkerShape "ICON";
                        _mkr setMarkerType "hd_destroy"; 
                        _mkr setMarkerColor "ColorRed";
                        _mkr setMarkerText "FIRE MISSION MORTERO";
                        _bengala = "G_40mm_SmokeRed" createvehicle [(_tgtPos select 0) , (_tgtPos select 1) , 100];
			_mortar doArtilleryFire [_tgtPos, _theAmmo  , 10 ]; // 		_mortar doArtilleryFire [_tgtPos, _theAmmo  , 1 ];		
			//Reload time
			sleep 5;
			sleep _shellTravelTime;	
		}else {
			//The target may have moved too
			//close or too far, or you might
			//try moving the target area marker.	
			_idx = _shellCount;				
		};
	};
	_idx = _idx + 1;
};	

};

 

Share this post


Link to post
Share on other sites

Correct me if I'm wrong but with this script won't the artillery fire on targets merely because the targets enter the area regardless if the operator or spotter is aware of the target?

 

Share this post


Link to post
Share on other sites

Hello leecarter,

 

I ask you permission to post a laser assignment script in the forum that uses your "findLiveGroundTarget.sqf" script, indicating that the script is from your creation.

 

BR.

 

AtomicBot

Share this post


Link to post
Share on other sites

Hello leecarter,

although it's kind of late, but I found your arty script here and modified it to prevent AI from shelling nearby friendlies (of AI side):

// Artillery Fire Mission script - fires at targets in trigger area - by leecarter (modified by mallinga)
// example:
// _null = [arty1, thislist] execVM "Scripts\fireMission.sqf"; 

_FireMission = _this select 0;
_targetArray = _this select 1;

_target  = _targetArray select 0;
_theAmmo  = getArtilleryAmmo [_FireMission] select 0;
_idx = 0;

//-- prevents friendly fire - "SoldierEB" = east, "SoldierWB" = west, "SoldierGB" = resistance
while {(_idx < 10)} do 
{
	_friendlyUnitsAlive = {alive _x} count ((_target nearObjects ["SoldierEB", 50]) + (_target nearObjects ["SoldierGB", 50]));

	//-- prevents firing at aircraft 
	if (isTouchingGround _target && _friendlyUnitsAlive == 0) then 
	{	
		//-- The number of times you want artillery to check for range
		while {(_idx < 10)} do
		{
			_tgtPos = position _target;
			_isInRange = _tgtPos inRangeOfArtillery [[_FireMission], _theAmmo  ];
			if (_isInRange) then 
			{
				_FireMission commandArtilleryFire [_tgtPos, _theAmmo  , 1 ];			
			};
			sleep 5;
			_idx = _idx + 1;
		};	
	};
	sleep 5;
};

now, if you enter the trigger area, it will start or stop fire in a 50m radius around target until condition (times of adjusting, here: 10) is met.

cheers

Share this post


Link to post
Share on other sites

I have ready solution for that (for static is no problems with it):

 

https://steamcommunity.com/sharedfiles/filedetails/?id=2255028526

 

but try to command for example to mlrs  move to some location and back on the same place (by waypoints od by domove command)

and after it stops give it command to fire like commandArtilleryFire - if it  turn gun to target and fire specified number of rounds wijout moving turret/gun in some unspecific directions during fire give me info how it is done.

I make many experiments and effect is always the same shells/rockets  if are fired drops very far from target oposite to state when it will be ordered  before it start to move to waypoints ->  for me every movement of unit (vehicle) kill this function "commandArtilleryFire" - i can of course make manual overriden fire but it is chaotic and not precise fire (i can even block by disableai weponamim moving of gun but every shot make movement of vehicle and change destination of shell.

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

×