Jump to content
Sign in to follow this  
Woodpeckersam

Optimizing Scripts for Multiplayer

Recommended Posts

Hello there, its not often I do this, but this time round it's different.

Now.... I have a teeny tiny little bit of knowledge regarding scripting for multiplayer and making sure it is optimized.

This mission however can get a little laggy, especially at the moment a unit/group spawns.

It's not major, not hugely noticeable.. unless you are tired and concentrated on it, like me lol.

Now I was wondering if some of you can have a read through my scripts, give me some tips on optimization and ways to improve it.

Basically my mission is very much going along the lines of the Defend the Town missions. The only differences are that I have not taken any code from the Official Missions (well I hope not as I've used code inspirations from various sources on the forums and outside the forums), and that I am not using any of the modules in the game.

I am really building a mission, maybe to release. It wont be great, wont be a 5 star mission, but i'm doing it to help improve my knowledge of Multiplayer mission editing. The current state of it took my five hours to make. Complete with spawning and respawning markers, playable units, AI units defending the town and a trigger than when no Opfor are found (after spawning them), activate the Deactivate init field to say that Opfor_Present is false.

Ok, so far I have been using publicVariable everywhere as, if I read correctly, needs to be used EVERY time a variable change. I also really want this to work with JIP, but I havent seen my friend online today to test it out. But some of you will probably know if it will work with JIP just by looking at it.

Init.sqf

// Initialization
//waitUntil {!(isNil "BIS_MPF_initDone") AND !(isNil "BIS_fnc_init") AND !(isNull player)};//DOESNT WORK
waitUntil {BIS_fnc_init};

if (isNil "Timer") then {Timer = 30;}; publicVariable "Timer";

if (isNil "Multiplier") then {Multiplier = 1.7;}; publicVariable "Multiplier";

if (isNil "WaveTypeMultiplier") then {WaveTypeMultiplier = 2;}; publicVariable "WaveTypeMultiplier";

if (isNil "WaveCount") then {WaveCount = 30;}; publicVariable "WaveCount";

if (isNil "WaveSpawnDelay") then {WaveSpawnDelay = 5;}; publicVariable "WaveSpawnDelay";

if (isNil "TimerDone") then {TimerDone = false;}; publicVariable "TimerDone";

//if (isServer) then {
FNC_Timer = compile preprocessFile "Scripts\Timer.sqf";
FNC_Groups = compile preprocessFile "Scripts\Groups.sqf";
[] execVM "Waves.sqf";
setViewDistance 1500;
setTerrainGrid 40;
0 setFog [1,0.019,100];

//Allow players the ability to choose a class/loadout before respawn
[west, "WEST1"] call BIS_fnc_addRespawnInventory;
[west, "WEST2"] call BIS_fnc_addRespawnInventory;
[west, "WEST3"] call BIS_fnc_addRespawnInventory;
[west, "WEST4"] call BIS_fnc_addRespawnInventory;

//Set spawn positions
[west, getMarkerPos "Respawn1"] call BIS_fnc_addRespawnPosition;
[west, getMarkerPos "Respawn2"] call BIS_fnc_addRespawnPosition;
[west, getMarkerPos "Respawn3"] call BIS_fnc_addRespawnPosition;
//};

Description.ext

Author = "Woodpeckersam";
OnLoadMission = "Defend Oreokastro!";
Respawn = "BASE";
RespawnDelay = 10;
DisabledAI = false;
joinUnassigned = true; // auto assign

class Header
{
gameType=Defend;
minPlayers=1;
maxPlayers=10;
};


respawnTemplatesWest[] = {"MenuInventory","MenuPosition"};

class CfgRespawnInventory //Borrowed from FighterD1, thanks!
{
class WEST1
{
	displayName = "Grenadier"; // Name visible in the menu
	icon = "\A3\ui_f\data\igui\cfg\weaponicons\GL_ca.paa"; // Icon displayed next to the name
	// Condition must return true in order for the loadout to be displayed in the menu.
	// Evaluated when the menu is opened.
	show = "side group _this == west";

	// Loadout definition, uses same entries as CfgVehicles classes
	weapons[] = {
		"arifle_MX_GL_F",
		"hgun_P07_F",
		"Rangefinder"
	};
	magazines[] = {
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"SmokeShell",
		"SmokeShell",
		"SmokeShell",
		"HandGrenade",
		"HandGrenade",
		"HandGrenade",
		"1Rnd_HE_Grenade_shell",
		"1Rnd_HE_Grenade_shell",
		"1Rnd_HE_Grenade_shell",
		"1Rnd_HE_Grenade_shell",
		"1Rnd_HE_Grenade_shell",
		"1Rnd_HE_Grenade_shell",
		"1Rnd_Smoke_Grenade_shell",
		"1Rnd_Smoke_Grenade_shell",
		"1Rnd_Smoke_Grenade_shell",
		"1Rnd_Smoke_Grenade_shell",
		"16Rnd_9x21_Mag",
		"16Rnd_9x21_Mag"
	};
	items[] = {
		"FirstAidKit",
		"FirstAidKit",
		"FirstAidKit",
		"FirstAidKit"
	};
	linkedItems[] = {
		"V_Chestrig_khk",
		"H_HelmetB",
		"optic_aco",
		"acc_flashlight",
		"ItemGPS",
		"ItemMap",
		"ItemCompass",
		"ItemWatch",
		"ItemRadio"
	};
	uniformClass = "U_B_CombatUniform_mcam_tshirt";
	backpack = "B_AssaultPack_mcamo";
};

class WEST2
{
	displayName = "Automatic Rifleman"; // Name visible in the menu
	icon = "\A3\ui_f\data\igui\cfg\weaponicons\MG_ca.paa"; // Icon displayed next to the name
	// Condition must return true in order for the loadout to be displayed in the menu.
	// Evaluated when the menu is opened.
	show = "side group _this == west";

	// Loadout definition, uses same entries as CfgVehicles classes
	weapons[] = {
		"LMG_Zafir_F",
		"hgun_P07_F",
		"Rangefinder"
	};
	magazines[] = {
		"150Rnd_762x51_Box",
		"150Rnd_762x51_Box",
		"150Rnd_762x51_Box",
		"150Rnd_762x51_Box",
		"150Rnd_762x51_Box",
		"SmokeShell",
		"SmokeShell",
		"SmokeShell",
		"HandGrenade",
		"HandGrenade",
		"16Rnd_9x21_Mag",
		"16Rnd_9x21_Mag"
	};
	items[] = {
		"FirstAidKit",
		"FirstAidKit",
		"FirstAidKit",
		"FirstAidKit"
	};
	linkedItems[] = {
		"V_PlateCarrier2_rgr",
		"H_HelmetB",
		"optic_mrco",
		"acc_flashlight",
		"ItemGPS",
		"ItemMap",
		"ItemCompass",
		"ItemWatch",
		"ItemRadio"
	};
	uniformClass = "U_B_CombatUniform_mcam_tshirt";
	backpack = "B_AssaultPack_mcamo";
};

class WEST3
{
	displayName = "Explosive Specialist"; // Name visible in the menu
	icon = "\A3\ui_f\data\igui\cfg\weaponicons\AT_ca.paa"; // Icon displayed next to the name
	// Condition must return true in order for the loadout to be displayed in the menu.
	// Evaluated when the menu is opened.
	show = "side group _this == west";

	// Loadout definition, uses same entries as CfgVehicles classes
	weapons[] = {
		"arifle_MXC_F",
		"hgun_P07_F",
		"Rangefinder"
	};
	magazines[] = {
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"SmokeShell",
		"SmokeShell",
		"HandGrenade",
		"HandGrenade",
		"APERSBoundingMine_Range_Ammo",
		"APERSBoundingMine_Range_Ammo",
		"16Rnd_9x21_Mag",
		"16Rnd_9x21_Mag"
	};
	items[] = {
		"FirstAidKit",
		"FirstAidKit",
		"FirstAidKit",
		"FirstAidKit"
	};
	linkedItems[] = {
		"V_PlateCarrier2_rgr",
		"H_Cap_headphones",
		"optic_aco",
		"acc_flashlight",
		"ItemGPS",
		"ItemMap",
		"ItemCompass",
		"ItemWatch",
		"ItemRadio"
	};
	uniformClass = "U_B_CombatUniform_mcam";
	backpack = "B_Kitbag_cbr";
};

class WEST4
{
	displayName = "Marksman"; // Name visible in the menu
	icon = "\A3\ui_f\data\igui\cfg\weaponicons\srifle_ca.paa"; // Icon displayed next to the name
	// Condition must return true in order for the loadout to be displayed in the menu.
	// Evaluated when the menu is opened.
	show = "side group _this == west";

	// Loadout definition, uses same entries as CfgVehicles classes
	weapons[] = {
		"arifle_MXM_F",
		"hgun_P07_F",
		"Laserdesignator"
	};
	magazines[] = {
		"Laserbatteries",
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"30Rnd_65x39_caseless_mag",
		"SmokeShell",
		"SmokeShell",
		"HandGrenade",
		"HandGrenade",
		"ClaymoreDirectionalMine_Remote_Mag",
		"ClaymoreDirectionalMine_Remote_Mag",
		"16Rnd_9x21_Mag",
		"16Rnd_9x21_Mag"
	};
	items[] = {
		"FirstAidKit",
		"FirstAidKit",
		"FirstAidKit",
		"FirstAidKit"
	};
	linkedItems[] = {
		"V_Chestrig_khk",
		"H_Watchcap_blk",
		"optic_Hamr",
		"acc_flashlight",
		"ItemGPS",
		"ItemMap",
		"ItemCompass",
		"ItemWatch",
		"ItemRadio"
	};
	uniformClass = "U_B_CombatUniform_mcam";
};
};

Waves.sqf

private ["_spawnMultiplier","_spawnType","_randomSquad","_type","_RoadSpawns","_grpMot","_Spawns","_grpInf"];
_spawnMultiplier = 1.7;
publicVariable "_spawnMultiplier";


For "_i" from 1 to WaveCount do
{
Curr_WaveCount = _i;
publicVariable "Curr_WaveCount";

[] spawn FNC_Timer;
waitUntil {TimerDone};

Hint Format ["Wave %1 Commencing",_i];
sleep 5;


_spawnMultiplier = round ( _spawnMultiplier * Multiplier);
publicVariable "_spawnMultiplier";

for "_i" from 1 to _spawnMultiplier do
{
	_spawnType = if (count _this > 2) then {_this select 2} else {"inf"};
	_type = "";
	_randomSquad = "";

	publicVariable _spawnType;
	publicVariable _randomSquad;
	publicVariable _type;

          	if (Curr_WaveCount < round (Wavecount / WaveTypeMultiplier + (Multiplier)) then {_spawnType = ["inf"] call BIS_fnc_selectRandom;};

	if (Curr_WaveCount > round (Wavecount / WaveTypeMultiplier + (Multiplier / 0.5))) then {_spawnType = ["inf","sup"] call BIS_fnc_selectRandom;};

	if (Curr_WaveCount > round (Wavecount / WaveTypeMultiplier + (Multiplier / 0.4))) then {_spawnType = ["inf","sup","mot"] call BIS_fnc_selectRandom;};

	publicVariable _spawnType;

	switch (_spawnType) do6
	{
		case "inf": {
		    // configfile >> "CfgGroups" >> "East" >> "OPF_F" >> "Infantry" >> _randomInfSquad
		    _randomSquad = ["OIA_InfSquad", "OIA_InfSquad_Weapons", "OIA_InfTeam", "OIA_InfTeam_AT"] call BIS_fnc_selectRandom;
		    _type = "Infantry";
		    };

		case "mot": {
		    // configfile >> "CfgGroups" >> "East" >> "OPF_F" >> "Motorized_MTP" >> _randomMotorizedSquad
		    _randomSquad = ["OIA_MotInf_AT", "OIA_MotInf_Team"] call BIS_fnc_selectRandom;
		    };


		case "sup": {
		    // configfile >> "CfgGroups" >> "East" >> "OPF_F" >> "Support" >> _randomSupportSquad
		    _randomSquad = ["OI_support_CLS", "OI_support_ENG", "OI_support_EOD"] call BIS_fnc_selectRandom;
		    _type = "Support";
		    };
	};

	publicVariable _randomSquad;
	publicVariable _type;

	if (_spawnType == "mot") then
	{
		_RoadSpawns = ["RoadSpawn1","RoadSpawn2","RoadSpawn3"] call BIS_fnc_selectRandom;
		publicVariable "_RoadSpawns";

		_grpMot = [getMarkerPos _RoadSpawns, EAST, (configfile >> "CfgGroups" >> "East" >> "OPF_F" >> "Motorized_MTP" >> _randomSquad)] call BIS_fnc_spawnGroup; publicVariable "_grpMot";

		[_grpMot, getMarkerPos "Defend", 150] call CBA_fnc_taskAttack;
		_grpMot allowFleeing false;

	} else
	{
		_Spawns = ["Spawn1","Spawn2","Spawn3","Spawn4","Spawn5","Spawn6","Spawn7","Spawn8"] call BIS_fnc_selectRandom;
		publicVariable "_Spawns";

		_grpInf = [getMarkerPos _Spawns, EAST, (configfile >> "CfgGroups" >> "East" >> "OPF_F" >> _type >> _randomSquad)] call BIS_fnc_spawnGroup; publicVariable "_grpInf";

		[_grpInf, getMarkerPos "Defend", 150] call CBA_fnc_taskAttack;
		_grpInf allowFleeing false;
	};

	sleep WaveSpawnDelay;

};

waitUntil {!Opfor_Present};
};

Scripts\Timer.sqf

private ["_Cur_Time"];
_Cur_Time = Timer;
publicVariable "_Cur_Time";

For "_i" from Timer to 0 step -1 do
{
Hint Format ["%1 Seconds left until next Wave",_Cur_Time];
sleep 1;
_Cur_Time = _Cur_Time - 1; publicVariable "Cur_Time";
};

TimerDone = true; publicVariable "TimerDone";
sleep 5;

TimerDone = false; publicVariable "TimerDone";
exit;

Any help will be appreciate, as it will help boost my learning skills and maybe hopefully teach other people on the forums =)

---------- Post added at 23:09 ---------- Previous post was at 23:04 ----------

Major Troubles/Questions:

1. I have no idea how to create an effective Multiplier (<- MULT I PLIER NOT MULTI PLAYER. That word plays a trick on the eyes hence the reason I made it bold lol) that works universally. My maths are not great so MY logic behind a multiplier wont be lol :P

2. WEST4 is not being found in Description.ext when starting a mission. WEST4 is related to the Respawn Inventory.

3. When the timer counts down on the hints dialog, it makes that horrible beeping sound. Is there any way to set your own beeping sound?

4. Multiplayer parameters. I've looked into it, but the page on the BIKI doesnt seem to be available, at least for titleParams%.

Share this post


Link to post
Share on other sites

Can someone help me understand when to use isServer?

Because whenever I use it (its commented out in my init.sqf) none of code inside it runs. At least myself, the player wont see anything..

---------- Post added at 21:43 ---------- Previous post was at 21:30 ----------

Can someone help me understand how to use synchronizeObjectAdd on respawn?

I swear i remember reading somewhere something about "onRespawn" but I cannot seem to find it. Basically i need to check that if a player/unit has respawned, re-sync them to the modules I have. I dont have any right now, but I will need to, as I need support modules to be included in the mission.

Share this post


Link to post
Share on other sites

First, how are you testing this? With a dedicated server and client on one machine or with a local host and another client machine?

To answer the questions in order:

1. Creating a constant is simply defining that value to be the same on all machines. All machines run the init.sqf. Therefore, you would simply declare your constant near the beginning of the init as a global variable and everyone would have that value. There is not need to Public Variable (PV) anything.

2. Did you save the mission to reload the description.ext? If yes, then I do not know what the problem is. I do not see anything wrong with the description.ext.

3. hintSilent

4. titleParam% is obsolete, params is what you would use: http://community.bistudio.com/wiki/Description.ext#params_2

5. All machines will run all code in the init (and in all functions called there and so on) unless you control who executes what. I am of the opinion that the server should do everything and that the clients should only do the minimum that is necessary, due to locality. For example, a spawning function should run only the server. Things that have global effect, like those BIS functions for respawn inventory and position, will automatically be sent over the network to clients. Only the server needs to call those functions, and everyone will see the effect. There are also functions and commands that only have local effect, or require that their arguments be local. In that case, you would have the server remotely execute the function or command on the client machine. For example, a countdown hint function.

6. http://community.bistudio.com/wiki/Arma_3:_Event_Handlers#Respawn

On to the code, there is no need to use PV unless you are updating data on the clients that they are actively accessing, or if you need the clients to execute a function. There is no need to use a global variable unless it is critical that other functions share the data. Also, I always thought that you could not PV a local variable, but apparently you can. In the init, I have added a line for JIP, and put everything that the server can do in the if-block.

if !(isServer) then {waitUntil {!(isNull player)};};
waitUntil {BIS_fnc_init};
finishMissionInit;

FNC_Timer = compileFinal preprocessFileLineNumbers "Scripts\Timer.sqf";
FNC_Groups = compileFinal preprocessFileLineNumbers "Scripts\Groups.sqf";
FNC_Waves = compileFinal preprocessFileLineNumbers "Waves.sqf";

Multiplier = 1.7;
TimerDone = false;

0 setFog [1,0.019,100];

if (isServer) then {

   _waveCount = 30;
   _waveTypeMultiplier = 2;
   _waveSpawnDelay = 5;
   _timer = 30;

   0 = [_waveCount, _waveTypeMultiplier, Multiplier, _waveSpawnDelay, _timer] spawn FNC_Waves;

   [west, "WEST1"] call BIS_fnc_addRespawnInventory;
   [west, "WEST2"] call BIS_fnc_addRespawnInventory;
   [west, "WEST3"] call BIS_fnc_addRespawnInventory;
   [west, "WEST4"] call BIS_fnc_addRespawnInventory;

   [west, getMarkerPos "Respawn1"] call BIS_fnc_addRespawnPosition;
   [west, getMarkerPos "Respawn2"] call BIS_fnc_addRespawnPosition;
   [west, getMarkerPos "Respawn3"] call BIS_fnc_addRespawnPosition;
};

sleep 1;
setViewDistance 1500;
setTerrainGrid 40;

You have described your mission, but not what the functions you posted are supposed to do. I am going to be honest and say that there are a lot of strange things and likely mistakes that are hard to decipher. I am going to assume that you want to spawn waves of enemies and show a timer to all the players that counts down until the next wave. Only the server should spawn the waves, and it would remotely execute the hint function on clients. Based upon that, I would write the scripts like this:

#define Spawn_Multiplier 1.7
private ["_waveCount", "_waveTypeMultiplier", "_waveSpawnDelay", "_timer", "_spawnType", "_randomSquad", "_type", "_spawnMarkers", "_attackGroup", "_i", "_j"];

_waveCount = _this select 0;
_waveTypeMultiplier = _this select 1;
_waveSpawnDelay = _this select 2;
_timer = _this select 3;

For "_j" from 1 to _waveCount do {

   0 = [[_timer], "FNC_Timer"] spawn BIS_fnc_MP;
   sleep _timer;
   hintSilent Format ["Wave %1 Commencing",_i];
   sleep 5;

   for "_i" from 1 to (round (Spawn_Multiplier * Multiplier)) do {
       _spawnType = if (count _this > 2) then {_this select 2;} else {"infantry";};

       if (_i < round (_waveCount / _waveTypeMultiplier + (Multiplier)) then {_spawnType = ["infantry"] call BIS_fnc_selectRandom;};
       if (_i > round (_waveCount / _waveTypeMultiplier + (Multiplier / 0.5))) then {_spawnType = ["infantry","support"] call BIS_fnc_selectRandom;};
       if (_i > round (_waveCount / _waveTypeMultiplier + (Multiplier / 0.4))) then {_spawnType = ["infantry","support","motorized"] call BIS_fnc_selectRandom;};

       switch (_spawnType) do {
           case "infantry": {
               _randomSquad = ["OIA_InfSquad", "OIA_InfSquad_Weapons", "OIA_InfTeam", "OIA_InfTeam_AT"] call BIS_fnc_selectRandom;
               _type = "Infantry";
           };
           case "motorized": {
               _randomSquad = ["OIA_MotInf_AT", "OIA_MotInf_Team"] call BIS_fnc_selectRandom;
           };
           case "support": {
               _randomSquad = ["OI_support_CLS", "OI_support_ENG", "OI_support_EOD"] call BIS_fnc_selectRandom;
               _type = "Support";
           };
       };

       if (_spawnType == "motorized") then {
           _spawnMarkers = ["RoadSpawn1","RoadSpawn2","RoadSpawn3"] call BIS_fnc_selectRandom;
           _attackGroup = [(getMarkerPos _spawnMarkers), east, (configfile >> "CfgGroups" >> "East" >> "OPF_F" >> "Motorized_MTP" >> _randomSquad)] call BIS_fnc_spawnGroup;
       } else {
           _spawnMarkers = ["Spawn1","Spawn2","Spawn3","Spawn4","Spawn5","Spawn6","Spawn7","Spawn8"] call BIS_fnc_selectRandom;
           _attackGroup = [(getMarkerPos _spawnMarkers), east, (configfile >> "CfgGroups" >> "East" >> "OPF_F" >> _type >> _randomSquad)] call BIS_fnc_spawnGroup;
       };
       0 = [_attackGroup, (getMarkerPos "Defend"), 150] call CBA_fnc_taskAttack;
       _attackGroup allowFleeing false;
       sleep _waveSpawnDelay;
   };
   waitUntil {sleep 2; !(Opfor_Present)};
};
if (true) exitWith {};

private ["_timer", "_i"];
_timer = _this select 0;

for "_i" from _timer to 0 step -1 do {
   hintSilent format ["%1 Seconds left until next Wave", _i];
   sleep 1;
};

TimerDone = true;
publicVariable "TimerDone";

sleep 5;

TimerDone = false;
publicVariable "TimerDone";

if (true) exitWith {};

Parameters and local variables are better than using global variables, as there is less to keep track of and less chance for hard-to-find bugs to occur. Also, you used a nested for loop that iterates over the same variable as the other for loop, and then a for loop that does not use the iterating variable when it should. Instead of 'exit', it should be 'if (true) exitWith {};' at the end of functions. There are some strange things about the multipliers and getting the number and type of enemies to spawn. If you explained what exactly you want in terms of probabilities, I could help with the math. I also renamed some of the variables and strings, and I moved some code around to avoid repeating it.

All of this is untested, as I cannot test your mission, and there are probably typos etc. There are other functions and triggers running in your mission besides what you have posted there, so I cannot really determine if this will work. Consider this general advice and a different style of coding. To be clear, copying and pasting that code will probably not work. I also made some global variables local, as they do not need to be global based upon the code you posted.

I am going to be honest and say that I do not know what will work with JIP and what will not. I suspect that the respawn inventory might not work for JIP clients, but they will be able to see the waves of enemies and the timer. You might want to consider using VAS for the inventory, as that would work well with JIP (at least it should).

These are some good resources:

http://community.bistudio.com/wiki/6thSense.eu:EG

http://community.bistudio.com/wiki/Code_Optimisation

Share this post


Link to post
Share on other sites

Well, it seems like you are asking more about how to run scripts in a server/client environment, rather than how to optimize them (to optimize them, they must first be functional...).

The answer is rather complicated, as it really depends on exactly what you want to do. Some commands have only a local effect, while some affect all machines, and either may or may not be what you want, depending on your goals. Some commands require the arguments to be local, some don't, and some will only have a global effect if the argument is local, but bug-out otherwise (for example setFuel, setVehicleAmmo, etc will bug out if you run them where the vehicle is not local).

Remember each computer runs scripts on his own, including the server itself. Also, on a hosted server (multiplayer->new), the host is also a player, so scripts must be written to work in that situation as well. If you run a command with a global effect on every machine, it will usually execute more times than you want (though sometimes you may want the opposite, which is why it's complicated and depends on what you try to do).

Try to only run publicVariable on the server, unless it's specifically updating the server (and possibly other clients) about something that happened to the player and could not be determined on the server without the client sending that variable over, such as the player using an action or getting killed.

Share this post


Link to post
Share on other sites

Thanks so much for the response guys. I did a lot of fiddling with the scripts just before you posted on here. You have most certainly opened my eyes a lot more.

Firstly if I have a fnc_respawn set up for any units who respawn, I would require 3 major variables.

The first is the WaveCount, the second is the Multiplier and the third is the Curr_Wavecount.

This will run in the unit's init at start...

 nul = this addMPEventHandler ["MPRespawn", {nul = _this execVM "fnc_respawn.sqf";}]; 

Running a script outside the init.sqf, and requiring the use of some variables would mean that the variables would have to be global am I right?

I need that code running in every playable unit's init field in order to check when they have respawned.

But in the fnc_respawn.sqf I can have the code running locally as it already runs on the client, the player who has respawned.

fnc_respawn.sqf

private ["_unit"];

_unit = _this select 0;

if (Current_Wave <= WaveCount - (round (WaveCount / Multiplier) )) then
{
   _unit synchronizeObjectsAdd ["SupplyDrop"];
   nul = _unit addMPEventHandler ["MPRespawn", {nul = _this execVM "fnc_respawn.sqf";}];
};

if (Current_Wave > WaveCount - (round (WaveCount / Multiplier) ))  then
{
   _unit synchronizeObjectsAdd ["Artillery"];
   _unit synchronizeObjectsAdd ["SupplyDrop"];
   nul = _unit addMPEventHandler ["MPRespawn", {nul = _this execVM "fnc_respawn.sqf";}];
};

if (Current_Wave >= WaveCount - (round (WaveCount / Multiplier / Multiplier) ))then
{
   _unit synchronizeObjectsAdd ["Helicopter"];
   _unit synchronizeObjectsAdd ["Artillery"];
   _unit synchronizeObjectsAdd ["SupplyDrop"];
   nul = _unit addMPEventHandler ["MPRespawn", {nul = _this execVM "fnc_respawn.sqf";}];
};

if (true) exitWith {};

Am I doing it right?

Please check the spoiler for updated scripts, i used the spoiler to reduce the size of my post.

The New Init.sqf

private ["_Timer","_WaveSpawnDelay"];

if !(isServer) then {waitUntil {!(isNull player)};};
waitUntil {BIS_fnc_init};
finishMissionInit;

FNC_Timer = compile preprocessFileLineNumbers "Scripts\fnc_timer.sqf";
FNC_Respawn = compile preprocessFileLineNumbers "Scripts\fnc_respawn.sqf";
FNC_Waves = compile preprocessFileLineNumbers "Scripts\fnc_waves.sqf";

//Sets the TimerDone variable to false as the Timer has not even started yet
TimerDone = false;

//Sets the fog to the current values
0 setFog [1,0.019,100];

//If it is the server's turn to read the init.sqf then do the following commands ON the server
if (isServer) then
   {
   _Timer = 30;
   WaveCount = 30; //Needs to be used Globally across all scripts, especially the respawn script
   _WaveSpawnDelay = 5;
   Multiplier = 1.7; //Had to use the Global Multiplier as it is required across all scripts AND ingame unit inits.

   nul = [WaveCount, Multiplier, _WaveSpawnDelay,_Timer] spawn FNC_Waves;

   //Allow players the ability to choose a class/loadout before respawn
   [west, "WEST1"] call BIS_fnc_addRespawnInventory;
   [west, "WEST2"] call BIS_fnc_addRespawnInventory;
   [west, "WEST3"] call BIS_fnc_addRespawnInventory;
   [west, "WEST4"] call BIS_fnc_addRespawnInventory;

   //Set spawn positions
   [west, getMarkerPos "Respawn1"] call BIS_fnc_addRespawnPosition;
   [west, getMarkerPos "Respawn2"] call BIS_fnc_addRespawnPosition;
   [west, getMarkerPos "Respawn3"] call BIS_fnc_addRespawnPosition;
};
sleep 1;

setViewDistance 1500;
setTerrainGrid 40;

Updated fnc_waves.sqf

#define Global_Multiplier 1.7

private ["_spawnType","_randomSquad","_type","_RoadSpawns","_Spawns","_Mot","_Curr_WaveCount","_waveCount","_multiplier","_waveSpawnDelay","_timer"];

_waveCount = _this select 0;
_multiplier = _this select 1;
_waveSpawnDelay = _this select 2;
_timer = _this select 3;

//Start Counting the amount of waves from 1 to _waveCount
For "_w" from 1 to _waveCount do
{
Current_Wave = _w; //Need this for the Respawn
_mot = 0;

//Spawns the timer
nul = [[_timer],  "FNC_Timer"] spawn BIS_fnc_MP;
sleep _timer;

//Shows a quiet hint telling the player(s) what wave they are currently on
HintSilent Format ["Wave %1 Commencing",_w];
sleep 5;

for "_s" from 1 to (round (_Curr_WaveCount * Global_Multiplier)) do
{
	_spawnType = if (count _this > 2) then {_this select 2} else {"inf"};

	//Checks for the current wave number against the total wave count and Global_Multiplier
       if (_Curr_WaveCount <= _waveCount - (round (_waveCount / _multiplier) )) then { _spawnType = ["inf"] call BIS_fnc_selectRandom};

       if (_Curr_WaveCount > _waveCount - (round (_waveCount / _multiplier) )) then{_spawnType = ["inf","sup"] call BIS_fnc_selectRandom};

       if (_Curr_WaveCount >= _waveCount - (round (_waveCount / _multiplier / _multiplier) )) then{_spawnType = ["inf","sup","mot"] call BIS_fnc_selectRandom};

	//Switch the type of group that spawns based on the random selection
	switch (_spawnType) do
	{
		//Choose a random Infantry group in the array
		case "inf": {
		    _randomSquad = ["OIA_InfSquad", "OIA_InfSquad_Weapons", "OIA_InfTeam", "OIA_InfTeam_AT"] call BIS_fnc_selectRandom;
		    _type = "Infantry";
		    };

		//Choose a random Motorized group in the array
		case "mot": {
		    _randomSquad = ["OIA_MotInf_AT", "OIA_MotInf_Team"] call BIS_fnc_selectRandom;
		    _type = "Motorized_MTP";
		    };

		//Choose a random Support group in the array
		case "sup": {
		    // configfile >> "CfgGroups" >> "East" >> "OPF_F" >> "Support" >> _randomSupportSquad
		    _randomSquad = ["OI_support_CLS", "OI_support_ENG", "OI_support_EOD"] call BIS_fnc_selectRandom;
		    _type = "Support";
		    };
	};

	//If the spawn type is equalled to the Motorized Groups, and there are Less than or Equal to 3 groups that are spawned....
	if (_spawnType == "mot" && (count _Mot <= 3)) then
	{
		//Add 1 to the variable until it makes 3
		_Mot = _Mot + 1;

		//Spawn the Motorized groups at any of these 3 road spawn points
		_RoadSpawns = ["RoadSpawn1","RoadSpawn2","RoadSpawn3"];

		//Spawns the group at a random marker, and set its side to EAST (Opfor)
		enemy_Group = [getMarkerPos _RoadSpawns, EAST, (configfile >> "CfgGroups" >> "East" >> "OPF_F" >> "Motorized_MTP" >> _randomSquad)] call BIS_fnc_spawnGroup;


		[enemy_Group, getMarkerPos "Defend", 150] call CBA_fnc_taskAttack;

		//Dont allow the group to flee
		enemy_Group allowFleeing false;

	} else
	{
		if (_spawnType == "mot") then
			{
			if (_Curr_WaveCount <= _waveCount - (round (_waveCount / _multiplier) )) then { _spawnType = ["inf"] call BIS_fnc_selectRandom};
			if (_Curr_WaveCount > _waveCount - (round (_waveCount / _multiplier) )) then{_spawnType = ["inf","sup"] call BIS_fnc_selectRandom};

			switch (_spawnType) do
				{

				//Choose a random Infantry group in the array
				case "inf": {
				    _randomSquad = ["OIA_InfSquad", "OIA_InfSquad_Weapons", "OIA_InfTeam", "OIA_InfTeam_AT"] call BIS_fnc_selectRandom;
				    _type = "Infantry";
				    };

				//Choose a random Support group in the array
				case "sup": {
				    // configfile >> "CfgGroups" >> "East" >> "OPF_F" >> "Support" >> _randomSupportSquad
				    _randomSquad = ["OI_support_CLS", "OI_support_ENG", "OI_support_EOD"] call BIS_fnc_selectRandom;
				    _type = "Support";
				    };
			};
		};

		//Spawn the Infantry groups at any of these spawn points
		_Spawns = ["Spawn1","Spawn2","Spawn3","Spawn4","Spawn5","Spawn6","Spawn7","Spawn8"] call BIS_fnc_selectRandom;

		//Spawns the group at a random marker, and set its side to EAST (Opfor)
		enemy_Group = [getMarkerPos _Spawns, EAST, (configfile >> "CfgGroups" >> "East" >> "OPF_F" >> _type >> _randomSquad)] call BIS_fnc_spawnGroup; publicVariable "enemy_Group";

		//Make the spawned group attack their objective
		[enemy_Group, getMarkerPos "Defend", 150] call CBA_fnc_taskAttack;

		//Dont allow the group to flee
		enemy_Group allowFleeing false;
	};

	//Use the defined Spawn Delay to stop the script proceeding until the delay is over
	sleep _waveSpawnDelay;

};

waitUntil {sleep 2; !Opfor_Present};
};
if (true) exitWith {};

Updated fnc_timer.sqf

private ["_timer"];

_timer = _this select 0;

for "_i" from _timer to 0 step -1 do {
   hintSilent format ["%1 Seconds left until next Wave", _i];
   sleep 1;
};

//Sets the Timer to True to say that the Timer has gone down to 0
TimerDone = true;
publicVariable "TimerDone";

sleep 5;

//After 5 seconds reset the Timer to false as it is not being used right now
TimerDone = false;
publicVariable "TimerDone";

if (true) exitWith {};

---------- Post added at 11:47 ---------- Previous post was at 11:43 ----------

Also forgot to ask what is the purpose of #Define? Is that for use with Params?

Share this post


Link to post
Share on other sites

I think addMPEventHandler is persistent over respawns, so you don't have to readd it after a units spawn.

Also it could cause problems with mods/missions/addons if you are using very common variables. Maybe some addons also uses Multiplier as variable and then they conflict with your mission. You should always add some kind of tag to a global variable like "WPS_Multiplier".

#Define is a so called preprocessor definition. Before every script is executed, the preprocessor will look over the syntax and pompting errors like missing semicolons. Also the preprocessor will replace definitions. For example you could write:

#define PI 3.141

And everytime you write PI in your script, it will be replaced with 3.141.

And maybe you should have a look at addpublicvariableeventhandler, this could save you from some headache

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
Sign in to follow this  

×