Jump to content
Sign in to follow this  
shuko

Taskmaster, creating and updating tasks for any set of players with JIP support

Recommended Posts

Taskmaster is an attempt to have a more flexible way to create and update tasks.

Features:

- JIP support

- Create tasks for any set of units/groups/sides

- Create tasks during missions (and have them shown for JIP)

- Easy way to update task states

- Uses the fancy taskHint

- Automatic marker hiding/showing

taskmaster.rar

Armaholic mirror

The rar contains couple of example missions and the script file.

The script and instructions can be found here as well:

http://derfel.org/arma2/scripts/briefing.sqf

It will always have the latest version.

FAQ:

Q: Why the tasks don't show up in editor preview?

A: Script uses a command called "playableunits", which does not work in single player/editor. The script is designed for multiplayer missions on dedicated servers.

Edited by Shuko

Share this post


Link to post
Share on other sites

I don't know why you need such a major script pack. All you need is to simply have 1 script that will handle tasks, and for adding/updating use [] spawn {waitUntil {condition}; ... }; That should be fully JIP-compatible, assuming the condition is correct for JIP players, that is, relies on something that is in the actual game (ex: !alive vehicle) or a public variable that is broadcasted by the server when a player connects.

In order to add the tasks for all units rather than the player's unit, all you need is to create a task array rather than a single task that will contain the task for all units, ex: _task1=[]; {_task1 = _task1 + [_x createTask...]} forEach unitsThatNeedTheTask;

Then when the task needs updating you for forEach _task1 rather than just the player's task.

Share this post


Link to post
Share on other sites

It's not a major pack just to support JIP, it's just one feature.

About the player vs unit array, two ways that both can be made to work. However, both will require you to loop through player/group array, mine just updates task for one (=player) instead of every single unit.

If you have a simple one script solution to add & update tasks for different factions and/or groups, which will allow me to also add new tasks during mission for them, I'm more than happy to use that instead.

Share this post


Link to post
Share on other sites

Here's an example for a mission I've made. Keep in mind if there's no kind of team switching available you may want to just use the original method of just creating them for the player.

In fact you probably don't even need the waitUnitl {!isNull player}; for this script, since you create them for units rather than for the player.

waitUntil {!isNull player};

{
_x createDiaryRecord ["Diary", ["Credits", "Mission by GalZohar"]];
_x createDiaryRecord ["Diary", ["Execution", "Your team is to reach Utes by boat, and use laser designation to guide hellfire missiles to their targets."]];
_x createDiaryRecord ["Diary", ["Friendly forces", "Friendly forces consist of 10 marines on a RIHB and an <b>AH-1Z on standby, which must be returned unharmed</b>."]];
_x createDiaryRecord ["Diary", ["Enemy forces", "The island is fully controlled by insurgents. On top of the patrols surrounding the islands and holding the towns, there are also 2 mounted patrols and 3 mounted squads and an Mi-8 on standby, expect them to react once you are detected."]];
_x createDiaryRecord ["Diary", ["Mission", "Destroy insurgent objectives in Utes"]];
} forEach blueforUnits;

_countBlueforUnits = count blueforUnits;

_taskHelo = [];
{_taskHelo = _taskHelo + [_x createSimpleTask [""]];} forEach blueforUnits;
for "_i" from 0 to ((_countBlueforUnits)-1) do
{
(_taskHelo select _i)
	setSimpleTaskDescription
	["Return the cobra to <marker name='mrkBase'>base</marker> unharmed.", "Return cobra unharmed", "base"];
};

_taskMi8 = [];
{_taskMi8 = _taskMi8 + [_x createSimpleTask [""]];} forEach blueforUnits;
for "_i" from 0 to ((_countBlueforUnits)-1) do
{
(_taskMi8 select _i)
	setSimpleTaskDescription
	["Destroy the standby Mi-8, it should be somewhere around the <marker name='mrkAirfield'>airfield</marker>.", "destroy Mi-8", "airfield"];
};

_taskSU25 = [];
{_taskSU25 = _taskSU25 + [_x createSimpleTask [""]];} forEach blueforUnits;
for "_i" from 0 to ((_countBlueforUnits)-1) do
{
(_taskSU25 select _i)
	setSimpleTaskDescription
	["Destroy the SU25 in the <marker name='mrkHanger'>hanger</marker>.", "Destroy SU25", "hanger"];
};

_taskC130J = [];
{_taskC130J = _taskC130J + [_x createSimpleTask [""]];} forEach blueforUnits;
for "_i" from 0 to ((_countBlueforUnits)-1) do
{
(_taskC130J select _i)
	setSimpleTaskDescription
	["Destroy the <marker name='mrkC130J'>C-130J</marker> on the runway.", "Destroy C-130J", "C-130J"];
};

_taskTower = [];
{_taskTower = _taskTower + [_x createSimpleTask [""]];} forEach blueforUnits;
for "_i" from 0 to ((_countBlueforUnits)-1) do
{
(_taskTower select _i)
	setSimpleTaskDescription
	["Destroy the airfield's <marker name='mrkTower'>control tower</marker>.", "Destroy control tower", "control tower"];
};

_taskAmmo = [];
{_taskAmmo = _taskAmmo + [_x createSimpleTask [""]];} forEach blueforUnits;
for "_i" from 0 to ((_countBlueforUnits)-1) do
{
(_taskAmmo select _i)
	setSimpleTaskDescription
	["Destroy the 3 ammo caches at the <marker name='mrkAirfield'>airfield</marker>.", "Destroy ammo caches", "airfield"];
};


_taskMi8 spawn
{
waitUntil {helodestroyed};
hint "Mi-8 destroyed.";
for "_i" from 0 to ((count _this)-1) do
{
	(_this select _i) setTaskState "Succeeded";
};
};

_taskSU25 spawn
{
waitUntil {aircraft1destroyed};
hint "SU25 destroyed.";
for "_i" from 0 to ((count _this)-1) do
{
	(_this select _i) setTaskState "Succeeded";
};
};

_taskC130J spawn
{
waitUntil {aircraft2destroyed};
hint "C-130J destroyed.";
for "_i" from 0 to ((count _this)-1) do
{
	(_this select _i) setTaskState "Succeeded";
};
};

_taskTower spawn
{
waitUntil {towerdestroyed};
hint "Control tower destroyed.";
for "_i" from 0 to ((count _this)-1) do
{
	(_this select _i) setTaskState "Succeeded";
};
};

_taskAmmo spawn
{
waitUntil {ammodestroyed};
hint "Ammo caches destroyed.";
for "_i" from 0 to ((count _this)-1) do
{
	(_this select _i) setTaskState "Succeeded";
};
};

_taskHelo spawn
{
waitUntil {cobraFailed};
hint "Helo took critical damage.";
for "_i" from 0 to ((count _this)-1) do
{
	(_this select _i) setTaskState "Failed";
};
};

_taskHelo spawn
{
waitUntil {boolComplete};
for "_i" from 0 to ((count _this)-1) do
{
	(_this select _i) setTaskState "Succeeded";
};
};

Share this post


Link to post
Share on other sites

Yes, as I already said, tasks can be created and updated per-unit or per-player basis. Your script still doesn't do what I asked for. Maybe an example helps (probably not :D).

We have 3 USMC groups and 2 CDF groups. Let's call them u1, u2, u3, c1,c2.

In the beginning of the mission, for briefing, we want to add taskA for groups u1 and u2, taskB for u3. Groups c1 and c2 will get taskC. During mission when taskA is succeeded, groups u1 and u2 will receive new task taskD, but if taskA is failed, they will get taskC. After taskD is done they will get taskC (if it's still active for groups c1 and c2). On the other hand, if taskC has been done before taskA is done, groups c1 and c2 will get taskA (unless A is done, then they get D), then taskD after that in the same way as groups u1 and u2. Once tasks C and D are done, all four groups will get taskE.

Keyword here is flexibility, to be able to add and update tasks for whoever and whenever, not just static addition at the start of the mission.

BTW, why do you go through your unit list 13 times when it could be done just once?

Edited by Shuko

Share this post


Link to post
Share on other sites

You could shove all the array creations in the first loop, yeah, not that it's going to matter much becuase it's only ran once at the beginning of the missions and does very little while the mission is actually running, so I didn't really focus on optimization. After initialization is done (which is very very very quick), all that is running is a few waitUntil commands.

If you want to add a task when a certain condition is met, all you have to do is something like this:

[] spawn
{
  waitUntil {condition required to start the next task};
  //add the task and its handler(s) in the same fashion as before
}

My script example runs forEach blueforUnits which is an array created by init.sqf - you could do it for any array you choose, as well as add different tasks to different units.

You don't need more than 1 script running to handle the tasks (or anything, for that matter), as long as the conditions for the tasks are properly handled and publicVariabled elsewhere.

Share this post


Link to post
Share on other sites

dont need any of this since 1.03 patch BIS fixed it all :) all the JIP / Dead errors etc lol

Share this post


Link to post
Share on other sites
dont need any of this since 1.03 patch BIS fixed it all :) all the JIP / Dead errors etc lol

galzohar's fixes are for TeamSwitch, which still 'breaks' breifings. 1.03 fixed losing breifings after respawn/JIP, but not TeamSwitch, since most briefings are only assigned to players rather than AI which is required in TeamSwitch mode.

My question about the above script is, are all those spawned waitUntil's better than using triggers on the map?

Share this post


Link to post
Share on other sites

In terms of efficiency it's probably the same thing. In term of easiness to perform, it's much easier. When the condition for the task action is something set by the server and publicVariabled every time a player connects, you can be sure that it'll work fine for JIP players. If you set the task to complete via a trigger and then the trigger deactivates, JIP players relying on that trigger will not properly update the task. If the trigger instead sets a variable to true, then that variable can later be sent to JIP players via onPlayerConnected (which should execute a script/code that publicvariables the relevant variables that are used in the waitUntil conditions), then the task can remain completed for JIP players even though the trigger is no longer active or even no longer exists.

Even if the trigger is always active (condition is true) once the task is complete and never changes its state, the trigger's task updating script will still need to wait until the task initialization is complete, and you'll have to make the task variables global so that the trigger's code/script can see them. So you're probably actually being more efficient by having the trigger change a boolean variable value anyway (and if the trigger's state is always the same as the task state then you can save the publicVariable every time a player connects, since the trigger will update the variable).

Edited by galzohar

Share this post


Link to post
Share on other sites

galzohar approach (unit specific tasks and waituntil powered), just less copy-pasting needed to use. Also able to define condition to who the task is to be created for.

/*
Executed from init.sqf

Example:

[[
 ["group _x == gSnipers","Kill",["Kill General. He's at <marker name='mPetrovka'>Petrovka</marker>.","Kill general","General"]],
 ["group _x == gSpetsnaz","Destroy",["There is a new prototype plane in the <marker name='mAirfield'>airfield</marker> being prepared for tests. Destroy it.","Destroy plane","Plane"]]
],[
 ["true",["About","Made by Shuko"]],
 ["side _x == EAST",["Mission","Do stuff"]]
]] execvm "briefing.sqf";


To complete tasks/update task states, variable with name objTaskName is used. It should be PV'd for JIPs to receive the update as well.

Examples:

objKill = "Succeeded"; publicvariable "objKill";
objDestroy = "Canceled"; publicvariable "objDestroy";


New task can be created during mission using same method as creating briefing.

Example:

nul = [[["side _x == EAST","Extract",["Get to teh choppa!","Extraction","Extract"]]],[]] execvm "briefing.sqf";
*/

waituntil {!isnull player};
private ["_t","_y"];
{
 _y = _x;
 call compile format ["tsk%1 = []",(_y select 1)];
 {
   if (call compile format ["%1",(_y select 0)]) then {
     _t = _x createsimpletask [""];
     _t setsimpletaskdescription (_y select 2);
     call compile format ["tsk%1 set[count tsk%1,_t]",(_y select 1)];
   };
 } foreach allunits;
 (_y select 1) spawn {
   call compile format ["waituntil {!isnil ""obj%1""}; {_x settaskstate obj%1} foreach tsk%1;",_this];
 };
} foreach (_this select 0);
{
 _y = _x;
 {
   if (call compile format ["%1",(_y select 0)]) then {
     _x creatediaryrecord ["Diary",(_y select 1)];
   };
 } foreach allunits;
} foreach (_this select 1);

---------- Post added at 07:58 PM ---------- Previous post was at 07:38 PM ----------

Of course, you are also able to add new tasks middle of a mission as well. Using the same method as creating the briefing.

nul = [[["side _x == EAST","Extract",["Get to teh choppa!.","Extraction","Extract"]]],[]] execvm "briefing.sqf";

Kind of messy at the moment, might add few checks to make is cleaner.

Edited by Shuko

Share this post


Link to post
Share on other sites

Shk,

Is this the Holy Grail, I am struggling getting my task’s to update with my co-op mission when I host the mission using my PC as the server???

Will this fix my problem??

Thanks

Cool Breeze-ICON

Share this post


Link to post
Share on other sites

Don't know about the Holy Grail. This was designed and tested only on dedicated server, so I have no idea how it behaves on hosted server. Please try it out and write here the result.

Copy this to your mission folder: http://derfel.org/arma2/scripts/briefing.sqf

Briefing and tasks are created in init.sqf, so you don't need to edit the briefing.sqf.

Example how they are created:

[[
 ["group _x == gSnipers","Kill",["Kill General. He's at <marker name='mPetrovka'>Petrovka</marker>.","Kill general","General"]],
 ["group _x == gSpetsnaz","Destroy",["There is a new prototype plane in the <marker name='mAirfield'>airfield</marker> being prepared for tests. Destroy it.","Destroy plane","Plane"]]
],[
 ["true",["About","Made by Shuko"]],
 ["side _x == EAST",["Mission","Do stuff"]]
]] execvm "briefing.sqf";

First you give the tasks and then notes. You can specify which units (groups, side etc) you want to create the tasks/notes. The condition uses directly sqf's IF syntax, so you can create a condition however you want, where _x is the unit (=player). Next you give the task's name, which is used when you want to update it. Rest are just the descriptions. Notes use the same IF system.

Updating tasks is as simple as just doing the following in script/trigger:

objKill = "Succeeded"; publicvariable "objKill";

objXXXX where XXXX is the task name you gave it.

Share this post


Link to post
Share on other sites

Hi Shk,

Ok, I now have several tasks working but I would like to start the mission with the 1st task set as current task and once the 1st task has been completed task 2 automatically set as current and so one.

Thanks

Cool Breeze-ICON

Edited by Cool Breeze-ICON

Share this post


Link to post
Share on other sites

Here is a very basic example: http://derfel.org/arma2/scripts/taskmaster.rar

Here is the breakdown of it. We create one task and one note for all players:

[[
 ["true","Task1",["Clear the airfield. Kill all enemies.","Remove enemy forces","Clear airfield"]]
],[
 ["true",["Note1","Note 1 text field"]]
]] execvm "briefing.sqf";

Filter is set as "true", which means task and note will be added to every player, no matter which side or group they are on. I gave the first task name "Task1". Other fields are just the texts which are shown in briefing (exactly same way as the setSimpleTaskDescription which is used in the conventional briefing files).

Then I added couple of units and a trigger which will check if the objective is done. In this case it's checking if the enemy unit is dead (!alive enemyDude). Any condition you want can be used just like with other briefings.

If the enemy is killed, objective is completed. Trigger will fire and run this code:

objTask1 = "Succeeded"; publicVariable "objTask1"

This will set the task as succeeded. There is no hint by default, but you can add that to the trigger if you want.

Edited by Shuko

Share this post


Link to post
Share on other sites

Hi Shk,

Thanks for the basic mission very helpful. Just hosted a game with a friend and all tasks completed successfully. :yay:

I have 2 requests:-

1. I would now like to start the mission with the 1st task set as current task and once the 1st task has been completed task 2 automatically set as current and so on.

2. I would like to use the fancy hints as detailed in post http://forums.bistudio.com/showthread.php?t=73424.

Hope you can help. :bounce3::bounce3::bounce3:

Thanks

Cool Breeze-ICON

Share this post


Link to post
Share on other sites

For non-dedicated it should work just as well as long as you run it for every client rather than for every non-server. That is, use !isNull player || !isServer, rather than just !isServer, or simply run it for everyone including the server.

Share this post


Link to post
Share on other sites

1. I would now like to start the mission with the 1st task set as current task and once the 1st task has been completed task 2 automatically set as current and so on.

2. I would like to use the fancy hints as detailed in post http://forums.bistudio.com/showthread.php?t=73424.

I'll try to add that current thing tonight, the hint you can do in the trigger:

objTask1 = "Succeeded"; publicVariable "objTask1"; nul = [objNull, objNull, tskTask1, "SUCCEEDED"] execVM "CA\Modules\MP\data\scriptCommands\taskHint.sqf";

Share this post


Link to post
Share on other sites

Hi Shk,

I have added your code to my trigger and had to re-order the code to show the task hint as follows:-

null = [objNull, objnull, objTask1, "SUCCEEDED"] execVM "CA\Modules\MP\data\scriptCommands\taskHint.sqf"; objTask1 = "Succeeded"; publicVariable "objTask1";

The hint display's but it reads "TASK ACCOMPLISHED: any". Obviously "any" should be replaced with task title.

Hope this helps,

Cool Breeze-ICON

Share this post


Link to post
Share on other sites

I rewrote the whole thing last night/today. Check out the instructions either from the briefing.sqf or the first post of this thread. Here is the updated files: taskmaster.rar

Share this post


Link to post
Share on other sites

Hi Shk,

Ok, tried the modification out and my findings are:-

1. I had to re-arrange tasks in the init.sqf file in ascending order to show in the correctly order in the briefing screen. This was no issue and actual makes more sense.

2. Using the code bellow now display's the fancy task hint with task title once it has been completed which is good.

nul = ["Task1","Succeeded"] call TASKMASTER_upd; objTask1 = "Succeeded"; publicVariable "objTask1"

3. There is no hint at the start of the mission telling you that task 1 has been assigned.

4. For some reason there is still an issue where once task 1 has been completed it assigns task 2 before showing you that task 1 is accomplished.

Hope this helps again, you are very close.

I will upload my mission and post you the link.

Thanks

Cool Breeze-ICON

Share this post


Link to post
Share on other sites
3. There is no hint at the start of the mission telling you that task 1 has been assigned.

It's not even supposed to do that, because it could get a bit spammy if someone decides to give a lot of tasks.

4. For some reason there is still an issue where once task 1 has been completed it assigns task 2 before showing you that task 1 is accomplished.

I don't know why it does that exactly as the hints are in the right order (complete -> assigned). Something to do with the BIS's taskHint command. Maybe it queues the hints to be shown and goes through them in reversed order.

Share this post


Link to post
Share on other sites

0.5 Added: Two way marker support; basic setSimpleTaskDestination creation and alpha change (show hidden markers) for normal editor placed markers.

0.4 Added: tskTaskNameCompleted boolean variable (for example: tskKillCompleted). Can be used to check if the task is completed (succeeded/failed/canceled).

Share this post


Link to post
Share on other sites

I like this a lot.

First I was a bit puzzled by it but it's actually much simpler than the methods I was using before.

Hyvää työtä!

Share this post


Link to post
Share on other sites

I know it can be somewhat difficult to get the hang of it at first. It doesn't help that I suck at writing readme/instructions.

All questions are welcome, in here or in PM.

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  

×