Visa ämne
Anrop » ArmA 3 » Editing
 Skriv ut ämne
ZluskeN's Wave Respawn
Arma 3 har redan ett system för "wave" respawn, dessa vågor avgörs dock av en nedräknings-timer (alla respawnar x antal sekunder efter att första spelaren dött) och jag ville ha ett system som går på antal döda spelare i stället (alla spawnar efter att x antal spelare dött). Efter mycket testande med hjälpsamma anropare och en hel del svordomar från mitt håll har jag nu lyckats få till ett fungerande system:

i mission-editorn, placera en respawn-marker, döp den till respawn_east, respawn_west eller respawn_guerrila (beroende på spelar-sida/or) Det här kommer att vara spelarnas "väntrum". De respawnar hit direkt när de dör, men får inte kontroll över sin enhet förrän de är rätt antal på platsen. Om uppdraget har flera sidor är det viktigt att dessa markers är minst 100 meter ifrån varandra. Sätt dem på en säker plats långt från striden och överväg att "rama in" dem med hesco-barriärer eller liknande.

Placera ett objekt på kartan, döp det till zsn_respawn_east, zsn_respawn_west eller zsn_respawn_guerrila enligt ovanstående. Det är hit spelarna teleporteras när vågen initieras.
(Stöd för markers borttaget, det skapar problem på dedikerade servrar och objekt är smidigare på alla sätt)

Exempelmission: https://drive.google.com/open?id=0B77...GNwOUZXS1U

Github: https://github.com/ZluskeN/ZSN_wavere...le.Stratis

Raden

Citera

[west, 4, -1, true, false] remoteExec ["zsn_waverespawn", 2];
Är där magin sker. Den innehåller fem parametrar:
Side, vilken sida det gäller, default är west
Size, hur stor vågen är i antal spelare, default är 4 st.
Count, antal vågor om man vill begränsa, default är -1 (oändligt)
Radio, om den nya vågens chef ska utrustas med en radio, default true
PvP, om det är en PvP-match. Det har att göra med end-triggers, så att överlevande sida vinner när förlorarna får slut på spawns, default är false, alltså coop

Raden kan flyttas ut ur initserver.sqf och exekveras var som helst i uppdraget (sätt det i on activation på en trigger och se till att den returnerar "true") Den kan också exekveras flera gånger och för olika sidor om man vill ändra någon regel i scriptet under spelets gång.

Vill man initiera scriptet för flera sidor i PvP lägger man bara till en likadan rad under den första med nya parametrar för den andra sidan.

Kod borttagen, pga skum teckenhantering på sidan, se koden på github i stället
Redigerat av ZluskeN den 2017-08-27 18:38
onplayerkilled.sqf
Kod Källa  

[player] join grpnull;
["Initialize", [player, [side player], false, false, true, true, true, true, true, true]] call BIS_fnc_EGSpectator;

Så sätter den automagiskt side till spelaren side

Kan vara bra att kolla så Task Force Radio är aktiverat med och att radion faktiskt finns,

Kod Källa  

if (isClass(configFile >> "CfgWeapons" >> "tf_pnr1000a")) then
{
    // Lägg till radios
};


Undvik helst att använda BIS som namespace för funktionerna utan använd ett eget om BIS får för sig att skapa egna i framtiden :)
Tack, Dahlgren, jag har sett bis-prefixet så mycket i exemplen att jag trodde det var obligatoriskt. Scriptet uppdaterat.
Redigerat av ZluskeN den 2016-12-06 20:19
Snyggt!

remoteExec kan ta en enhet eller en lista på enheter och körs då bara på de klienterna istället för alla, smidigt om man vill köra kod selektivt. En av mina favoriter bland BIS nya MP kommandon. Se anropet till zsn_killspectator.

Kommandot join tar en lista med enheter så du kan skicka in thisList direkt som parameter istället för att sätta klamrar runt den, d.v.s. en lista med en lista på enheter (ja, ska vara dubbelt där), och då behöver du inte loopen längre :). I din nuvarande kod blir det bara thisList join grp som du även ser nedan.

Som nästa tips hade jag flyttat innehållet i onActivation till en egen funktion pris som de andra zsn_killspectator etc och anropat den med thislist. Så kan du redigera allt beteende direkt i init.sqf :)

onActivation
Kod Källa  

thisList zsn_waveready;


init.sqf
Kod Källa  

zsn_waveready = {
    params ["_units"];
    _grp = createGroup east;
    _units join _grp;
    _grp remoteExec ["zsn_spawnwave", 2];
    remoteExec ["zsn_killspectator", _grp];
};


Med risk för fel då jag inte kan testa atm :D

Här är en ännu mer trippy som tar ut side för gruppen enligt första soldaten som ska vara i gruppen

init.sqf
Kod Källa  

zsn_waveready = {
    params ["_units"];
    _side = side (_units select 0);
    _grp = createGroup _side;
    _units join _grp;
    _grp remoteExec ["zsn_spawnwave", 2];
    remoteExec ["zsn_killspectator", _grp];
};


Känns som något man kanske skulle göra en automagiskt modul av på sikt så man bara kan droppa in och länka ihop de enheter man vill ha? Vore ju awesome både för egen respawn men borde ju typ gå göra AI waves med! :o
Bra jobbat Zlusken, har ofta haft tankar att skappa ett uppdrag med sådan respawn men har haft problem att få det att funka.
Mycket bra!

Finns det något sätt att ställa in antal respawn tickets?
Borde räcka med en variabel som räknar och en if-sats i onActivation/zsn_spawnwave/zsn_waveready
@Dahlgren Låter bra, ska testa det så får vi se hur det går. Jag hade en hel del problem med den där triggern, så jag låter det som står vara tills vidare.

@Alekanderu Ticket-systemet blir jag inte riktigt klok på, så vitt spelet ser så är det ju helt vanlig base-respawn, så det borde fungera rakt av.
Jag börjar misstänka att det helt enkelt är sönder sen några patcher tillbaka. Jag tror det bästa sättet är att lägga till en egen counter, som Dahlgren säger.

man kunde lägga till som condition i triggern:
{Side _x == EAST} count thislist >= 4 AND ticketsleft >= 1

och i On Activation
ticketsleft = ticketsleft - 1;

i init.sqf
ticketsleft = 3; //antal respawn-waves

typ så, med reservation för att syntaxen kanske behöver se annorlunda ut och att ticketsleft säkert behöver globaliseras på nåt bra sätt.
Borde typ fungera ja :)

Kod Källa  

{_x join (grp)} forEach [thislist];

kan förenklas till
Kod Källa  

thisList join grp;

Fixat
killspectator borde även bara köras för de enheter som är påverkade av triggern, sparar in lite nätverkstrafik med! :)

Kod Källa  

remoteExec ["zsn_killspectator", thisList];

Ändrat det nu. Jag gillar din idé om zsn_waveready som funktion, frågan är hur den ska anropas.
Tror du det räcker med
Kod Källa  

thisList zsn_waveready;
eller borde man kanske köra någon slags remoteexec på den också?
Hmm, den borde ju typ bara aktivera på servern så

condition
Kod Källa  

isServer && ({side _x == EAST} count thisList >= 4)


on activation
Kod Källa  

thisList zsn_waveready;


Resten borde zsn_waveready kunna ta hand om, den kör clientfunktionerna där det behövs o resten bara på servern. Inte helt säker på hur den agerar med triggers :P

conditionen efter isServer skulle också kunna stoppas in i en funktion och anropas så behöver man bara justera side i init.sqf

Vill du göra det ännu lättare kan du ju skippa side checken i condition och skapa en variabel typ zsn_respawn_threshold = 4; i init.sqf och så kolla den i condition så behöver man bara peta i init.sqf för att justera :)
Okej, efter att ha sovit på saken och funderat lite ytterligare på Dahlgrens input har jag kommit på ett nytt sätt att göra det på. Obs att det här är helt otestad kod, saker kommer säkert behöva ändras, men värt att testa. onplayerkilled, trigger-activation, zsn_waveready och zsn_spawnwave är ihopslagna till ett script som körs på servern. I teorin ska det automagiskt identifiera sida, dela ut rätt radio och hantera spectating. Två variabler i början används för att bestämma antal vågor samt antal spelare per våg. jag lägger det här i stället för att uppdatera original-posten, då jag inte vet om/hur bra det fungerar. Uppdatering: testat och funkar offline, ej testat på dedikerad server.

i mission-editorn, placera en respawn-marker, döp den till respawn_east, respawn_west, respawn_guerrila eller respawn_civilian (beroende på spelar-sidan) Det här kommer att vara spelarnas "väntrum". De respawnar hit direkt när de dör, men får inte kontroll över sin enhet förrän de är rätt antal på platsen. sätt den på en säker plats långt från striden, överväg att "rama in" den med hesco-barriärer eller liknande.

Sätt ut ett objekt på kartan, döp det till zsn_respawn_east, zsn_respawn_west, zsn_respawn_guerrila eller zsn_respawn_civilian. enligt ovanstående Det är hit spelarna teleporteras när vågen initieras.

Klistra in följande i din init.sqf och mata in dina önskade inställningar i översta raden. Scriptet stödjer tre variabler:
side (west, east, guer, eller civ)
count (antal vågor, siffra)
size (storleken på vågorna, också en siffra)

init.sqf (Textfil i din mission-mapp)
Kod Källa  

[side, count, size] remoteExec ["zsn_waverespawn", 2];

zsn_waverespawn =
{
   _zsn_side = (_this select 0);

   _zsn_wavecount = (_this select 1);

   _zsn_wavesize = (_this select 2);

   if (_zsn_side == east) then
   {
      zsn_wavecount_east = _zsn_wavecount;

      publicVariable "zsn_wavecount_east";

      zsn_wavesize_east = _zsn_wavesize;

      publicVariable "zsn_wavesize_east";
   
      zsn_spawn_trg = createTrigger ["EmptyDetector", getmarkerPos "respawn_east"];

      zsn_spawn_trg setTriggerActivation ["east", "PRESENT", true];

      zsn_spawn_trg setTriggerStatements ["isServer && {Side _x == east} count thislist >= zsn_wavesize_east && zsn_wavecount_east >= 1", "thisList call zsn_spawnwave;",""];
   }
   else
   {
      if (_zsn_side == west) then
      {
         zsn_wavecount_west = _zsn_wavecount;

         publicVariable "zsn_wavecount_west";

         zsn_wavesize_west = _zsn_wavesize;

         publicVariable "zsn_wavesize_west";
     
         zsn_spawn_trg = createTrigger ["EmptyDetector", getmarkerPos "respawn_west"];

         zsn_spawn_trg setTriggerActivation ["west", "PRESENT", true];

         zsn_spawn_trg setTriggerStatements ["isServer && {Side _x == west} count thislist >= zsn_wavesize_west && zsn_wavecount_west >= 1", "thisList call zsn_spawnwave;",""];
      }
      else
      {   
         if (_zsn_side == guer) then
         {
            zsn_wavecount_guer = _zsn_wavecount;

            publicVariable "zsn_wavecount_guer";

            zsn_wavesize_guer = _zsn_wavesize;

            publicVariable "zsn_wavesize_guer";
         
            zsn_spawn_trg = createTrigger ["EmptyDetector", getmarkerPos "respawn_guerrila"];

            zsn_spawn_trg setTriggerActivation ["guer", "PRESENT", true];

            zsn_spawn_trg setTriggerStatements ["isServer && {Side _x == guer} count thislist >= zsn_wavesize_guer && zsn_wavecount_guer >= 1", "thisList call zsn_spawnwave;",""];
         }
         else
         {   
            zsn_wavecount_civ = _zsn_wavecount;

            publicVariable "zsn_wavecount_civ";

            zsn_wavesize_civ = _zsn_wavesize;

            publicVariable "zsn_wavesize_civ";
         
            zsn_spawn_trg = createTrigger ["EmptyDetector", getmarkerPos "respawn_civilian"];

            zsn_spawn_trg setTriggerActivation ["civ", "PRESENT", true];

            zsn_spawn_trg setTriggerStatements ["isServer && {Side _x == civ} count thislist >= zsn_wavesize_civ && zsn_wavecount_civ >= 1", "thisList call zsn_spawnwave;",""];
         };
      };
   };
};

zsn_spawnwave =
{
   _units = _this;

   _side = side (_units select 0);

   _grp = createGroup _side;

   _units join _grp;

   if (isClass(configFile >> "CfgPatches" >> "task_force_radio_items")) then
   {
      if (_side == east) then
      {
         radio = "tf_pnr1000a";
      }
      else
      {
         if (_side == west) then
         {
            radio = "tf_rf7800str";
         }
         else
         {   
            radio = "tf_anprc154";
         };
      };
   }
   else
   {
      radio = "itemradio";
   };
   leader _grp additem radio;

   leader _grp assignItem radio;

   remoteExec ["zsn_killspectator", _units];

   if (_side == east) then
   {
      {_x setPosASL (["zsn_respawn_east"] call BIS_fnc_randomPosTrigger)} forEach _units;

      zsn_wavecount_east = zsn_wavecount_east - 1;

      publicVariable "zsn_wavecount_east";
   }
   else
   {
      if (_side == west) then
      {
         {_x setPosASL (["zsn_respawn_west"] call BIS_fnc_randomPosTrigger)} forEach _units;

         zsn_wavecount_west = zsn_wavecount_west - 1;

         publicVariable "zsn_wavecount_west";
      }
      else
      {
         if (_side == guer) then
         {
            {_x setPosASL (["zsn_respawn_guerrila"] call BIS_fnc_randomPosTrigger)} forEach _units;

            zsn_wavecount_guer = zsn_wavecount_guer - 1;

            publicVariable "zsn_wavecount_guer";
         }
         else
         {
            {_x setPosASL (["zsn_respawn_civilian"] call BIS_fnc_randomPosTrigger)} forEach _units;

            zsn_wavecount_civ = zsn_wavecount_civ - 1;

            publicVariable "zsn_wavecount_civ";
         };
      };
   };
};

zsn_killspectator =
{
      ["Terminate"] call BIS_fnc_EGSpectator;
};

onplayerkilled.sqf (Textfil i din mission-mapp)
Kod Källa  

[player] join grpnull; //spelaren lämnar sin grupp, försvinner från STHud och liknande

["Initialize", [player, [side player], false, false, true, true, true, true, true, true]] call BIS_fnc_EGSpectator;  //spelaren placeras i spectator mode

Redigerat av ZluskeN den 2016-12-19 20:59
Mycket snyggt!!

Kod Källa  

remoteExec ["zsn_killspectator", units _grp];

Kommer antagligen att misslyckas då zsn_killspectator bara deklareras för servern, inte för klienter så när den försöker köras remote kommer den inte hittas. Antingen får du flytta zsn_killspectator till init.sqf eller så får du anropa BIS_fnc_EGSpectator direkt i din remoteExec till unitsen.

Du kan förenkla alla units _grp till bara _units med eftersom det är samma enheter.
Misstänkte det, men i så fall borde de kunna tas bort från initserver? Uppdaterat posten

EDIT: Eller bara köra allt i init.sqf, så blir det lite mindre stök i mission-mappen
Redigerat av ZluskeN den 2016-12-07 14:14
Hade nog även bytt _wavesize och _wavecount mot zsn_wavesize och zsn_wavecount för att vara konsekvent med de övriga globala variablerna Smile
Testade just lite i editorn, och jag har problem med att skjuta in thislist i scriptet. Minns nu att det var därför allt som har med gruppering att göra ligger i triggern.
Jag har i alla fall uppdaterat original-posten med det som fungerar. Stöd finns nu för begränsning av antal waves. Intieringen av spectating fungerar bättre i onplayerkilled, så jag rekommenderar att man använder det tills vidare. Bytte namn på variablerna också Smile
Det ska inte vara några problem, thisList är bara en tillfällig variabel som håller i en array med alla enheter unit triggerns blocks. Du borde inte behöva remoteExec där heller. En till förbättring kan även vara att skapa triggern i koden så behöver man bara placera ut en marker.
Så, nu har jag gjort lite tester och kommit fram till lite optimeringar. Scriptet styrs nu med tre variabler som matas in i översta raden, resten sköter sig självt. Den översta raden kan dessutom flyttas runt och exekveras i triggers eller andra script eller vad som. Det bör nu finnas stöd för alla sidor, i fall man vill köra PvP, dock ej testat. Nu behöver man bara placera ut två markers och mata in sina värden. Tidigare posten är uppdaterad, då det scriptet inte fungerade alls. Behöver testas på dedikerad server.

Citera

ZluskeN skrev:

i mission-editorn, placera en respawn-marker, döp den till respawn_east, respawn_west, respawn_guerrila eller respawn_civilian (beroende på spelar-sidan) Det här kommer att vara spelarnas "väntrum". De respawnar hit direkt när de dör, men får inte kontroll över sin enhet förrän de är rätt antal på platsen. sätt den på en säker plats långt från striden, överväg att "rama in" den med hesco-barriärer eller liknande.

Sätt ut ett objekt på kartan, döp det till zsn_respawn_east, zsn_respawn_west, zsn_respawn_guerrila eller zsn_respawn_civilian. enligt ovanstående Det är hit spelarna teleporteras när vågen initieras.

Klistra in följande i din init.sqf och mata in dina önskade inställningar i översta raden. Scriptet stödjer tre variabler:
side (west, east, guer, eller civ)
count (antal vågor, siffra)
size (storleken på vågorna, också en siffra)

init.sqf (Textfil i din mission-mapp)
Kod Källa  

[side, count, size] remoteExec ["zsn_waverespawn", 2];

zsn_waverespawn =
{
   _zsn_side = (_this select 0);

   _zsn_wavecount = (_this select 1);

   _zsn_wavesize = (_this select 2);

   if (_zsn_side == east) then
   {
      zsn_wavecount_east = _zsn_wavecount;

      publicVariable "zsn_wavecount_east";

      zsn_wavesize_east = _zsn_wavesize;

      publicVariable "zsn_wavesize_east";
   
      zsn_spawn_trg = createTrigger ["EmptyDetector", getmarkerPos "respawn_east"];

      zsn_spawn_trg setTriggerActivation ["east", "PRESENT", true];

      zsn_spawn_trg setTriggerStatements ["isServer && {Side _x == east} count thislist >= zsn_wavesize_east && zsn_wavecount_east >= 1", "thisList call zsn_spawnwave;",""];
   }
   else
   {
      if (_zsn_side == west) then
      {
         zsn_wavecount_west = _zsn_wavecount;

         publicVariable "zsn_wavecount_west";

         zsn_wavesize_west = _zsn_wavesize;

         publicVariable "zsn_wavesize_west";
     
         zsn_spawn_trg = createTrigger ["EmptyDetector", getmarkerPos "respawn_west"];

         zsn_spawn_trg setTriggerActivation ["west", "PRESENT", true];

         zsn_spawn_trg setTriggerStatements ["isServer && {Side _x == west} count thislist >= zsn_wavesize_west && zsn_wavecount_west >= 1", "thisList call zsn_spawnwave;",""];
      }
      else
      {   
         if (_zsn_side == guer) then
         {
            zsn_wavecount_guer = _zsn_wavecount;

            publicVariable "zsn_wavecount_guer";

            zsn_wavesize_guer = _zsn_wavesize;

            publicVariable "zsn_wavesize_guer";
         
            zsn_spawn_trg = createTrigger ["EmptyDetector", getmarkerPos "respawn_guerrila"];

            zsn_spawn_trg setTriggerActivation ["guer", "PRESENT", true];

            zsn_spawn_trg setTriggerStatements ["isServer && {Side _x == guer} count thislist >= zsn_wavesize_guer && zsn_wavecount_guer >= 1", "thisList call zsn_spawnwave;",""];
         }
         else
         {   
            zsn_wavecount_civ = _zsn_wavecount;

            publicVariable "zsn_wavecount_civ";

            zsn_wavesize_civ = _zsn_wavesize;

            publicVariable "zsn_wavesize_civ";
         
            zsn_spawn_trg = createTrigger ["EmptyDetector", getmarkerPos "respawn_civilian"];

            zsn_spawn_trg setTriggerActivation ["civ", "PRESENT", true];

            zsn_spawn_trg setTriggerStatements ["isServer && {Side _x == civ} count thislist >= zsn_wavesize_civ && zsn_wavecount_civ >= 1", "thisList call zsn_spawnwave;",""];
         };
      };
   };
};

zsn_spawnwave =
{
   _units = _this;

   _side = side (_units select 0);

   _grp = createGroup _side;

   _units join _grp;

   if (isClass(configFile >> "CfgPatches" >> "task_force_radio_items")) then
   {
      if (_side == east) then
      {
         radio = "tf_pnr1000a";
      }
      else
      {
         if (_side == west) then
         {
            radio = "tf_rf7800str";
         }
         else
         {   
            radio = "tf_anprc154";
         };
      };
   }
   else
   {
      radio = "itemradio";
   };
   leader _grp additem radio;

   leader _grp assignItem radio;

   remoteExec ["zsn_killspectator", _units];

   if (_side == east) then
   {
      {_x setPosASL (["zsn_respawn_east"] call BIS_fnc_randomPosTrigger)} forEach _units;

      zsn_wavecount_east = zsn_wavecount_east - 1;

      publicVariable "zsn_wavecount_east";
   }
   else
   {
      if (_side == west) then
      {
         {_x setPosASL (["zsn_respawn_west"] call BIS_fnc_randomPosTrigger)} forEach _units;

         zsn_wavecount_west = zsn_wavecount_west - 1;

         publicVariable "zsn_wavecount_west";
      }
      else
      {
         if (_side == guer) then
         {
            {_x setPosASL (["zsn_respawn_guerrila"] call BIS_fnc_randomPosTrigger)} forEach _units;

            zsn_wavecount_guer = zsn_wavecount_guer - 1;

            publicVariable "zsn_wavecount_guer";
         }
         else
         {
            {_x setPosASL (["zsn_respawn_civilian"] call BIS_fnc_randomPosTrigger)} forEach _units;

            zsn_wavecount_civ = zsn_wavecount_civ - 1;

            publicVariable "zsn_wavecount_civ";
         };
      };
   };
};

zsn_killspectator =
{
      ["Terminate"] call BIS_fnc_EGSpectator;
};


onplayerkilled.sqf (Textfil i din mission-mapp)
Kod Källa  

[player] join grpnull; //spelaren lämnar sin grupp, försvinner från STHud och liknande

["Initialize", [player, [side player], false, false, true, true, true, true, true, true]] call BIS_fnc_EGSpectator;  //spelaren placeras i spectator mode

Redigerat av ZluskeN den 2016-12-09 09:42