Schiffe versenken

Ich habe das Spiel oft gespielt, als ich noch jünger war; Sie plazieren einige Schiffe auf “ihrem Brett” und versuchen zu erraten, wo die Schiffe auf dem “gegnerischen Brett” sind. Starten Sie das Spiel, bevor Sie diesen Artikel lesen; Sie werden vieles von dem, was ich erklären möchte, dann viel leichter verstehen. Wenn Sie nicht wissen, wie Sie das Spiel starten können, lesen Sie die Anfängerecke.

Falls Sie bislang noch keinen Multiplayer Code benutzt haben, sollten Sie wissen, dass ein Teil des Codes nur auf dem Server und ein anderer Teil nur auf dem Client läuft. Sie können keinen Single Player Code in einem Multiplayer Spiel verwenden! Es ist immer besser, neuen Code für eine Multiplayer Anwendung zu schreiben.

Wir beginnen auf die einfache Art, mit einigen Variablendeklarationen:

var number_of_boats = 3;

var server_boats[26];
var client_boats[26];

var server_guess = 0; // stores a number between 1 and 25; the server tries to guess the position of a boat on the client
var client_guess = 0; // stores a number between 1 and 25; the client tries to guess the position of a boat on the server

var server_ready = 0;
var client_ready = 0;
var server_plays = 0;
var client_plays = 0;

var killed_by_server = 0;
var killed_by_client = 0;

Wir werden 3 Boote auf unser Brett stellen und benutzen 2 Arrays, um deren Positionen zu speichern. Die Boote werden auf einem 5 x 5 = 25 Felder großen Gitter plaziert, aber ich habe Arrays mit 26 Elementen benutzt, weil ich das erste Feld gern in server_boats[1] und client_boats[1] repräsentieren möchte; es vereinfacht die Dinge für Sie, wenn server_boats[0] und client_boats[0] nicht verwendet werden.

Server_guess und client_guess enthält eine Variable zwischen 1 und 25. Wenn der Client ein bestimmtes Feld angeklickt hat, wird client_guess auf diesen Wert gesetzt und an den Server übermittelt, wie im untenstehenden Bild:

Server_ready bzw. client_ready werden auf 1 gesetzt, wenn der Server (Client) bereit sind, d.h. wenn der jeweilige Spieler seine Boote plaziert und den “Start” Knopf gedrückt hat. Server_plays bzw. client_plays wird auf 1 gesetzt, wenn der jeweilige Spieler am Zug ist und die Maus bewegen kann. Schließlich werden mit killed_by_server und killed_by_client die Anzahl der Boote gespeichert, die der jeweilige Spieler zerstört hat.

Diesen Monat gibt es eine Menge Panel Definitionen:

panel main_pan
{
     bmap = main_pcx;
     pos_x = 0;
     pos_y = 0;
     layer = 10;

     button = 50, 390, start1_pcx, start1_pcx, start2_pcx, null, start_game, null;
     button = 50, 460, quit1_pcx, quit1_pcx, quit2_pcx, null, quit_game, null;
     button = 386, 192, boat_pcx, boat_pcx, boat_pcx, null, place_boat, null;

     flags = refresh, d3d, visible;
}

Das Haupt Panel ist das große Bild, das erscheint, wenn Sie das Spiel starten; es hat 3 Knöpfe, welche die Boote auf dem Feld plazieren, das Spiel starten und es beenden.

panel disabled_pan 
{
     bmap = disabled_pcx;
     pos_x = 595;
     pos_y = 379;
     layer = 30; // appears over the main panel
     alpha = 40;
     flags = transparent, refresh, d3d, visible;
}

Disabled_pan erscheint über dem “gegnerischen Brett” und blockiert den Zugriff, bis “Start” gedrückt wird.

panel enemyboard_pan 
{
     pos_x = 0;
     pos_y = 0;
     layer = 15; // appears over the main panel but it is covered by disabled_pan

     button = 599, 383, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 631, 383, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 663, 383, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 695, 383, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 727, 383, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;

     button = 599, 415, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 631, 415, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 663, 415, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 695, 415, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 727, 415, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;

     button = 599, 447, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 631, 447, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 663, 447, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 695, 447, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 727, 447, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;

     button = 599, 479, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 631, 479, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 663, 479, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 695, 479, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 727, 479, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;

     button = 599, 511, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 631, 511, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 663, 511, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 695, 511, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;
     button = 727, 511, bomb_pcx, invisible_pcx, invisible_pcx, fire_bomb, null, null;

     flags = refresh, d3d, visible;
}

Das gegnerische Brett war auf das Haupt Panel gemalt, also ist enemyboard_pan nur ein leeres Panel mit 25 unsichtbaren Knöpfen; wenn Sie auf eines der Felder des gegnerischen Brettes klicken, dann aktivieren Sie damit einen der Knöpfe.

panel boat1_pan
{
     bmap = boat_pcx;
     pos_x = 0;
     pos_y = 0;
     layer = 15; // appears over the main panel
     flags = refresh, d3d;
}

panel boat2_pan
{
     bmap = boat_pcx;
     pos_x = 0;
     pos_y = 0;
     layer = 15; // appears over the main panel
     flags = refresh, d3d;
}

panel boat3_pan
{
     bmap = boat_pcx;
     pos_x = 0;
     pos_y = 0;
     layer = 15; // appears over the main panel
     flags = refresh, d3d;
}

Diese Panels sind die Boote, die auf unserem Brett plaziert werden können.

panel boathit1_pan
{
     bmap = boathit_pcx;
     pos_x = 0;
     pos_y = 0;
     layer = 20; // appears over the boat1_pan panel
     flags = refresh, d3d;
}

panel boathit2_pan
{
     bmap = boathit_pcx;
     pos_x = 0;
     pos_y = 0;
     layer = 20; // appears over the boat2_pan panel
     flags = refresh, d3d;
}

panel boathit3_pan
{
     bmap = boathit_pcx;
     pos_x = 0;
     pos_y = 0;
     layer = 20; // appears over the boat3_pan panel
     flags = refresh, d3d;
}

Diese Panels sind die Boote mit Flammen, die auf unserem Brett erscheinen, wenn unser Gegner ihre Position erraten hat.

panel destroyed1_pan
{
     bmap = destroyed_pcx;
     pos_x = 0;
     pos_y = 0;
     layer = 17; // appears over the main panel
     flags = refresh, d3d;
}

panel destroyed2_pan
{
     bmap = destroyed_pcx;
     pos_x = 0;
     pos_y = 0;
     layer = 17; // appears over the main panel
     flags = refresh, d3d;
}

panel destroyed3_pan
{
     bmap = destroyed_pcx;
     pos_x = 0;
     pos_y = 0;
     layer = 17; // appears over the main panel
     flags = refresh, d3d;
}

Und diese Panels sind die Boote mit Flammen und einer Bombe, die auf dem gegnerischen Brett erscheinen, wenn Sie ein Boot Ihres Gegners zerstören.

panel youwon_pan
{
     bmap = youwon_pcx;
     pos_x = 250;
     pos_y = 200;
     layer = 30; // appears over all the panels
     flags = refresh, d3d;
}

panel youlost_pan
{
     bmap = youlost_pcx;
     pos_x = 250;
     pos_y = 200;
     layer = 30; // appears over all the panels
     flags = refresh, d3d;
}

Diese 2 Panels erscheinen zum Spielende und zeigen den Sieger und den Verlieren.

Die Main Funktion ist leicht:

function main()
{
     level_load (dummy_wmb);
     fps_max = 40; // lock the frame rate to 40 fps
     init_game();
}

Sehen wir uns die init_game() Funktion an:

function init_game()
{
     mouse_mode = 2;
     mouse_map = cursor_pcx;
     while (1)
     {
          mouse_pos.x = pointer.x;
          mouse_pos.y = pointer.y;

          if (server_ready + client_ready == 2)
          {
               if (killed_by_server != 3 && killed_by_client != 3)
               {
                    disabled_pan.visible = off;
               }
               ifdef server; // server
                    if (server_plays == 1)
                    {
                         mouse_mode = 2;
                    }
                    else
                    {
                         mouse_mode = 0;
                    }
              ifelse; // client
                    if (client_plays == 1)
                    {
                         mouse_mode = 2;
                    }
                    else
                    {
                         mouse_mode = 0;
                    }
             endif;
         }
         wait (1);
     }
}

Dies ist die erste Funktion mit Multiplayer spezifischem Code, also schauen Sie genau hin. Zum Start des Spiels wird der Mauszeiger aktiviert mit der Bitmap cursor_pcx; dieser wird wegen der Schleife seine Koordinaten ändern. Falls server_ready und client_ready beide auf 1 stehen (alle Boote wurden plaziert und beide Spieler haben den “Start” Knopf gedrückt) und das Spiel nicht vorüber ist (beide Seiten haben noch nicht alle Boote des Gegners zerstört), verstecken wir das disabled_pan Panel, welches das gegnerische Brett verdeckte, damit wir darauf klicken können.

Wenn unser PC der Server ist (also falls er mit der Option –sv –cl gestartet wurde) und der Server an der Reihe ist (erinnern Sie sich an die Variable server_plays?) zeigen wir die Maus an. Falls server_plays = 0 (der Client ist an der Reihe), verbergen wir den Mauszeiger. Falls unser PC der Client ist, läuft es genau andersrum, wir zeigen den Mauszeiger an, wenn der Client an der Reihe ist, sonst verstecken wir ihn, falls client_plays = 0.

Warten Sie einen Moment und schauen Sie sich den Code an, der mit ifdef server beginnt und endif endet. Auf diese Weise kann man verschiedenen Code auf Server und Client laufen lassen und wenn Sie Code für ein Multiplayer Spiel schreiben wollen, sollten Sie es so machen.

Nun weiter; wir sehen uns die Funktion an, die abläuft, wenn der Spieler (Client oder Server) den “Start” Knopf drückt: 

function start_game()
{
     if (number_of_boats != 0) {return;}
     ifdef server;
            server_ready = 1;
            send_var(server_ready);
            if (counter == 0)
            {
                counter = 1; // make sure that it runs only once
                if (sys_seconds % 2 == 0) // generate a random value (0 or 1)
                {
                     server_plays = 1; // shows the pointer on the server; the server has the first move
                     client_plays = 0; // the client hides its mouse pointer
                }
                else
                {
                     client_plays = 1;
                     server_plays = 0; // the server hides its mouse pointer
                 }
                send_var(client_plays); // shows or hides the mouse pointer on the client
         }
    ifelse;
           client_ready = 1; // the client is ready to play
           send_var(client_ready); // and sends this var to the server
    endif;
}

Falls noch nicht alle Boote plaziert wurden, tut die Funktion nichts. Sind wir der Server, zeigen wir an, dass wir bereit sind zu spielen und senden server_ready über das Netzwerk zum Client. Server_ready wird auf dem Client nicht von selbst aktualisiert, daher müssen wir den Wert dieser Variablen mit send_var übermitteln. Ich dachte, es wäre nett, wenn beide Spieler, Client und Server das Spiel beginnen dürfen, also erzeugen wir einen Zufallswert auf dem Server und je nachdem wie diese ausfällt, bekommt der Server oder der Client den ersten Zug. Diese Funktion läuft nur auf dem Server ab, wir brauchen nicht 2 verschiedene Zufallszahlen. Der Spieler könnte (aus Versehen oder auch nicht) im Laufe des Spiels auf den Start Knopf drücken, also darf diese Zahl nur einmal generiert werden. Ich benutze sys_seconds und teste, ob die Zahl gerade oder ungerade ist. Ist sie ungerade, beginnt der Server, ansonsten der Client.

All dies geschieht auf dem Server und damit der Client weiß, ob er den ersten Zug hat oder nicht, senden wir client_plays (diese könnte 1 oder 0 sein) an den Client, um den Mauszeiger dort sichtbar zu machen, oder eben auch nicht.

Wenn wir der Client sind (der Teil nach ifelse;) und haben den Startknopf gedrückt, setzen wir client_ready auf 1 und senden diese Variable an den Server.

Ergibt alles Sinn? Nun zu meiner Lieblingsfunktion – sie läuft, wenn Sie den “Quit” Knopf drücken:
 
function quit_game()
{
     exit; // shut down the engine
}
 
Es tut mir leid, die nächste Funktion ist einfach RIESIG, obwohl sie eigentlich ganz einfach ist. Es ist die Funktion, die läuft, wenn Sie auf das Boot Icon klicken; sie plaziert die Boote auf Ihrem Brett. Da ich nett bin, habe ich mich entschieden, Ihnen nur den Teil zu zeigen, der mit dem ersten Feld (1) und mit dem letzten (25) zu tun hat.

function place_boat()
{
     if (number_of_boats == 0) {return;} // all the boats are placed

Wenn alle Boote plaziert sind, verlassen wir die Funktion.

     mouse_map = boat_pcx;

Wenn wir auf das Boot Icon auf dem Haupt Panel klicken, ändert sich der Mauszeiger zu boat_pcx. Dies gibt uns die Illusion, dass wir das Boot über den Bildschirm “tragen”, aber in Wahrheit benutzen wir einfach einen anderen Mauszeiger.

     while (mouse_left != 1)
     {
          if (mouse_right == 1)
          {
               mouse_map = cursor_pcx;
               return;
          }
          wait (1);
     }

Solange die linke Maustaste noch nicht gedrückt wurde, können wir mit Hilfe der rechten Maustaste abbrechen. Wird letztere gedrückt, schalten wir zurück auf den normalen Mauszeiger und verlassen die Funktion.

Lassen Sie uns das Brett des Spielers ansehen:
 

     if (mouse_pos.x > 40 && mouse_pos.x < 230 && mouse_pos.y > 20 || mouse_pos.y < 200)
     {

Wenn das Boot irgendwo auf dem Brett plaziert wird (Ich habe die Koordinaten aus Paint Shop Pro, aber Sie können irgendein Malprogramm verwenden)

      if (mouse_pos.x > 40 && mouse_pos.x < 65) // placed on the "A" column
      {

Falls die Maus innerhalb dieser Koordinaten ist, wird das Boot in der A Reihe plaziert, es könnte A1 bis A5 sein.

           if (mouse_pos.y > 20 && mouse_pos.y < 55) // inside A1
           {

Ist die Maus innerhalb dieser Koordinaten, wird das Boot auf A1 plaziert.

                if (number_of_boats == 1)
                {
                     boat1_pan.pos_x = 51;
                     boat1_pan.pos_y = 41;
                     boat1_pan.visible = on;
                     number_of_boats -= 1;
                }

Wenn wir unser erstes Boot plazieren, benutzen wir das Panel für das erste Boot:

                if (number_of_boats == 2)
                {
                     boat2_pan.pos_x = 51;
                     boat2_pan.pos_y = 41;
                     boat2_pan.visible = on;
                     number_of_boats -= 1;
                }

Wenn wir unser zweites Boot plazieren, benutzen wir das Panel für das zweite Boot:

                if (number_of_boats == 3)
                {
                     boat3_pan.pos_x = 51;
                     boat3_pan.pos_y = 41;
                     boat3_pan.visible = on;
                     number_of_boats -= 1;
                }

Und schließlich, wenn wir unser drittes Boot plazieren, benutzen wir das Panel für das dritte Boot:

                ifdef server;
                     server_boats[1] = 1;
                ifelse;
                     client_boats[1] = 1;
                endif;
           }

Sind wir der Server, wird server_boats[1] auf 1 gesetzt, auf diese Weise sagen wir der Engine, dass auf dem ersten Feld des Servers ein Boot steht. Sind wir der Client, wird natürlich client_boats[1] auf 1 gesetzt. Beachten Sie bitte, dass die Positionen der Boote für den Server nur auf dem Server und die für den Client nur auf dem Client gespeichert werden.

          .............................................................

Die Punkte verbergen den Rest des Codes für die anderen Felder, aber bis auf die Koordinaten für die Maus und die Panels ist alles genau gleich. Hier ist der Rest:

           if (mouse_pos.y >= 155 && mouse_pos.y < 185) // inside E5
           {
                if (number_of_boats == 1)
                {
                     boat1_pan.pos_x = 180;
                     boat1_pan.pos_y = 169;
                     boat1_pan.visible = on;
                     number_of_boats -= 1;
                }
                if (number_of_boats == 2)
                {
                     boat2_pan.pos_x = 180;
                     boat2_pan.pos_y = 169;
                     boat2_pan.visible = on;
                     number_of_boats -= 1;
                }
                if (number_of_boats == 3)
                {
                     boat3_pan.pos_x = 180;
                     boat3_pan.pos_y = 169;
                     boat3_pan.visible = on;
                     number_of_boats -= 1;
                }
                ifdef server;
                     server_boats[25] = 1;
                ifelse;
                     client_boats[25] = 1;
                endif;

Falls wir der Server sind und ein Boot in E5 plaziert haben (das 25. Feld), setzen wir server_boats[25] auf 1. Sind wir der Client, geschieht dasselbe mit client_boats[25].

            }
        }
    }
   mouse_map = cursor_pcx;

Das Boot wurde plaziert, also stellen wir den ursprünglichen Mauszeiger (den Pfeil) wieder her.

}

Die Funktion oben stellt sicher, dass das Boot immer richtig plaziert wird, wenn irgendwo in ein Feld des eigenen Feldes geklickt wird.

Wenn der Spieler allerdings auf das gegnerische Brett klickt, um ein Schiff zu zerstören, läuft die Funktion fire_bomb():

function fire_bomb(square_number)
{
     snd_play(bomb_snd, 40, 0);
     ifdef server;
        server_guess = square_number;
        send_var(server_guess);
        server_plays = 0; // the server fired, now hide the pointer on the server
        client_plays = 1; // and make it visible on the client
    ifelse;
        client_guess = square_number;
        send_var(client_guess);
        client_plays = 0; // the client fired, now hide the pointer on the client
        server_plays = 1; // and make it visible on the server
    endif;
    send_var(server_plays);
    send_var(client_plays);
}

Wird auf eines der Felder des gegnerischen Brettes geklickt, wird automatisch ein Geräusch abgespielt. Sind wir der Server, wird square_number (und server_guess) auf die Zahl gesetzt, die als Parameter an die Funktion gereicht wird. Dies ist ein wirklich wichtiges Feature der Engine: alle Buttons eines Panels haben eine ID. Klicken wir auf den ersten Knopf vom gegnerischen Brett, drücken wir damit einen Knopf, der square_number und server_guess auf 1 setzt. Drücken wir auf Feld 14, werden beide Werte auf 14 gesetzt. Mit Hilfe dieses Features konnte ich eine einzige Funktion für alle 25 Knöpfe des Panels verwenden.

Erinnern Sie sich, dass wir auf dem Server sind? Dieser sendet seinen Tip (serber_guess) an den Client, beendet die Runde und teilt dem Client mit, dass der dran ist. Sind wir der Client, so senden wir client_guess an den Server, beenden die Runde und sagen dem Server, dass er wieder dran ist. Schließlich werden server_plays und client_plays über das Netzwerk übermittelt, um die Mauszeiger sichtbar bzw. unsichtbar zu machen.

Ich denke mal, dass Sie jetzt ziemlich müde sind, aber es sind noch 2 Funktionen übrig und sie ähneln sich ziemlich, also holen Sie sich eine beliebige Erfrischung und lesen Sie weiter. Der Server und auch der Client haben eine eigene Funktion, die jedes Mal läuft, wenn etwas über das Netzwerk versandt wird: wenn ein neuer Spieler das Spiel betritt, eine Variable versandt wurde, etc.

on_server server_event;
on_client client_event;

Dieser Teil des Codes sagt dem Server, die server_event Funktion zu starten (bzw. dem Client die client_event Funktion), wann immer etwas über das Netzwerk versandt wird.

Sehen wir uns diese Event Funktionen an:

function server_event()
{
     exclusive_global();

Nur eine Kopie der Funktion darf jeweils laufen.

     if (event_type == event_var)
     {

Falls der Client eine Variable an den Server gesandt hat

          if (client_ready == 1 && server_ready == 0)
          {
               snd_play(ready_snd, 50, 0);
          }

Falls der Client fertig ist und der Server nicht, spielen wir auf dem Server einen Sound, um anzudeuten, dass der Client fertig ist und auf den Server wartet.

          if (server_ready + client_ready != 2) {return;}

Der Server und der Client müssen fertig sein, damit das Spiel beginnt; ist dies nicht der Fall, verlassen wir die Funktion.

          if (server_boats[client_guess] == 1)

Angenommen, client_guess ist gleich 10 (der Client hat auf das 10. Feld geklickt). Falls der Server dort ein Boot versteckt, ist server_boats[10] auf 1 gesetzt, wegen der RIESIGEN Funktion, über die wir sprachen. Deshalb prüft die Zeile oben, ob der Client die Position eines Bootes richtig geraten hat.

          {
               snd_play(destroyed_snd, 100, 0); // a boat was hit!
               killed_by_client += 1;

Wir spielen einen Sound und erhöhen killed_by_client.

               if (killed_by_client == 1)

Falls dies das erste vom Client zerstörte Schiff ist

               {
                    boathit1_pan.pos_x = 52 + ((client_guess - 1) % 5) * 32;
                    boathit1_pan.pos_y = 41 + 32 * (client_guess > 5 && client_guess <= 10) + 64 * (client_guess > 10 && client_guess <= 15)
                     + 96 * (client_guess > 15 && client_guess <= 20) + 128 * (client_guess > 20);
                    boathit1_pan.visible = on; // appears on the server over its boat
               }

Berechnen wir die Position von boathit1_pan (Boot mit Flammen), welche auf dem Server angezeigt wird und zwar über dem getroffenen Schiff auf dem eigenen Brett. Schauen wir uns an, was mit den Panel Koordinaten geschieht:

                    boathit1_pan.pos_x = 52 + ((client_guess - 1) % 5) * 32;
                    boathit1_pan.pos_y = 41 + 32 * (client_guess > 5 && client_guess <= 10) + 64 * (client_guess > 10 && client_guess <= 15)
                     + 96 * (client_guess > 15 && client_guess <= 20) + 128 * (client_guess > 20);

Ich weiß, dass das Gitter für die Bretter Höhe und Breite von je 32 Pixeln hat; ist client_guess gleich 1, wird boathit1_pan auf pos_x 52 gesetzt; ist client_guess gleich 2, wird es auf 52 + 32 gesetzt und so weiter. Dasselbe geschieht mit der y Achse, aber dieses Mal ändert sich die z Koordinate je nach dem Intervall in den Klammern. Bitte beachten Sie, dass client_guess in diesem Fall die Feldnummer eines wirklich getroffenen Bootes enthält.

               if (killed_by_client == 2)
               {
                    boathit2_pan.pos_x = 52 + ((client_guess - 1) % 5) * 32;
                    boathit2_pan.pos_y = 41 + 32 * (client_guess > 5 && client_guess <= 10) + 64 * (client_guess > 10 && client_guess <= 15)
                     + 96 * (client_guess > 15 && client_guess <= 20) + 128 * (client_guess > 20);
                    boathit2_pan.visible = on; // appears on the server over its boat
               }

Wenn Boot 2 zerstört wurde, zeigen wir boathit2_pan.

               if (killed_by_client == 3)
               {
                    boathit3_pan.pos_x = 52 + ((client_guess - 1) % 5) * 32;
                    boathit3_pan.pos_y = 41 + 32 * (client_guess > 5 && client_guess <= 10) + 64 * (client_guess > 10 && client_guess <= 15)
                     + 96 * (client_guess > 15 && client_guess <= 20) + 128 * (client_guess > 20);
                    boathit3_pan.visible = on; // appears on the server over its boat
               }

Und ebenso mit dem dritten Boot.

               server_boats[client_guess] = 0;

Die Zeile oben setzt den Eintrag von server_boats auf 0 zurück. Auf diese Weise kann ein Boot nicht zweimal getroffen werden, ohne diese Zeile könnte ein Spieler dreimal auf das gleiche Boot klicken und gewinnen.

               send_var(killed_by_client); // the client has destroyed one of the boats -> make it show destroyed_pcx on its enemy board
          }

Der Client hat eines der Boote des Servers zerstört, also sendet dieser ein killed_by_client hinüber, damit der Client das zerstörte Boot anzeigen kann (destroyed_pcx auf dem gegnerischen Brett)

          // killed_by_server is sent from the client
          if (killed_by_server == 1 && destroyed1_pan.visible == off) // the first boat on the client was destroyed, runs only once
          {
               destroyed1_pan.pos_x = 599 + ((server_guess - 1) % 5) * 32;
               destroyed1_pan.pos_y = 383 + 32 * (server_guess > 5 && server_guess <= 10) + 64 * (server_guess > 10 && server_guess <= 15)
                 + 96 * (server_guess > 15 && server_guess <= 20) + 128 * (server_guess > 20);
               destroyed1_pan.visible = on;
          }
          if (killed_by_server == 2 && destroyed2_pan.visible == off) // the second boat on the client was destroyed, runs only once
          {
               destroyed2_pan.pos_x = 599 + ((server_guess - 1) % 5) * 32;
               destroyed2_pan.pos_y = 383 + 32 * (server_guess > 5 && server_guess <= 10) + 64 * (server_guess > 10 && server_guess <= 15)
                 + 96 * (server_guess > 15 && server_guess <= 20) + 128 * (server_guess > 20);
               destroyed2_pan.visible = on;
          }
          if (killed_by_server == 3 && destroyed3_pan.visible == off) // the third boat on the client was destroyed, runs only once
          {
               destroyed3_pan.pos_x = 599 + ((server_guess - 1) % 5) * 32;
               destroyed3_pan.pos_y = 383 + 32 * (server_guess > 5 && server_guess <= 10) + 64 * (server_guess > 10 && server_guess <= 15)
                 + 96 * (server_guess > 15 && server_guess <= 20) + 128 * (server_guess > 20);
               destroyed3_pan.visible = on;
          }

Killed_by_server wird jedes Mal zum Server gesandt, wenn dieser ein Boot des Clients zerstört hat. Wenn killed_by_server auf 1 steht und destroyed1_pan nicht sichtbar ist, zeigen wir es auf dem gegnerischen Brett des Servers an der Position an, die dem Klick entspricht. Ist killed_by_server gleich 2, zeigen wir destroyed2_pan und so weiter.

          if (killed_by_server == 3) // game over, server wins
          {
               youwon_pan.visible = on;
               disabled_pan.visible = on; // can't throw bombs from now on
               server_plays = 1; // show the mouse pointer
               client_plays = 1; // on the server and on the client because the game is over
          }
          if (killed_by_client == 3) // game over, client wins
          {
               youlost_pan.visible = on;
               disabled_pan.visible = on; // can't throw bombs from now on
               server_plays = 1; // show the mouse pointer
               client_plays = 1; // on the server and on the client because the game is over
          }
     }
}

Falls killed_by_server gleich 3 ist, gewinnt der Server und das youwon_pan Panel wird angezeigt, das disabled_pan wird sichtbar gemacht (damit nicht mehr auf das Brett des Gegners geklickt werden kann) und der Mauszeiger wird auf Server und Client angezeigt, damit beide das Spiel mit “Quit” verlassen können. Gewinnt der Client, zeigt der Server das youlost_pan Panel, zeigt wiederum das disabled_pan Panel an und auch die Mauszeiger.

Das Event für den Client ist ähnlich, aber sehen wir es uns dennoch an:

function client_event()
{
     exclusive_global();
     if (event_type == event_var)

Falls der Server eine Variable an den Client sendet

     {
          if (server_ready == 1 && client_ready == 0)
          {
               snd_play(ready_snd, 50, 0);
          }

Falls der Server fertig ist, aber der Client nicht, wird ein Sound auf dem Client gespielt, um anzuzeigen, dass der Server wartet.

          if (server_ready + client_ready != 2) {return;}

Client und Server müssen fertig sein, damit das Spiel starten kann:

          if (client_boats[server_guess] == 1)

Falls der Server die Position eines Bootes erraten hat:

          {
               snd_play(destroyed_snd, 100, 0); // a boat was hit!
               killed_by_server += 1;
               if (killed_by_server == 1)
               {
                    boathit1_pan.pos_x = 52 + ((server_guess - 1) % 5) * 32;
                    boathit1_pan.pos_y = 41 + 32 * (server_guess > 5 && server_guess <= 10) + 64 * (server_guess > 10 && server_guess <= 15)
                     + 96 * (server_guess > 15 && server_guess <= 20) + 128 * (server_guess > 20);
                    boathit1_pan.visible = on;
               }
               if (killed_by_server == 2)
               {
                    boathit2_pan.pos_x = 52 + ((server_guess - 1) % 5) * 32;
                    boathit2_pan.pos_y = 41 + 32 * (server_guess > 5 && server_guess <= 10) + 64 * (server_guess > 10 && server_guess <= 15)
                     + 96 * (server_guess > 15 && server_guess <= 20) + 128 * (server_guess > 20);
                    boathit2_pan.visible = on;
               }
               if (killed_by_server == 3)
               {
                    boathit3_pan.pos_x = 52 + ((server_guess - 1) % 5) * 32;
                    boathit3_pan.pos_y = 41 + 32 * (server_guess > 5 && server_guess <= 10) + 64 * (server_guess > 10 && server_guess <= 15)
                     + 96 * (server_guess > 15 && server_guess <= 20) + 128 * (server_guess > 20);
                    boathit3_pan.visible = on;
               }

Zeige das entsprechende boathit_pan Panel an, je nachdem, wie viele Boote vom Server zerstört wurden.

               client_boats[server_guess] = 0;

Ein Boot kann nicht zweimal zerstört werden.

               send_var(killed_by_server);

Der Server hat ein Boot zerstört, also sollte er destroyed_pcx auf dem gegnerischen Brett anzeigen.

          }
          if (killed_by_client == 1 && destroyed1_pan.visible == off) // the first boat on the server was destroyed, runs only once
          {
               destroyed1_pan.pos_x = 599 + ((client_guess - 1) % 5) * 32;
               destroyed1_pan.pos_y = 383 + 32 * (client_guess > 5 && client_guess <= 10) + 64 * (client_guess > 10 && client_guess <= 15)
                + 96 * (client_guess > 15 && client_guess <= 20) + 128 * (client_guess > 20);
               destroyed1_pan.visible = on;
          }
          if (killed_by_client == 2 && destroyed2_pan.visible == off) // the second boat on the server was destroyed, runs only once
          {
               destroyed2_pan.pos_x = 599 + ((client_guess - 1) % 5) * 32;
               destroyed2_pan.pos_y = 383 + 32 * (client_guess > 5 && client_guess <= 10) + 64 * (client_guess > 10 && client_guess <= 15)
                + 96 * (client_guess > 15 && client_guess <= 20) + 128 * (client_guess > 20);
               destroyed2_pan.visible = on;
          }
          if (killed_by_client == 3 && destroyed3_pan.visible == off) // the third boat on the server was destroyed, runs only once
          {
               destroyed3_pan.pos_x = 599 + ((client_guess - 1) % 5) * 32;
               destroyed3_pan.pos_y = 383 + 32 * (client_guess > 5 && client_guess <= 10) + 64 * (client_guess > 10 && client_guess <= 15)
                + 96 * (client_guess > 15 && client_guess <= 20) + 128 * (client_guess > 20);
               destroyed3_pan.visible = on;
          }

Killed_by_client wurde vom Server übermittelt; das entsprechende destroyed_pan Panel wird angezeigt, je nachdem, wie viele Boote zerstört wurden.

          if (killed_by_server == 3) // game over, server wins
          {
               youlost_pan.visible = on;
               disabled_pan.visible = on; // can't throw bombs from now on
               server_plays = 1; // show the mouse pointer
               client_plays = 1; // on the server and on the client because the game is over
          }
          if (killed_by_client == 3) // game over, client wins
          {
               youwon_pan.visible = on;
               disabled_pan.visible = on; // can't throw bombs from now on
               server_plays = 1; // show the mouse pointer
               client_plays = 1; // on the server and on the client because the game is over
          }
     }
}

Schließlich die Panels für Gewinner und Verlierer, disabled_pan anzeigen, um zu verhindern, dass auf das Brett geklickt wird, nachdem das Spiel vorüber ist und auf beiden Servern die Mauszeiger anzeigen.

Vielleicht haben Sie bemerkt, dss ich nur event_var benutzt habe; ich hätte (z.B.) event_leave einbauen können, um ein Panel anzuzeigen, wenn Server oder Client die Verbindung verlieren und so weiter. Der Code ist nicht schwer, wenn Sie Probleme haben, versuchen Sie, das Spiel laufen zu lassen, sehen Sie sich kleinere Effekte an, danach schauen Sie im Code nach, wie ich es gemacht habe. Hier ein Beispiel:

F: Wie kann ich einen Sound auf dem Client spielen, wenn auf dem Server “Start” angeklickt wird?
A: Wenn der Server seine Boote plaziert hat und der Spieler auf “Start” geklickt hat, wird server_ready auf 1 gesetzt und über das Netzwerk geschickt. Client_event läuft, sobald etwas gesendet wird; falls server_ready = 1 (der Server ist bereit), aber client_ready = 0 (der Client nicht), wird der Sound gespielt.