/* $Id: single.c,v 1.42 2003/07/27 20:16:58 stpohle Exp $ */ /* single player */ #include "basic.h" #include "bomberclone.h" void single_game_new () { int p, i; bman.players_nr_s = 1; bman.players_nr = 1; // set players variables for (p = 0; p < MAX_PLAYERS; p++) { bman.players[p].pos.x = -1; bman.players[p].pos.y = -1; if (PS_IS_used (bman.players[p].state)) { bman.players_nr_s++; bman.players_nr++; bman.players[p].state = PSF_used + PSF_alife + PSF_playing; } else bman.players[p].state = 0; // reset bombs bman.players[p].bombs_n = START_BOMBS; bman.players[p].range = START_RANGE; bman.players[p].speed = START_SPEED; bman.players[p].special.type = SP_nothing; bman.updatestatusbar = 1; for (i = 0; i < MAX_BOMBS; i++) { bman.players[p].bombs[i].state = BS_off; bman.players[p].bombs[i].ex_nr = -1; bman.players[p].bombs[i].moves = 0; bman.players[p].bombs[i].moveto.x = 0; bman.players[p].bombs[i].moveto.y = 0; } for (i = 0; i < PI_max; i++) bman.players[p].ill[i].to = 0; bman.players[p].frame = 0; bman.players[p].frameto = 0; bman.players[p].d = 0; } bman.players[bman.p_nr].state = PSFM_alife; player_set_gfx (&bman.players[bman.p_nr], bman.players[bman.p_nr].gfx_nr); bman.last_ex_nr = 1; init_map_tileset (); bman.gametype = GT_single; bman.state = GS_running; }; inline int ai_checkfield (int x, int y) { return ((map.field[x][y].type == FT_nothing || map.field[x][y].type == FT_fire || map.field[x][y].type == FT_shoe || map.field[x][y].type == FT_bomb || map.field[x][y].type == FT_mixed) && map.bfield[x][y] == 0); } /* give the run away direction */ int ai_easyrunaway (_point p) { int i, done = 0, dir = 0; _point pos[4], m[4]; for (i = 0; i < 4; i++) { pos[i] = p; switch (i) { case (left): m[i].x = -1; m[i].y = 0; break; case (right): m[i].x = 1; m[i].y = 0; break; case (up): m[i].x = 0; m[i].y = -1; break; default: m[i].x = 0; m[i].y = 1; break; } pos[i].x += m[i].x; pos[i].y += m[i].y; } /* test the possible ways */ while (!done) { done = 1; for (i = 0; i < 4; i++) { /* check if we are still in the game field */ if (pos[i].x <= 0 || pos[i].y <= 0 || pos[i].x >= map.size.x - 1 || pos[i].y >= map.size.y - 1) pos[i].x = pos[i].y = -1; if (pos[i].x != -1 && pos[i].y != -1) { /* check if this place is free to go to */ if (ai_checkfield (pos[i].x, pos[i].y)) { done = 0; /* check the field left and right beside */ if (i == left || i == right) { if (ai_findnearbombs (pos[i]) == 0 && (ai_checkfield (pos[i].x, pos[i].y - 1) || ai_checkfield (pos[i].x, pos[i].y + 1))) dir |= (1 << i); } else { if (ai_findnearbombs (pos[i]) == 0 && (ai_checkfield (pos[i].x - 1, pos[i].y) || ai_checkfield (pos[i].x + 1, pos[i].y))) dir |= (1 << i); } pos[i].x += m[i].x; pos[i].y += m[i].y; } } } } return dir; }; /* give the run away direction the return value: */ _airunaway ai_runawayfrom (_point p, int nearbomb, signed char norecursive) { int i, done = 0, nbomb, tdir, _i, bdirpoints = 10, j, c; _airunaway res; _point pos[4], m[4], tpos; res.dir = 0; res.bestdir = -1; for (i = 0; i < 4; i++) { pos[i] = p; switch (i) { case (left): m[i].x = -1; m[i].y = 0; break; case (right): m[i].x = 1; m[i].y = 0; break; case (up): m[i].x = 0; m[i].y = -1; break; default: m[i].x = 0; m[i].y = 1; break; } pos[i].x += m[i].x; pos[i].y += m[i].y; } /* test if we just have to move to the side */ if (!norecursive) for (i = 0; i < 4; i++) if (ai_checkfield (pos[i].x, pos[i].y) && ai_findnearbombs (pos[i]) == 0) { bdirpoints = 0; res.bestdir = i; res.dir |= (1 << i); } /* test the possible ways */ while (!done) { done = 1; for (i = 0; i < 4; i++) { /* check if we are still in the game field */ if (pos[i].x <= 0 || pos[i].y <= 0 || pos[i].x >= map.size.x - 1 || pos[i].y >= map.size.y - 1) pos[i].x = pos[i].y = -1; if (pos[i].x != -1 && pos[i].y != -1) { /* check if this place is free to go to */ if (ai_checkfield (pos[i].x, pos[i].y)) { done = 0; /* check the field left and right beside */ for (j = 0; j < 4; j++) if (((i == left || i == right) && (j == up || j == down)) || ((j == left || j == right) && (i == up || i == down))) { c = 10; tpos.x = pos[i].x + m[j].x; tpos.y = pos[i].y + m[j].y; if (ai_checkfield (tpos.x, tpos.y)) { nbomb = ai_findnearbombs (tpos); c = s_countbits (nbomb, 5); if (!norecursive) { tdir = ai_runawayfrom (tpos, nbomb, 1).dir; _i = ai_invertdir (i); if (tdir != (1 << _i)) { // usefull direction res.dir |= (1 << i); // add this one } else { c = -1; } } else res.dir |= (1 << i); /* check for the best direction */ if (c != -1 && !norecursive) { if (c < bdirpoints) { bdirpoints = c; res.bestdir = i; } else if (bdirpoints != 0 && c == bdirpoints && c < 5 && s_random (2) == 0) res.bestdir = i; // random if the points are equal } } } pos[i].x += m[i].x; pos[i].y += m[i].y; } } } } return res; }; /* count the points for dropping a bomb here this will even count if there is any powerup*/ int ai_bombpoints (_point pos, int range) { int points = 0, d, r; _point p, m; if (map.field[pos.x][pos.y].type != FT_block && map.field[pos.x][pos.y].type != FT_stone && ai_checkfield (pos.x, pos.y)) { for (d = 0; d < 4; d++) { switch (d) { case (left): m.x = -1; m.y = 0; break; case (right): m.x = 1; m.y = 0; break; case (up): m.x = 0; m.y = -1; break; default: m.x = 0; m.y = 1; break; } p = pos; for (r = 0; (r < range && map.field[p.x][p.y].type == FT_nothing); r++) { p.x += m.x; p.y += m.y; } if (map.field[p.x][p.y].type != FT_nothing && (map.field[p.x][p.y].type != FT_block || map.field[p.x][p.y].type == FT_shoe || map.field[p.x][p.y].type == FT_bomb || map.field[p.x][p.y].type == FT_fire)) points++; if (map.field[p.x][p.y].type == FT_shoe || map.field[p.x][p.y].type == FT_bomb || map.field[p.x][p.y].type == FT_fire) points += 2; } } if (ai_easyrunaway (pos) == 0 || ai_findnearbombs (pos) != 0) points = 0; return points; }; /* find the best position to go for dropping a bomb */ int ai_findbestbombdir (_point pos, int dir, int range) { int points[5] = { 0, 0, 0, 0, 0 }; int d, done = 0, j, maxpoints = 0, bestd; _point m[4], p[4]; points[4] = ai_bombpoints (pos, range); /* fill in the positions */ for (d = 0; d < 4; d++) { p[d] = pos; switch (d) { case (left): m[d].x = -1; m[d].y = 0; break; case (right): m[d].x = 1; m[d].y = 0; break; case (up): m[d].x = 0; m[d].y = -1; break; default: m[d].x = 0; m[d].y = 1; break; } } while (!done) { done = 1; for (d = 0; d < 5; d++) { if (d < 4) { p[d].x += m[d].x; p[d].y += m[d].y; if (p[d].x > 0 && p[d].y > 0 && p[d].x < map.size.x - 1 && p[d].y < map.size.y - 1) { if (ai_checkfield (p[d].x, p[d].y)) { /* we are opn a empty field go on with the test */ done = 0; j = ai_bombpoints (p[d], range); if (points[d] < j) points[d] = j; } else /* no empty field */ p[d].x = p[d].y = -1; } } if (maxpoints < points[d]) maxpoints = points[d]; } } bestd = 0; if (maxpoints > 2) maxpoints--; for (d = 0; d < 5; d++) if (points[d] >= maxpoints) bestd |= (1 << d); /* prevent from turning around */ if (dir != -1 && (bestd & (0xFF - (1 << ai_invertdir (dir))))) bestd &= (0xFF - (1 << ai_invertdir (dir))); return bestd; } /* check if is there is a bom in the near returns the directions (bits 0(left)-3(down) bit4 bomb under us) where a bomb is */ int ai_findnearbombs (_point pos) { int d, res = 0, // result if there is a bomb done = 0; _point m[4]; // direction addition _point dist[4]; // to check every direction (on three ways) for (d = 0; d < 4; d++) switch (d) { case (left): m[d].x = -1; m[d].y = 0; dist[d].x = pos.x - 1; dist[d].y = pos.y; break; case (right): m[d].x = 1; m[d].y = 0; dist[d].x = pos.x + 1; dist[d].y = pos.y; break; case (up): m[d].x = 0; m[d].y = -1; dist[d].x = pos.x; dist[d].y = pos.y - 1; break; case (down): m[d].x = 0; m[d].y = 1; dist[d].x = pos.x; dist[d].y = pos.y + 1; break; } while (!done) { done = 1; /* check every direction again */ for (d = 0; d < 4; d++) if (dist[d].x >= 0 && dist[d].x < map.size.x && dist[d].y >= 0 && dist[d].y < map.size.y) { if (map.bfield[dist[d].x][dist[d].y] != 0) { res |= (1 << d); // set the bit for the direction; dist[d].x = dist[d].y = -1; // don't check no more. } if (map.field[dist[d].x][dist[d].y].type != FT_nothing) dist[d].x = dist[d].y = -1; // don't check no more. else if (dist[d].x != -1 && dist[d].y != -1) { dist[d].x += m[d].x; dist[d].y += m[d].y; done = 0; } } } if (map.bfield[pos.x][pos.y] != 0) res |= 16; // set the 4th. bit return res; } /* check if we are still running and fill out the position return == 0 we're still walking ... else we have reached a point */ inline int ai_checkpos (_player * pl, _point * pos) { _point _p; _p.x = pl->pos.x & 255; _p.y = pl->pos.y & 255; pos->x = pl->pos.x >> 8; pos->y = pl->pos.y >> 8; if (_p.x > 128) (pos->x)++; if (_p.y > 128) (pos->y)++; return ((_p.x < 42 || _p.x > 213) && (_p.y < 42 || _p.y > 213)); }; /* random choose direction */ int ai_choosedir (int dir, int nearbomb, int oldpos) { int rdir[4]; int bdir[4 * 3]; int i, rnr, bnr; for (rnr = bnr = i = 0; i < 4; i++) { if ((dir & (1 << i)) != 0) { rdir[rnr] = i; rnr++; } if (((nearbomb & (DIRM_up + DIRM_down)) != 0) && ((dir & (1 << i)) != 0) && (i == left || i == right) && i != ai_invertdir (oldpos)) { bdir[bnr] = i; bnr++; } if (((nearbomb & (DIRM_left + DIRM_right)) != 0) && ((dir & (1 << i)) != 0) && (i == down || i == up) && i != ai_invertdir (oldpos)) { bdir[bnr] = i; bnr++; } } if (rnr == 0) return (s_random (4)); if (bnr != 0) i = bdir[s_random (bnr)]; else i = rdir[s_random (rnr)]; return i; }; inline int ai_invertdir (int dir) { int idir; switch (dir) { case (left): idir = right; break; case (right): idir = left; break; case (down): idir = up; break; default: idir = down; break; } return idir; }; inline int ai_checknewpos (_point pos, int d) { _point m; switch (d) { case (left): m.x = pos.x - 1; m.y = pos.y; break; case (right): m.x = pos.x + 1; m.y = pos.y; break; case (up): m.x = pos.x; m.y = pos.y - 1; break; default: m.x = pos.x; m.y = pos.y + 1; break; } return (ai_findnearbombs (m) == 0); }; /* create a giving number of ai players */ void single_create_ai (int players) { int p, count, gfx_sel, i = 0; _player *pl; for (count = 0; count < players; count++) { /* find free players */ for (pl = NULL, p = 0; (pl == NULL && p < MAX_PLAYERS); p++) if (!(PS_IS_used (bman.players[p].state))) { pl = &bman.players[p]; sprintf (pl->name, "AI %d", count + 1); pl->state |= PSF_used + PSF_alife + PSF_playing; do { gfx_sel = s_random (MAX_PLAYERS); MW_IS_GFX_SELECT (gfx_sel, i); } while (i != -1); player_set_gfx (pl, gfx_sel); } if (pl == NULL) return; } }; /* single player game win/point screen */ void single_playergame () { int p, done = 0; /* delete player from the game */ for (p = 0; p < MAX_PLAYERS; p++) { bman.players[p].points = 0; bman.players[p].wins = 0; bman.players[p].state = 0; bman.players[p].gfx_nr = -1; bman.players[p].gfx = NULL; } for (bman.p_nr = -1, p = 0; (bman.p_nr == -1 && p < MAX_PLAYERS); p++) if (!(PS_IS_used (bman.players[p].state))) bman.p_nr = p; if (bman.p_nr >= MAX_PLAYERS) { printf ("ERROR in function (single_game_new): couldn't find any free player\n"); exit (1); } if ((bman.players[bman.p_nr].gfx_nr = single_select_player ()) == -1) return; bman.players[bman.p_nr].state = PSF_used + PSF_alife + PSF_playing; strncpy (bman.players[bman.p_nr].name, bman.playername, LEN_PLAYERNAME); single_create_ai (bman.ai_players); bman.state = GS_ready; while (!done && bman.state != GS_quit && bman.state != GS_startup) { single_game_new (); game_start (); game_loop (); game_end (); } }; /* single player loop for calculating the ai players */ void single_loop () { int p; _player *pl; _point plpos; int nearbomb = 0, bestbdir, i; _airunaway rawdir; for (p = 0; p < MAX_PLAYERS; p++) if (p != bman.p_nr && PS_IS_alife (bman.players[p].state)) { pl = &bman.players[p]; i = ai_checkpos (pl, &plpos); if (!i) /* we're still moving */ pl->m = 1; else { nearbomb = ai_findnearbombs (plpos); if (nearbomb == 0) { // no bombs found bestbdir = ai_findbestbombdir (plpos, pl->d, pl->range); if (bestbdir & DIRM_under) { if (ai_easyrunaway (plpos) != 0) player_drop_bomb (p); } else if (bestbdir == 0) { pl->d = s_random (4); pl->m = 1; } else { pl->d = ai_choosedir (bestbdir, 0, pl->d); pl->m = 1; } if (!ai_checknewpos (plpos, pl->d)) pl->m = 0; } else { // bombs in the near found rawdir = ai_runawayfrom (plpos, nearbomb, 0); if (rawdir.dir != 0 && rawdir.bestdir == -1) { pl->d = ai_choosedir (rawdir.dir, nearbomb, pl->d); // we have to make a choice.. do it pl->m = 1; } else if (rawdir.bestdir != -1) { pl->d = rawdir.bestdir; pl->m = 1; } } } player_ilness_loop (p); move_player (p); } }; /* singleplayer menü with some options you can make */ void single_menu () { int menuselect = 0; _menu menu[] = { {1, "Start Game"}, {2, "Number Of Players"}, {3, "Map Option"}, {4, "Return To Main Menu"}, {-1, ""} }; while (menuselect != -1 && bman.state != GS_quit) { sprintf (menu[1].text, "%d AI Player", bman.ai_players); menuselect = menu_loop ("Single Player", menu, menuselect); switch (menuselect) { case (0): // Start Game single_playergame (); break; case (1): // Change number of Player bman.ai_players++; if (bman.ai_players >= MAX_PLAYERS) { if (debug) bman.ai_players = 0; // null players with debugging on else bman.ai_players = 1; } break; case (2): // Map Options mapmenu (); break; case (3): menuselect = -1; break; } } }; /* single player selection return value the gfx of the player or -1 for escape */ int single_select_player () { int selgfx = 0, done = 0, x, y, keypressed = 0, frame = 0, d = 0, dto = 2, newplayer = -1, pos = 0, i; SDL_Rect dest, src; char text[255]; SDL_Event event; Uint8 *keys; Uint32 timeloop1; menu_displaytext ("Loading..", "Please Wait", 32, 128, 32); dest.x = dest.y = 0; dest.w = gfx.res.x; dest.h = gfx.res.y; gfx_load_players (64, 64); gfx_blitupdaterectclear (); draw_logo (); strcpy (text, "Select your Player"); x = (gfx.res.x - (font[2].size.x * strlen (text)) - 64) / 2; y = (gfx.res.y - font[2].size.y - 64 - 128) / 2; font_setcolor (128, 128, 0, 2); font_draw (x - 2, y, text, 2); font_draw (x + 2, y, text, 2); font_draw (x, y - 2, text, 2); font_draw (x, y + 2, text, 2); font_setcolor (255, 255, 128, 2); font_draw (x, y, text, 2); gfx_blitupdaterectadd (&dest); while (!done) { /* draw selected player */ frame++; if (frame >= gfx.players[selgfx].ani.frames) { frame = 0; if (dto-- <= 0 && newplayer == -1) { dto = 5; if (d == up) d = left; else if (d == left) d = down; else if (d == down) d = right; else d = up; } } if (newplayer != -1) { SDL_Rect dest1, src1; pos += 8; if (d == left) { /* image 1 */ src.h = dest.h = gfx.players[selgfx].ani.h; src.w = dest.w = gfx.players[selgfx].ani.w; src.x = gfx.players[selgfx].ani.w * d; src.y = gfx.players[selgfx].ani.h * frame; dest.x = ((gfx.res.x - gfx.players[selgfx].ani.w) / 2) - pos; dest.y = y + font[0].size.y + 64; /* image 2 */ src1.h = dest1.h = gfx.players[newplayer].ani.h; src1.w = dest1.w = gfx.players[newplayer].ani.w; src1.x = gfx.players[newplayer].ani.w * d; src1.y = gfx.players[newplayer].ani.h * frame; dest1.x = ((gfx.res.x - gfx.players[newplayer].ani.w) / 2) + ((gfx.res.x / 2)+ 128) - pos; dest1.y = y + font[0].size.y + 64; } else { /* image 1 */ src.h = dest.h = gfx.players[selgfx].ani.h; src.w = dest.w = gfx.players[selgfx].ani.w; src.x = gfx.players[selgfx].ani.w * d; src.y = gfx.players[selgfx].ani.h * frame; dest.x = ((gfx.res.x - gfx.players[selgfx].ani.w) / 2) + pos; dest.y = y + font[0].size.y + 64; /* image 2 */ src1.h = dest1.h = gfx.players[newplayer].ani.h; src1.w = dest1.w = gfx.players[newplayer].ani.w; src1.x = gfx.players[newplayer].ani.w * d; src1.y = gfx.players[newplayer].ani.h * frame; dest1.x = ((gfx.res.x - gfx.players[newplayer].ani.w) / 2) - ((gfx.res.x / 2)+ 128) + pos; dest1.y = y + font[0].size.y + 64; } redraw_logo (0, y + font[0].size.y + 64, gfx.res.x, 128); if (dest.x + dest.w > 0) gfx_blit (gfx.players[selgfx].ani.image, &src, gfx.screen, &dest, 0); if (dest1.x + dest1.w > 0) gfx_blit (gfx.players[newplayer].ani.image, &src1, gfx.screen, &dest1, 0); if (pos > (gfx.res.x / 2)+ 128) { selgfx = newplayer; newplayer = -1; pos = 0; frame = 0; d = down; dto = 5; } frame++; } else { src.h = dest.h = gfx.players[selgfx].ani.h; src.w = dest.w = gfx.players[selgfx].ani.w; src.x = gfx.players[selgfx].ani.w * d; src.y = gfx.players[selgfx].ani.h * frame; dest.x = (gfx.res.x - gfx.players[selgfx].ani.w) / 2; dest.y = y + font[0].size.y + 64; redraw_logo (dest.x, dest.y, dest.w, dest.h); gfx_blit (gfx.players[selgfx].ani.image, &src, gfx.screen, &dest, 0); } gfx_blitdraw (); /* do the keyboard handling */ if (SDL_PollEvent (&event) != 0) switch (event.type) { case (SDL_QUIT): bman.state = GS_quit; bman.p_nr = -1; done = 1; selgfx = -1; } keys = SDL_GetKeyState (NULL); if (keys[SDLK_ESCAPE] && event.type == SDL_KEYDOWN) { /* we want to quit */ done = 1; bman.p_nr = -1; keypressed = 1; bman.state = GS_startup; selgfx = -1; } if (keys[SDLK_LEFT] && (!keypressed) && newplayer == -1) { newplayer = selgfx - 1; if (newplayer < 0) newplayer = MAX_PLAYERS - 1; d = left; } if (keys[SDLK_RIGHT] && (!keypressed) && newplayer == -1) { newplayer = selgfx + 1; if (newplayer >= MAX_PLAYERS) newplayer = 0; d = right; } if ((keys[SDLK_RETURN] ||keys[SDLK_RCTRL] || keys[SDLK_LCTRL]) && (!keypressed) && (event.type = SDL_KEYDOWN) && newplayer == -1) { done = 1; keypressed = 1; // d_printf("return pressed - done=1\n"); } if (event.type == SDL_KEYUP) keypressed = 0; else if (event.type == SDL_KEYDOWN) keypressed = 1; // calculate time sync. timeloop1 = SDL_GetTicks (); if (newplayer != -1) i = 17; else i = 25; while (timeloop1 - timestamp >= 0 && timeloop1 - timestamp < i) { s_delay (i - (timeloop1 - timestamp) - 1); timeloop1 = SDL_GetTicks (); } timestamp = timeloop1; } gfx_free_players (); return selgfx; };