VgaGames4

VgaGames4 - tutorial

# Tutorial 8

To enhance our network-game we want to select our player-racket before the game-loop begins. For the selection we create a new canvas racket.cvas.

At first an overview about modifications in the file pingpong.c.

    pingpong.c: Overview about modifications

    /* global declarations */
    [CODEBOX]

    /* main function */
    int main(int argc, char **argv) {
      /* variable declaration */
      [CODEBOX]

      /* initializing */
      [...]      /* opening, collision functions and tag, loading audio files */
      [...]      /* set keys */
      [...]      /* show help */
      [...]      /* connect to network-server */
      [CODEBOX]  /* select player-racket and get selections of all clients */
      [CODEBOX]  /* create object-instances */
      [...]      /* start background music */

      /* game loop */
      [...]      /* receive input-events and check the local key-strokes */
      [...]      /* call f_run() of all object-instances */
      [...]      /* clear window, draw background, call f_draw() of all object-instances */
      [...]      /* flush and wait */

      /* end game */
      [...]      /* destroy and exit */
    }

The global declarations of the file pingpong.c. We add the function for selecting a player-racket and a boolean constant, whether selection of player-racket shall be exclusive, whether the player-rackets must have different color.
[To Position]

    pingpong.c: global declarations

    /* whether selection of player-racket shall be exclusive */
    static const VG_BOOL sel_excl = VG_TRUE;

    /* object-collision functions */
    extern void objcoll(void);

    /* connect to nw-server */
    extern int connect_to_nwserver(int);

    /* selections */
    extern VG_BOOL select_racket(struct VG_Image ***, VG_BOOL);

The main function of the file pingpong.c
[To Position]

    pingpong.c: variable declaration

    struct s_game sgame;  /* private structure of the game */
    int audc_gameover, audc_bgmusic;  /* audio descriptors */
    int clnr, clmax, cli;  /* local client-number, number of clients, control variable */
    VG_BOOL dowait;  /* whether vg4->misc->wait_time() shall be called */
    struct VG_Image **imgpp;

Now we select our player-racket and retrieve the selections of all clients. These selections are returned as loaded images for each client.
[To Position]

    pingpong.c: select player-racket and get selections of all clients

    /* select player-racket and get selections of all clients */
    if (!select_racket(&imgpp, sel_excl)) { VG_dest(); exit(1); }

When creating the two players we additionally pass the player-racket images, which were returned by select_racket().
[To Position]

    pingpong.c: create object-instances

    /* create object-instances */
    for (cli = 1; cli <= clmax; cli++) {  /* a player for each client */
      if (imgpp[cli - 1] != NULL) {
        if (objnew_PLAYER(&sgame, cli, imgpp[cli - 1]) == 0) { VG_dest(); exit(1); }
      }
    }
    free(imgpp);  /* free images */
    if (objnew_BALL(&sgame) == 0) { VG_dest(); exit(1); }
    if (objnew_BORDER(&sgame, "top") == 0) { VG_dest(); exit(1); }
    if (objnew_BORDER(&sgame, "bottom") == 0) { VG_dest(); exit(1); }

The object-file for the player-racket is changed as the image is passed.

    obj-player.c: export function for creating object-instances

    /* export-function to create a new object-instance of "PLAYER" */
    unsigned int
    objnew_PLAYER(void *vgame, int clnr, struct VG_Image *imgp)
    {
      const int coll_percent = 90;
      struct s_game *sgame = (struct s_game *)vgame;
      struct VG_Object *objp;
      struct sobj_player *objvars;

      if (sgame == NULL) { return 0; }
      if (clnr < 1) { return 0; }

      /* allocate private struct */
      objvars = calloc(1, sizeof(*objvars));
      if (objvars == NULL) { return 0; }

      /* set private struct */
      objvars->imgp = imgp;
      if (objvars->imgp == NULL) { return 0; }
      vg4->image->getsize(objvars->imgp, NULL, &objvars->rect.w, &objvars->rect.h);
      /* first connected player shall be the left player, the second the right player */
      if (clnr == 1) {
        objvars->rect.x = 0;
        objvars->rect.y = (sgame->winh - objvars->rect.h) / 2;
        if (clnr == vg4->nw->local_clnr()) { sgame->ply_local = PLY_LEFT; }
      } else {
        objvars->rect.x = sgame->winw - objvars->rect.w;
        objvars->rect.y = (sgame->winh - objvars->rect.h) / 2;
        if (clnr == vg4->nw->local_clnr()) { sgame->ply_local = PLY_RIGHT; }
      }
      objvars->clnr = clnr;

      /* create object-instance */
      objp = vg4->object->create(OBJID, 0, 0, 2, objvars);

      /* set functions */
      objp->f_free = f_free;
      objp->f_run = f_run;
      objp->f_draw = f_draw;
      /* f_data(), f_childexit() and f_signal() are not needed here */

      /* insert object-instance into collision-tag */
      vg4->collision->insert(sgame->coll_tag, objp->instanceid, &objvars->rect, coll_percent);

      return objp->instanceid;
    }

The new file rk_select.c to select the player-racket before the game-loop.

    rk_select.c: select player-racket and get selections of all clients

    /* show info */
    static void
    show_info(const char *text)
    {
      struct VG_Image *img;

      if (text == NULL || *text == '\0') { return; }

      img = vg4->font->totext(text, NULL, NULL, NULL, NULL);
      vg4->window->clear();
      vg4->window->copy(img, NULL, NULL);
      vg4->window->flush();
      vg4->image->destroy(img);
    }

    /* execute canvas to select player-racket
     * @param rkname     for returning name of racket
     * @param rksize     sizeof(rkname)
     * @param xdatalist  exchange-data list of already received clients
     * @return VG_TRUE = OK or VG_FALSE = got exit-request
     */
    static VG_BOOL
    exec_canvas_racket(char *rkname, size_t rksize, struct VG_NwXdataList *xdatalist)
    {
      const char *chain_name = "rk_chain";
      struct VG_Canvas *cvas;
      const char *selname;
      struct VG_NwXdataList *xdptr;

      if (rkname == NULL || rksize == 0) { return VG_FALSE; }

      *rkname = '\0';

      /* load canvas */
      cvas = vg4->canvas->load("files/canvas/racket.cvas", NULL);
      if (cvas == NULL) { return VG_FALSE; }

      /* disable chain-elements of already set player-rackets */
      for (xdptr = xdatalist; xdptr != NULL; xdptr = xdptr->next) {
        vg4->canvas->chain_disable(cvas, chain_name, xdptr->data, VG_TRUE);
      }

      for (;;) {
        /* execute canvas */
        if (!vg4->canvas->exec(cvas, NULL, &selname)) { return VG_FALSE; }
        if (selname == NULL) { break; }  /* cancelled via escape-key or cancel-button */

        /* act according to selection */

        if (*selname == '\0') {  /* no selection, but return-key pressed */
          /* no action, re-execute canvas */
          ;

        } else if (strcmp(selname, chain_name) == 0) {  /* item selected from chain */
          const char *chainkey = vg4->canvas->chain_get_activated(cvas, selname);
          if (chainkey != NULL) {
            vg4->misc->strcpy(rkname, rksize, chainkey);
            break;
          }
        }
      }

      vg4->canvas->destroy(cvas);

      if (*rkname == '\0') { return VG_FALSE; }
      return VG_TRUE;
    }

    /* select a player-racket and receive selections of each clients
     * @param imgpp  for returning an array of images,
     *               the index represents the client-number (0 = client1, 1 = client2, ...),
     *               the value is the image for its client or NULL if the client has disconnected,
     *               the array's size is the number of clients
     * @param exclusive  whether player-rackets must be unique
     * @return  VG_TRUE = OK or VG_FALSE = got exit request
     */
    VG_BOOL
    select_racket(struct VG_Image ***imgpp, VG_BOOL exclusive)
    {
      struct VG_NwXdataList *xdatalist, *xdptr;
      int idx, clnr, clmax;
      char rkname[32], filename[256];
      VG_BOOL redo;

      if (imgpp == NULL) { return VG_TRUE; }
      *imgpp = NULL;

      clnr = vg4->nw->local_clnr();
      clmax = vg4->nw->numberofclients(NULL);
      if (clnr == 0 || clmax == 0) { return VG_TRUE; }

      xdatalist = NULL;

      /* set a new tag */
      vg4->nw->xdata_retag();

    reselect:
      /* select player-racket */
      if (!exec_canvas_racket(rkname, sizeof(rkname), xdatalist)) { return VG_FALSE; }

      /* show info */
      show_info("Receiving player-rackets ...");

      /* send selected player-racket to network-server */
      if (!vg4->nw->xdata_send(rkname, strlen(rkname) + 1)) { return VG_FALSE; }

      /* receive player-rackets of each client from network-server */
      if (!vg4->nw->xdata_allclients_recv(&xdatalist, exclusive, rkname, strlen(rkname) + 1, &redo)) { return VG_FALSE; }
      if (redo) {  /* select another player-racket */
        show_info("Selected racket is no more available");
        sleep(3);
        vg4->window->clear();
        goto reselect;
      }

      /* create image array and load images */
      *imgpp = calloc(clmax, sizeof(**imgpp));
      if (*imgpp == NULL) { fprintf(stderr, "calloc: %s\n", strerror(errno)); return VG_FALSE; }
      for (idx = 0; idx < clmax; idx++) { (*imgpp)[idx] = NULL; }
      for (xdptr = xdatalist; xdptr != NULL; xdptr = xdptr->next) {
        snprintf(filename, sizeof(filename), "files/player-%s.bmp", xdptr->data);
        (*imgpp)[xdptr->clnr - 1] = vg4->image->load(filename);
      }

      /* free exchange-data list */
      vg4->nw->xdata_allclients_free(xdatalist);

      return VG_TRUE;
    }

At last the canvas-file to select the player-racket.

    files/canvas/racket.cvas: canvas-file for selecting player-racket

    # we don't use an image as background, but draw it ourself: a blue box with a red frame
    [MAIN]
    %{txt[
      # red frame
      boxwidth=200
      boxheight=150
      bgcolor=0xb10000
      orientation=center
      v-orientation=center
     ]: %{txt[
      # blue box
      boxwidth=180
      boxheight=130
      bgcolor=0x000088
      utag-title=10+160,8+12
      utag-rk_chain=20+140,30+80
     ]:%}
    %}

    [VAR-DEFAULT]
    var.title: Select player-racket

    [MOUSE]
    disable: 0

    [FONT]
    sys:low

    # the title of the canvas
    [CV-TEXT]
    name: title
    position-itag: title
    default-text: %{var: title%}
    text.orientation: center+left
    text.v-orientation: center
    text.fgcolor: 0xffff00
    text.nowrap: 1

    # the selection-chain for the player-rackets
    [CV-CHAIN]
    name: rk_chain
    position-itag: rk_chain
    element.red: img:../player-red.bmp
    element.yellow: img:../player-yellow.bmp
    element.green: img:../player-green.bmp
    element.blue: img:../player-blue.bmp

<<Previous | Download | Next>>