static int engrid_cell(grid * gold, gridindex *index) { vector gmin, gmax, gsize; flt len; int numobj, numcbrt, xs, ys, zs; grid * g; objectlist **list; objectlist * newobj; list = &gold->cells[index->z*gold->xsize*gold->ysize + index->y*gold->xsize + index->x]; if (*list == NULL) return 0; numobj = cellbound(gold, index, &gmin, &gmax); VSub(&gmax, &gmin, &gsize); len = 1.0 / (MYMAX( MYMAX(gsize.x, gsize.y), gsize.z )); gsize.x *= len; gsize.y *= len; gsize.z *= len; if (numobj > 16) { numcbrt = (int) cbrt(2*numobj); xs = (int) ((flt) numcbrt * gsize.x); if (xs < 1) xs = 1; ys = (int) ((flt) numcbrt * gsize.y); if (ys < 1) ys = 1; zs = (int) ((flt) numcbrt * gsize.z); if (zs < 1) zs = 1; g = (grid *) newgrid(xs, ys, zs, gmin, gmax); engrid_objectlist(g, list); newobj = (objectlist *) rt_getmem(sizeof(objectlist)); newobj->obj = (object *) g; newobj->next = *list; *list = newobj; g->nextobj = gold->objects; gold->objects = (object *) g; } return 1; }
int engrid_scene(object ** list) { grid * g; int numobj, numcbrt; vector gmin, gmax; gridindex index; if (*list == NULL) return 0; numobj = countobj(*list); if ( !silent_mode ) fprintf(stderr, "Scene contains %d bounded objects.\n", numobj); if (numobj > 16) { numcbrt = (int) cbrt(4*numobj); globalbound(list, &gmin, &gmax); g = (grid *) newgrid(numcbrt, numcbrt, numcbrt, gmin, gmax); engrid_objlist(g, list); numobj = countobj(*list); g->nextobj = *list; *list = (object *) g; /* now create subgrids.. */ for (index.z=0; index.z<g->zsize; index.z++) { for (index.y=0; index.y<g->ysize; index.y++) { for (index.x=0; index.x<g->xsize; index.x++) { engrid_cell(g, &index); } } } } return 1; }
/* Load the game file. */ int load_game_file(nbstate *state) { FILE *fp; int line = 1; level *l, *lev = NULL; char buf[256], *p; int inlevelblock = 0; /* Generate the full game file name including the directory: */ snprintf(buf, 256, "%s/%s", state->gamedir, state->gamefile); /* Try to open the game file: */ if(!(fp = fopen(buf, "r"))) { /* It failed, so print an error message and return "failure": */ GrError("Failed to open game file \"%s\": %s\n", buf, strerror(errno)); return 1; } /* Read the file one line at a time in a loop: */ while(fgets(buf, 256, fp)) { /* Look for the newline at the end of the line. */ if(!(p = strchr(buf, '\n'))) { /* There wasn't one, which probably means that the * line was longer than 255 characters. */ GrError("Too long line on line %d of game file " "\"%s\"\n", line, state->gamefile); return 1; } *p = 0; /* Get rid of the newline. */ /* Ignore comments and blank lines: */ if(*buf == '#' || *buf == 0) { /* Compare the line against each of the different keywords: */ } else if(!memcmp(buf, "TitleBackground ", 16)) { if(state->titlebackground) { redefinewarning("TitleBackground", line, state->gamefile); free(state->titlebackground); } if(!(state->titlebackground = strdup(buf + 16))) { oom(); goto err; } } else if(!memcmp(buf, "TitleBackgroundTiled ", 21)) { /* Check whether the parameter to TitleBackgroundTiled * is "Yes" (1), "No" (0), or something else (parse * error): */ if(!strcmp(buf + 21, "Yes")) state->backgroundtiled = 1; else if(!strcmp(buf + 21, "No")) state->backgroundtiled = 0; else goto parseerr; } else if(!memcmp(buf, "TitleSplash ", 12)) { if(state->titlesplash) { redefinewarning("TitleSplash", line, state->gamefile); free(state->titlesplash); } if(!(state->titlesplash = strdup(buf + 12))) { oom(); goto err; } } else if(!memcmp(buf, "GameWonSplash ", 14)) { if(state->gamewonsplash) { redefinewarning("GameWonSplash", line, state->gamefile); free(state->gamewonsplash); } if(!(state->gamewonsplash = strdup(buf + 14))) { oom(); goto err; } } else if(!memcmp(buf, "GameLostSplash ", 15)) { if(state->gamelostsplash) { redefinewarning("GameLostSplash", line, state->gamefile); free(state->gamelostsplash); } if(!(state->gamelostsplash = strdup(buf + 15))) { oom(); goto err; } } else if(!memcmp(buf, "NormalPoints ", 13)) { /* Convert the parameter to NormalPoints into a * number: */ state->normalpoints = strtol(buf + 13, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "SmallBonusPoints ", 17)) { state->smallbonuspoints = strtol(buf + 17, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "MediumBonusPoints ", 18)) { state->mediumbonuspoints = strtol(buf + 18, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "LargeBonusPoints ", 17)) { state->largebonuspoints = strtol(buf + 17, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "HugeBonusPoints ", 16)) { state->hugebonuspoints = strtol(buf + 16, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "PowerUpPoints ", 14)) { state->poweruppoints = strtol(buf + 14, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "PowerDownPoints ", 16)) { state->powerdownpoints = strtol(buf + 16, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "StartBalls ", 11)) { state->startballs = strtol(buf + 11, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "NewLevelBalls ", 14)) { state->newlevelballs = strtol(buf + 14, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "BallImage ", 10)) { /* If the ball sprite has already been defined, print * a warning and destroy the old sprite: */ if(state->ball.s) { redefinewarning("BallImage", line, state->gamefile); destroy_sprite(state, state->ball.s); } /* Try to load the ball sprite: */ if(!(state->ball.s = load_sprite(state, buf + 10, -1, -1))) { /* That failed so try to make an empty one: */ if(!(state->ball.s = make_empty_sprite(state, buf + 10, DEFAULT_BALL_SIZE, DEFAULT_BALL_SIZE))) { /* That failed too so print an error * message and give up: */ GrError("Couldn't create " "ball sprite on line " "%d of game file " "\"%s\"\n", line, state->gamefile); goto err; } /* Fill in the dummy sprite with a white * circle: */ GrSetGCForeground(state->gc, GR_COLOR_WHITE); GrFillEllipse(state->ball.s->p, state->gc, DEFAULT_BALL_SIZE / 2, DEFAULT_BALL_SIZE / 2, (DEFAULT_BALL_SIZE / 2) - 1, (DEFAULT_BALL_SIZE / 2) - 1); GrFillEllipse(state->ball.s->a, state->gc, DEFAULT_BALL_SIZE / 2, DEFAULT_BALL_SIZE / 2, (DEFAULT_BALL_SIZE / 2) - 1, (DEFAULT_BALL_SIZE / 2) - 1); } } else if(!memcmp(buf, "SlowBallVelocity ", 17)) { state->ball.sv = strtol(buf + 17, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "NormalBallVelocity ", 19)) { state->ball.nv = strtol(buf + 19, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "FastBallVelocity ", 17)) { state->ball.fv = strtol(buf + 17, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "BatVelocity ", 12)) { state->batv = strtol(buf + 12, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "PowerVelocity ", 14)) { state->powerv = strtol(buf + 14, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "AnimatePeriod ", 14)) { state->animateperiod = strtol(buf + 14, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "BrickWidth ", 11)) { state->brickwidth = strtol(buf + 11, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "BrickHeight ", 12)) { state->brickheight = strtol(buf + 12, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "Brick ", 6)) { /* Parse the brick line: */ if(parse_brick(state, inlevelblock, lev, line, buf + 6)) goto err; } else if(!memcmp(buf, "Width ", 6)) { if(lev) { GrError("Error: Width must be set " "before the first level is defined " "(see line %d of game file \"%s\")\n", line, state->gamefile); goto err; } state->width = strtol(buf + 6, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "Height ", 7)) { if(lev) { GrError("Error: Height must be set " "before the first level is defined " "(see line %d of game file \"%s\")\n", line, state->gamefile); goto err; } state->height = strtol(buf + 7, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "BatHeight ", 10)) { state->batheight = strtol(buf + 10, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "NormalBatWidth ", 15)) { state->batwidths[NORMALBAT] = strtol(buf + 15, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "SmallBatWidth ", 14)) { state->batwidths[SMALLBAT] = strtol(buf + 14, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "LargeBatWidth ", 14)) { state->batwidths[LARGEBAT] = strtol(buf + 14, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "NormalBat ", 10)) { if(state->bats[NORMALBAT]) { redefinewarning("NormalBat", line, state->gamefile); destroy_sprite(state, state->bats[NORMALBAT]); } if(!(state->bats[NORMALBAT] = load_sprite(state, buf + 10, state->batwidths[NORMALBAT], state->batheight))) { if(!(state->bats[NORMALBAT] = make_empty_sprite(state, buf + 10, state->batwidths[NORMALBAT], state->batheight))) { GrError("Couldn't create " "normal bat sprite on " "line %d of game file " "\"%s\"\n", line, state->gamefile); goto err; } GrSetGCForeground(state->gc, GR_COLOR_RED); GrFillRect(state->bats[NORMALBAT]->p, state->gc, 0, 0, state->batwidths[NORMALBAT], state->batheight); GrSetGCForeground(state->gc, GR_COLOR_WHITE); GrFillRect(state->bats[NORMALBAT]->a, state->gc, 0, 0, state->batwidths[NORMALBAT], state->batheight); } } else if(!memcmp(buf, "SmallBat ", 9)) { if(state->bats[SMALLBAT]) { redefinewarning("SmallBat", line, state->gamefile); destroy_sprite(state, state->bats[SMALLBAT]); } if(!(state->bats[SMALLBAT] = load_sprite(state, buf + 9, state->batwidths[SMALLBAT], state->batheight))) { if(!(state->bats[SMALLBAT] = make_empty_sprite(state, buf + 9, state->batwidths[SMALLBAT], state->batheight))) { GrError("Couldn't create " "small bat sprite on " "line %d of game file " "\"%s\"\n", line, state->gamefile); goto err; } GrSetGCForeground(state->gc, GR_COLOR_RED); GrFillRect(state->bats[SMALLBAT]->p, state->gc, 0, 0, state->batwidths[SMALLBAT], state->batheight); GrSetGCForeground(state->gc, GR_COLOR_WHITE); GrFillRect(state->bats[SMALLBAT]->a, state->gc, 0, 0, state->batwidths[SMALLBAT], state->batheight); } } else if(!memcmp(buf, "LargeBat ", 9)) { if(state->bats[LARGEBAT]) { redefinewarning("LargeBat", line, state->gamefile); destroy_sprite(state, state->bats[LARGEBAT]); } if(!(state->bats[LARGEBAT] = load_sprite(state, buf + 9, state->batwidths[LARGEBAT], state->batheight))) { if(!(state->bats[LARGEBAT] = make_empty_sprite(state, buf + 9, state->batwidths[LARGEBAT], state->batheight))) { GrError("Couldn't create " "large bat sprite on " "line %d of game file " "\"%s\"\n", line, state->gamefile); goto err; } GrSetGCForeground(state->gc, GR_COLOR_RED); GrFillRect(state->bats[LARGEBAT]->p, state->gc, 0, 0, state->batwidths[LARGEBAT], state->batheight); GrSetGCForeground(state->gc, GR_COLOR_WHITE); GrFillRect(state->bats[LARGEBAT]->a, state->gc, 0, 0, state->batwidths[LARGEBAT], state->batheight); } } else if(!memcmp(buf, "PowerSprite ", 12)) { /* Parse the PowerSprite line: */ if(parse_powersprite(state, buf + 12, line)) goto err; } else if(!memcmp(buf, "PowerUpTimeout ", 15)) { state->poweruptime = strtol(buf + 15, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "PowerDownTimeout ", 17)) { state->powerdowntime = strtol(buf + 17, &p, 10); if(*p) goto parseerr; } else if(!memcmp(buf, "FadeRate ", 9)) { state->faderate = strtol(buf + 9, &p, 10); if(*p) goto parseerr; if(state->faderate > 255 || state->faderate < 0) { GrError("Invalid fade rate on line " "%d of game file \"%s\"\n", line, state->gamefile); goto err; } } else if(!memcmp(buf, "SolidFloorCheat ", 16)) { if(state->cheats[SFCHEAT]) { redefinewarning("SolidFloorCheat", line, state->gamefile); free(state->cheats[SFCHEAT]); } if(!(state->cheats[SFCHEAT] = strdup(buf + 16))) { oom(); goto err; } if(strlen(state->cheats[SFCHEAT]) > MAXCHEATLEN) { GrError("Cheat sequence too long on " "line %d of game file \"%s\"\n", line, state->gamefile); goto err; } } else if(!memcmp(buf, "TeleportCheat ", 14)) { if(state->cheats[TPCHEAT]) { redefinewarning("TeleportCheat", line, state->gamefile); free(state->cheats[TPCHEAT]); } if(!(state->cheats[TPCHEAT] = strdup(buf + 14))) { oom(); goto err; } if(strlen(state->cheats[TPCHEAT]) > MAXCHEATLEN) { GrError("Cheat sequence too long on " "line %d of game file \"%s\"\n", line, state->gamefile); goto err; } } else if(!memcmp(buf, "NoBounceCheat ", 14)) { if(state->cheats[NBCHEAT]) { redefinewarning("NoBounceCheat", line, state->gamefile); free(state->cheats[NBCHEAT]); } if(!(state->cheats[NBCHEAT] = strdup(buf + 14))) { oom(); goto err; } if(strlen(state->cheats[NBCHEAT]) > MAXCHEATLEN) { GrError("Cheat sequence too long on " "line %d of game file \"%s\"\n", line, state->gamefile); goto err; } } else if(!memcmp(buf, "NoPowerDownCheat ", 17)) { if(state->cheats[NPDCHEAT]) { redefinewarning("NoPowerDownCheat", line, state->gamefile); free(state->cheats[NPDCHEAT]); } if(!(state->cheats[NPDCHEAT] = strdup(buf + 17))) { oom(); goto err; } if(strlen(state->cheats[NPDCHEAT]) > MAXCHEATLEN) { GrError("Cheat sequence too long on " "line %d of game file \"%s\"\n", line, state->gamefile); goto err; } } else if(!memcmp(buf, "NoPowerUpTimeoutCheat ", 22)) { if(state->cheats[NPUTOCHEAT]) { redefinewarning("NoPowerUpTimeoutCheat", line, state->gamefile); free(state->cheats[NPUTOCHEAT]); } if(!(state->cheats[NPUTOCHEAT] = strdup(buf + 22))) { oom(); goto err; } if(strlen(state->cheats[NPUTOCHEAT]) > MAXCHEATLEN) { GrError("Cheat sequence too long on " "line %d of game file \"%s\"\n", line, state->gamefile); goto err; } } else if(!memcmp(buf, "BeginLevel", 10)) { /* Check to make sure we haven't got another BeginLevel * line while already in a level definition block: */ if(inlevelblock) { GrError("Error: BeginLevel while " "already in a level block on line %d " "of game file \"%s\"\n", line, state->gamefile); goto err; } inlevelblock = 1; /* Allocate a new level structure: */ if(!(lev = malloc(sizeof(level)))) { oom(); goto err; } /* Fill in the structure members with some defaults: */ lev->bricks = NULL; lev->backgroundname = NULL; lev->backgroundtiled = DEFAULT_BACKGROUND_TILED; lev->numbricks = 0; lev->next = NULL; /* Allocate and initialise the level grid: */ if(!(lev->grid = newgrid(state))) { oom(); goto err; } /* Link the new level structure onto the end of the * levels list: */ if(!state->levels) state->levels = lev; else { for(l = state->levels; l->next; l = l->next); l->next = lev; } } else if(!memcmp(buf, "LevelBackground ", 16)) { if(!inlevelblock) { notinlevblockerr("LevelBackground", line, state->gamefile); goto err; } if(lev->backgroundname) { redefinewarning("LevelBackground", line, state->gamefile); free(lev->backgroundname); } if(!(lev->backgroundname = strdup(buf + 16))) { oom(); goto err; } } else if(!memcmp(buf, "LevelBackgroundTiled ", 21)) { if(!inlevelblock) { notinlevblockerr("LevelBackgroundTiled", line, state->gamefile); goto err; } if(!strcmp(buf + 21, "Yes")) lev->backgroundtiled = 1; else if(!strcmp(buf + 21, "No")) lev->backgroundtiled = 0; else goto parseerr; } else if(!memcmp(buf, "BeginRows", 9)) { if(!inlevelblock) { notinlevblockerr("BeginRows", line, state->gamefile); goto err; } /* Parse the rows block: */ if(parse_rows(state, lev, fp, &line)) goto err; } else if(!memcmp(buf, "EndRows", 7)) { /* We should never see an EndRows here in a valid * level file because parse_rows() consumes it. */ GrError("Error: EndRows without corresponding " "BeginRows on line %d of game file " "\"%s\"\n", line, state->gamefile); goto err; } else if(!memcmp(buf, "BeginPowers", 11)) { if(!inlevelblock) { notinlevblockerr("BeginPowers", line, state->gamefile); goto err; } if(parse_powers(state, lev, fp, &line)) goto err; } else if(!memcmp(buf, "EndPowers", 9)) { GrError("Error: EndPowers without " "corresponding BeginPowers on line %d " "of game file \"%s\"\n", line, state->gamefile); goto err; } else if(!memcmp(buf, "EndLevel", 8)) { if(!inlevelblock) { GrError("Error: EndLevel while not in " "in a level block on line %d of game " "file \"%s\"\n", line, state->gamefile); goto err; } inlevelblock = 0; state->numlevels++; } else { GrError("Unknown command \"%s\" on line %d " "of game file \"%s\"\n", buf, line, state->gamefile); } /* We keep a count of the line we're on so that errors and * warnings can print out the number of the bad line: */ line++; } /* Check if the reason fgets() failed was because of an I/O error * instead of simply reaching the end of the file: */ if(ferror(fp)) { GrError("Error reading from game file \"%s\" on line " "%d: %s\n", state->gamefile, line, strerror(errno)); goto err; } /* Allocate and initialise the current game grid: */ if(!(state->grid = newgrid(state))) { oom(); goto err; } fclose(fp); /* Close the game file. */ return 0; /* Success. */ parseerr: /* A parse error occured so print an error message: */ GrError("Parse error on line %d of game file \"%s\"\n", line, state->gamefile); err: /* Some other error occured and we've already printed the error message. */ fclose(fp); /* Close the game file (may fail but we don't care). */ return 1; /* Failure. */ }