/* * file map.c - handling the level tile map * * Program XBLAST * (C) by Oliver Vogel (e-mail: m.vogel@ndh.net) * * $Id: map.c,v 1.7 2004/06/30 16:06:00 iskywalker Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2; or (at your option) * any later version * * This program is distributed in the hope that it will be entertaining, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "map.h" #include "atom.h" #include "bomb.h" #include "geom.h" #include "gui.h" #include "info.h" #include "str_util.h" #include "random.h" /* * local types */ /* extra distribution */ typedef enum { DEnone = 0, DEsingle, DEall, DEspecial, DEget, DEdouble } BMExtraDistribution; /* one maze column */ typedef unsigned short col[MAZE_H]; /* extra probabilities */ typedef struct { int bomb; int range; int ill; int invinc; int evil; } BMExtraProb; /* * local variables */ static col em1[MAZE_W]; static col em2[MAZE_W]; static col *explMaze = em1; static col *oldExplMaze = em2; static col em3[MAZE_W]; // KOEN static col *kexplMaze = em3; // KOEN static unsigned explDirty; static unsigned explOldDirty; static unsigned redrawMaze[MAZE_H]; static unsigned redrawStat[STAT_H]; static BMMapTile maze[MAZE_W][MAZE_H]; static BMMapTile extra[MAZE_W][MAZE_H]; static BMExtraDistribution distribExtras; static BMExtraProb extraProb; /* conversion table for level parsing */ static DBToInt distribExtraTable[] = { { "all", (int) DEall }, { "double", (int) DEdouble }, { "get", (int) DEget }, { "single", (int) DEsingle }, { "special", (int) DEspecial }, { NULL, (int) DEnone }, }; /* * compare to tile graphics */ static XBBool CompareTile (const BMBlockTile *a, const BMBlockTile *b) { return ( 0 == strcmp (a->name, b->name) && a->fg == b->fg && a->bg == b->bg && a->add == b->add); } /* CompareTile */ /* * public function: ConfigLevelGraphics */ XBBool ConfigLevelGraphics (const DBSection *section) { int i; const char *s; char **argv[MAX_BLOCK]; int argc; BMBlockTile tile[MAX_BLOCK]; assert (section != NULL); /* parse each entry */ for (i=0; i< MAX_BLOCK; i++) { if (! DB_GetEntryString (section, atomArrayBlock00[i], &s) ) { return XBFalse; } argv[i] = SplitString (s, &argc); assert (NULL != argv[i]); /* check block type */ if (1 == argc) { /* simple rgb pixmap */ tile[i].name = argv[i][0]; tile[i].fg = COLOR_INVALID; tile[i].bg = COLOR_INVALID; tile[i].add = COLOR_INVALID; } else if (4 == argc) { /* layered ppm pixmap */ tile[i].name = argv[i][0]; if (COLOR_INVALID == (tile[i].fg = StringToColor (argv[i][1]) ) || COLOR_INVALID == (tile[i].bg = StringToColor (argv[i][2]) ) || COLOR_INVALID == (tile[i].add = StringToColor (argv[i][3]) ) ) { return XBFalse; } } else { return XBFalse; } } /* load blocks */ for (i=0; i< MAX_BLOCK; i++) { if (tile[i].fg != COLOR_INVALID) { GUI_LoadBlockCch (i, tile[i].name, tile[i].fg, tile[i].bg, tile[i].add); } else { GUI_LoadBlockRgb (i, tile[i].name); } } GUI_InitExplosionBlocks (); /* set level info */ if (CompareTile (tile + BTFree, tile + BTBlock) ) { AddLevelInfo ("Walls are invisble"); } if (CompareTile (tile + BTBomb, tile + BTRange) && CompareTile (tile + BTBomb, tile + BTSick) && CompareTile (tile + BTBomb, tile + BTSpecial) ) { AddLevelInfo ("Extras all look the same"); } if (CompareTile (tile + BTExtra, tile + BTBlock) ) { AddLevelInfo ("Some walls are blastable"); } /* clean up */ for (i=0; i< MAX_BLOCK; i++) { free ((char *) argv[i]); } /* that's all */ return XBTrue; } /* ConfigLevelGraphics */ /* * string for extra prob */ static const char * ExtraProbString (int val) { if (val <= 0) { return NULL; } else if (val <=4) { return "scarce"; } else if (val <=8) { return "rare"; } else if (val <=16) { return "uncommon"; } else if (val <=32) { return "common"; } else { return "plentiful"; } } /* ExtraProbName */ /* * clean up graphics allocated by level */ void FinishLevelGraphics (void) { int i; for (i = 0; i< MAX_TILE; i++) { GUI_FreeBlock (i); } GUI_FreeExplosionBlocks (); } /* UnloadBlocks */ /* * configure level map layout */ XBBool ConfigLevelMap (const DBSection *section) { int x, y; const char *s; int prob; assert (section != NULL); /* extra distribution */ if (! DB_ConvertEntryInt (section, atomExtraDistribution, (int *) &distribExtras, distribExtraTable) ) { distribExtras = DEnone; } /* extra probalities */ if (DB_GetEntryInt (section, atomProbBomb, &prob) ) { extraProb.bomb = 64 * prob / 100; } else { extraProb.bomb = 0; } if (DB_GetEntryInt (section, atomProbRange, &prob) ) { extraProb.range = extraProb.bomb + 64 * prob / 100; } else { extraProb.range = extraProb.bomb; } if (DB_GetEntryInt (section, atomProbVirus, &prob) ) { extraProb.ill = extraProb.range + 64 * prob / 100; } else { extraProb.ill = extraProb.range; } if (DB_GetEntryInt (section, atomProbSpecial, &prob) ) { extraProb.invinc = extraProb.ill + 64 * prob / 100; } else { extraProb.invinc = extraProb.ill; } if (DB_GetEntryInt (section, atomProbHidden, &prob) ) { extraProb.evil = extraProb.invinc + 64 * prob / 100; } else { extraProb.evil = extraProb.invinc; } /* the layout */ for (y = 0; y < MAZE_H; y ++) { if (! DB_GetEntryString (section, atomArrayRow00[y], &s) ) { return XBFalse; } for (x = 0; x < MAZE_W; x ++) { switch (s[x]) { case '_': SetMazeBlock (x, y, BTFree); break; case 'B': SetMazeBlock (x, y, BTBlock); break; case 'R': SetMazeBlock (x, y, BTBlockRise); break; case 'X': SetMazeBlock (x, y, BTExtra); break; case 'b': SetMazeBlock (x, y, BTBomb); break; case 'r': SetMazeBlock (x, y, BTRange); break; case 's': SetMazeBlock (x, y, BTSick); break; case 'q': SetMazeBlock (x, y, BTSpecial); break; case 'v': SetMazeBlock (x, y, BTVoid); break; case 'e': SetMazeBlock (x, y, BTEvil); break; case 'V': SetMazeBlock (x, y, BTBackground); break; default: return XBFalse; } explMaze[x][y] = 0; oldExplMaze[x][y] = 0; redrawMaze[y] &= ~(1< PIXH) { MarkMaze (x/STAT_WIDTH, y/BLOCK_HEIGHT, (x+w-1)/STAT_WIDTH, (y+h-1)/BLOCK_HEIGHT); } else { MarkMaze (x/BLOCK_WIDTH, y/BLOCK_HEIGHT, (x+w-1)/BLOCK_WIDTH, MAZE_H - 1); MarkMaze (x/STAT_WIDTH, MAZE_H, (x+w-1)/STAT_WIDTH, (y+h-1)/BLOCK_HEIGHT); } } /* MarkMazeRect */ /* * */ XBBool SpriteMarked (const Sprite *spr) { int left, right, top, bottom; const BMRectangle *r; int x, y; unsigned mask; /* get sprite drawing rectangle */ r = SpriteRectangle (spr); /* get borders of rectangle */ left = (r->x) / BLOCK_WIDTH; right = (r->x + r->w - 1) / BLOCK_WIDTH; top = (r->y) / BLOCK_HEIGHT; bottom = (r->y + r->h - 1) / BLOCK_HEIGHT; /* set redraw x test-mask */ mask = 0; for (x=left; x<=right; x++) { mask |= (1<MAZE_H ) || (y>MAZE_W-4 ) */); } XBBool CheckMazeSolid (int x, int y) { return (maze[x][y] == BTBlock || maze[x][y] == BTBlockRise || maze[x][y] == BTExtra || maze[x][y] == BTExtraOpen); } /* CheckMazeSolid */ /* * */ XBBool CheckMazeOpen (int x, int y) { return (maze[x][y] == BTFree || maze[x][y] == BTBurned || maze[x][y] == BTVoid); } /* CheckMazeOpen */ /* * */ XBBool CheckMazeExtra (int x, int y) { return (maze[x][y] == BTExtra); } /* CheckMazeExtra */ /* * */ XBBool CheckExplosion (int x, int y) { return (0 != explMaze[x][y]); } /* CheckExplosion */ /* * */ void SetExplBlock (int x, int y, int value) { if((x>=0) && (y>=0) && (x0) && (extraBlock == BTSick)) { extraBlock = 0; } // if((x==1) && (y==4)) { Dbg_Out(" +++++++++++ %i %i \n", maze[x][y], extraBlock);} if (extraBlock != 0) { if(maze[x][y]!=BTVoid) { SetMazeBlock (x, y, BTFree); } } /* check if special extras is distributed immediately */ if ( (extraBlock == BTSpecial) && ( (distribExtras == DEget) || (distribExtras == DEdouble) ) ) { DistributeExtras (0, 0, -1, 0); } return extraBlock ; } /* GetExtra */ /* * check if special extra are distributed */ XBBool DistribSpecial (void) { return (distribExtras == DEspecial); } /* DistribSpecial */ /* * */ static int PutExtras (int freeBlocks, BMPosition *distPos, BMMapTile tile, int numExtras) { int i, where; BMPosition swap; for (i = 0; (freeBlocks > 0) && (i < numExtras); i++) { where = GameRandomNumber (freeBlocks); SetMazeBlock (distPos[where].x, distPos[where].y, tile); freeBlocks --; /* this position is used */ if (where != freeBlocks) { swap = distPos[where]; distPos[where] = distPos[freeBlocks]; distPos[freeBlocks] = swap; } } return freeBlocks; } /* PutExtras */ /* * */ void DistributeExtras (int bombs, int range, int extras, int specials) { int x, y; int i; int freeBlocks = 0; int numExtras = 0; static unsigned distExtra[MAZE_H]; static BMPosition distPos[MAZE_W*MAZE_H]; /* are there extras to distribute */ if (bombs + range + extras + specials == 0) { return; } /* Create Extra Distribution Map */ memset (distExtra, 0, sizeof (distExtra)); /* First check for free Blocks */ for (x = 0; x < MAZE_W; x++) { for (y = 0; y < MAZE_H; y++) { if (maze[x][y] == BTBurned || maze[x][y] == BTFree) { distExtra[y] |= (1 << x); freeBlocks ++; } } } freeBlocks = CheckDistribExpl (distExtra, freeBlocks); if (freeBlocks <= 0) { return; } /* fill dist_koord array */ i = 0; for (x = 0; x < MAZE_W; x ++) { for (y = 0; y < MAZE_H; y ++) { if (distExtra[y] & (1 << x)) { distPos[i].x = x; distPos[i].y = y; i ++; } } } /* Distribute special extras */ /* use -1 for direct distribution on get extra */ switch (distribExtras) { case DEnone: numExtras = 0; break; case DEsingle: numExtras = (extras > 0) ? 1 : 0; break; case DEall: numExtras = (extras > 0) ? extras : 0; break; case DEspecial: numExtras = (specials + 2)/3; break; case DEget: numExtras = (extras < 0) ? 1 : 0; break; case DEdouble: numExtras = (extras < 0) ? 2 : 0; break; } freeBlocks = PutExtras (freeBlocks, distPos, BTSpecial, numExtras); if (freeBlocks <= 0) { return; } /* distribute range extras */ freeBlocks = PutExtras (freeBlocks, distPos, BTRange, range); if (freeBlocks <= 0) { return; } /* distribute bombs */ freeBlocks = PutExtras (freeBlocks, distPos, BTBomb, bombs); if (freeBlocks <= 0) { return; } } /* DistributeExtras */ /* * */ void BlastExtraBlock (int x, int y) { switch(maze[x][y]) { /* open extra block */ case BTExtraOpen: SetMazeBlock (x, y, extra[x][y]); break; /* special extras are redistributed in DEget mode */ case BTSpecial: if (DEget == distribExtras || DEdouble == distribExtras ) { DistributeExtras (0, 0, -1, 0); } /* blast away extra and correct shadow */ case BTSick: case BTBomb: case BTRange: SetMazeBlock (x, y, BTFree); break; default: break; } } /* BlastExtraBlock */ /* * */ void CopyExplBlock (int x, int y, const int block[CHARH][CHARW]) { int xp, yp; for (xp = x; xp < x+CHARW; xp ++) { for (yp = y; yp < y+CHARH; yp ++) { explMaze[xp][yp] = block[yp-y][xp-x]; explDirty |= (1<