VgaGames4 - tutorial

Tutorial 8 

screenshot.gif

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() and f_childexit() 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>>