/* * Portions of this file are copyright Rebirth contributors and licensed as * described in COPYING.txt. * Portions of this file are copyright Parallax Software and licensed * according to the Parallax license below. * See COPYING.txt for license details. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ /* * * Routines to parse bitmaps.tbl * */ #include #include #include #include #include #include #include #include "pstypes.h" #include "gr.h" #include "bm.h" #include "u_mem.h" #include "dxxerror.h" #include "object.h" #include "vclip.h" #include "effects.h" #include "polyobj.h" #include "wall.h" #include "textures.h" #include "game.h" #include "iff.h" #include "hostage.h" #include "powerup.h" #include "laser.h" #include "sounds.h" #include "piggy.h" #include "aistruct.h" #include "robot.h" #include "weapon.h" #include "gauges.h" #include "player.h" #include "fuelcen.h" #include "endlevel.h" #include "cntrlcen.h" #include "args.h" #include "text.h" #include "physfsx.h" #include "physfs-serial.h" #include "strutil.h" #if DXX_USE_EDITOR #include "editor/texpage.h" #endif #include "compiler-range_for.h" #include "partial_range.h" static void bm_read_sound(char *&arg, int skip, int pc_shareware); static void bm_read_robot_ai(char *&arg, int skip); static void bm_read_robot(char *&arg, int skip); static void bm_read_object(char *&arg, int skip); static void bm_read_player_ship(char *&arg, int skip); static void bm_read_some_file(const std::string &dest_bm, char *&arg, int skip); static void bm_read_weapon(char *&arg, int skip, int unused_flag); static void bm_read_powerup(char *&arg, int unused_flag); static void bm_read_hostage(char *&arg); static void verify_textures(); #define BM_NONE -1 #define BM_COCKPIT 0 #define BM_TEXTURES 2 #define BM_UNUSED 3 #define BM_VCLIP 4 #define BM_EFFECTS 5 #define BM_ECLIP 6 #define BM_WEAPON 7 #define BM_DEMO 8 #define BM_ROBOTEX 9 #define BM_WALL_ANIMS 12 #define BM_WCLIP 13 #define BM_ROBOT 14 #define BM_GAUGES 20 static short N_ObjBitmaps=0; static short N_ObjBitmapPtrs=0; static int Num_robot_ais = 0; namespace dsx { #if DXX_USE_EDITOR powerup_names_array Powerup_names; robot_names_array Robot_names; #endif } //---------------- Internal variables --------------------------- static int SuperX = -1; static int Installed=0; static short tmap_count = 0; static short texture_count = 0; static unsigned clip_count; static unsigned clip_num; static short sound_num; static uint16_t frames; static float play_time; static int hit_sound = -1; static sbyte bm_flag = BM_NONE; static int abm_flag = 0; static int rod_flag = 0; static short wall_open_sound, wall_close_sound,wall_explodes,wall_blastable, wall_hidden; static float vlighting=0; static int obj_eclip; static int dest_vclip; //what vclip to play when exploding static int dest_eclip; //what eclip to play when exploding static fix dest_size; //3d size of explosion static int crit_clip; //clip number to play when destroyed static int crit_flag; //flag if this is a destroyed eclip static int tmap1_flag; //flag if this is used as tmap_num (not tmap_num2) static int num_sounds=0; int linenum; //------------------- Useful macros and variables --------------- #define IFTOK(str) if (!strcmp(arg, str)) //--------------------------------------------------------------- int compute_average_pixel(grs_bitmap *n) { int row, column, color; // char *pptr; int total_red, total_green, total_blue; total_red = 0; total_green = 0; total_blue = 0; for (row=0; rowbm_h; row++) for (column=0; columnbm_w; column++) { color = gr_gpixel (*n, column, row); total_red += gr_palette[color].r; total_green += gr_palette[color].g; total_blue += gr_palette[color].b; } total_red /= (n->bm_h * n->bm_w); total_green /= (n->bm_h * n->bm_w); total_blue /= (n->bm_h * n->bm_w); return BM_XRGB(total_red/2, total_green/2, total_blue/2); } //--------------------------------------------------------------- // Loads a bitmap from either the piggy file, a r64 file, or a // whatever extension is passed. static bitmap_index bm_load_sub(int skip, const char * filename) { bitmap_index bitmap_num; palette_array_t newpal; int iff_error; //reference parm to avoid warning message char fname[20]; bitmap_num.index = 0; if (skip) { return bitmap_num; } removeext( filename, fname ); bitmap_num=piggy_find_bitmap( fname ); if (bitmap_num.index) { return bitmap_num; } grs_bitmap n; iff_error = iff_read_bitmap(filename, n, &newpal); if (iff_error != IFF_NO_ERROR) { Error("File %s - IFF error: %s",filename,iff_errormsg(iff_error)); } gr_remap_bitmap_good(n, newpal, iff_has_transparency ? iff_transparent_color : -1, SuperX); n.avg_color = compute_average_pixel(&n); bitmap_num = piggy_register_bitmap(n, fname, 0); return bitmap_num; } static void ab_load(int skip, const char * filename, array &bmp, unsigned *nframes ) { bitmap_index bi; int iff_error; //reference parm to avoid warning message palette_array_t newpal; char fname[20]; char tempname[20]; if (skip) { Assert( bogus_bitmap_initialized != 0 ); bmp[0] = piggy_register_bitmap(bogus_bitmap, "bogus", 0); *nframes = 1; return; } removeext( filename, fname ); { unsigned i; for (i=0; i, MAX_BITMAPS_PER_BRUSH> bm; iff_error = iff_read_animbrush(filename,bm,nframes,newpal); if (iff_error != IFF_NO_ERROR) { Error("File %s - IFF error: %s",filename,iff_errormsg(iff_error)); } const auto nf = *nframes; if (nf >= bm.size()) return; for (uint_fast32_t i = 0; i != nf; ++i) { snprintf(tempname, sizeof(tempname), "%s#%" PRIuFAST32, fname, i); gr_remap_bitmap_good(*bm[i].get(), newpal, iff_has_transparency ? iff_transparent_color : -1, SuperX); bm[i]->avg_color = compute_average_pixel(bm[i].get()); bmp[i] = piggy_register_bitmap(*bm[i].get(), tempname, 0); } } int ds_load(int skip, const char * filename ) { int i; digi_sound n; char fname[20]; char rawname[100]; if (skip) { // We tell piggy_register_sound it's in the pig file, when in actual fact it's in no file // This just tells piggy_close not to attempt to free it return piggy_register_sound( &bogus_sound, "bogus", 1 ); } removeext(filename, fname); snprintf(rawname, sizeof(rawname), "Sounds/%s.raw", fname); i=piggy_find_sound( fname ); if (i!=255) { return i; } if (auto cfp = PHYSFSX_openReadBuffered(rawname)) { n.length = PHYSFS_fileLength( cfp ); MALLOC( n.data, ubyte, n.length ); PHYSFS_read( cfp, n.data, 1, n.length ); n.bits = 8; n.freq = 11025; } else { return 255; } i = piggy_register_sound( &n, fname, 0 ); return i; } //parse a float static float get_float() { char *xarg; xarg = strtok( NULL, space_tab ); return atof( xarg ); } //parse an int static int get_int() { char *xarg; xarg = strtok( NULL, space_tab ); return atoi( xarg ); } // rotates a byte left one bit, preserving the bit falling off the right //void //rotate_left(char *c) //{ // int found; // // found = 0; // if (*c & 0x80) // found = 1; // *c = *c << 1; // if (found) // *c |= 0x01; //} #define LINEBUF_SIZE 600 namespace dsx { //----------------------------------------------------------------- // Initializes all properties and bitmaps from BITMAPS.TBL file. int gamedata_read_tbl(int pc_shareware) { std::string dest_bm; int have_bin_tbl; ObjType[0] = OL_PLAYER; ObjId[0] = 0; Num_total_object_types = 1; for (unsigned i = 0; i < MAX_SOUNDS; ++i) { Sounds[i] = 255; AltSounds[i] = 255; } range_for (auto &ti, TmapInfo) { ti.eclip_num = eclip_none; ti.flags = 0; } Num_effects = 0; range_for (auto &ec, Effects) { //Effects[i].bm_ptr = (grs_bitmap **) -1; ec.changing_wall_texture = -1; ec.changing_object_texture = -1; ec.segnum = segment_none; ec.vc.num_frames = -1; //another mark of being unused } for (unsigned i = 0; i < MAX_POLYGON_MODELS; ++i) Dying_modelnums[i] = Dead_modelnums[i] = -1; Num_vclips = 0; range_for (auto &i, Vclip) { i.num_frames = -1; i.flags = 0; } range_for (auto &w, WallAnims) w.num_frames = wclip_frames_none; Num_wall_anims = 0; if (Installed) return 1; Installed = 1; // Open BITMAPS.TBL for reading. have_bin_tbl = 0; auto InfoFile = PHYSFSX_openReadBuffered("BITMAPS.TBL"); if (!InfoFile) { InfoFile = PHYSFSX_openReadBuffered("BITMAPS.BIN"); if (!InfoFile) Error("Missing BITMAPS.TBL and BITMAPS.BIN file\n"); have_bin_tbl = 1; } linenum = 0; PHYSFSX_fseek( InfoFile, 0L, SEEK_SET); PHYSFSX_gets_line_t inputline; while (PHYSFSX_fgets(inputline, InfoFile)) { int l; const char *temp_ptr; int skip; linenum++; if (have_bin_tbl) { // is this a binary tbl file decode_text_line (inputline); } else { while (inputline[(l=strlen(inputline))-2]=='\\') { if (!isspace(inputline[l-3])) { //if not space before backslash... inputline[l-2] = ' '; //add one l++; } PHYSFSX_fgets(inputline,InfoFile,l-2); linenum++; } } REMOVE_EOL(inputline); if (strchr(inputline, ';')!=NULL) REMOVE_COMMENTS(inputline); if (strlen(inputline) == LINEBUF_SIZE-1) Error("Possible line truncation in BITMAPS.TBL on line %d\n",linenum); SuperX = -1; if ( (temp_ptr=strstr( inputline, "superx=" )) ) { char *p; auto s = strtol(&temp_ptr[7], &p, 10); if (!*p) SuperX = s; } char *arg = strtok( inputline, space_tab ); if (arg && arg[0] == '@') { arg++; skip = pc_shareware; } else skip = 0; while (arg != NULL ) { // Check all possible flags and defines. if (*arg == '$') bm_flag = BM_NONE; // reset to no flags as default. IFTOK("$COCKPIT") bm_flag = BM_COCKPIT; else IFTOK("$GAUGES") {bm_flag = BM_GAUGES; clip_count = 0;} else IFTOK("$SOUND") bm_read_sound(arg, skip, pc_shareware); else IFTOK("$DOOR_ANIMS") bm_flag = BM_WALL_ANIMS; else IFTOK("$WALL_ANIMS") bm_flag = BM_WALL_ANIMS; else IFTOK("$TEXTURES") bm_flag = BM_TEXTURES; else IFTOK("$VCLIP") {bm_flag = BM_VCLIP; vlighting = 0; clip_count = 0;} else IFTOK("$ECLIP") { bm_flag = BM_ECLIP; vlighting = 0; clip_count = 0; obj_eclip=0; dest_bm.clear(); dest_vclip=-1; dest_eclip=-1; dest_size=-1; crit_clip=-1; crit_flag=0; sound_num=sound_none; } else IFTOK("$WCLIP") { bm_flag = BM_WCLIP; vlighting = 0; clip_count = 0; wall_explodes = wall_blastable = 0; wall_open_sound=wall_close_sound=sound_none; tmap1_flag=0; wall_hidden=0; } else IFTOK("$EFFECTS") {bm_flag = BM_EFFECTS; clip_num = 0;} #if DXX_USE_EDITOR else IFTOK("!METALS_FLAG") TextureMetals = texture_count; else IFTOK("!LIGHTS_FLAG") TextureLights = texture_count; else IFTOK("!EFFECTS_FLAG") TextureEffects = texture_count; #else else IFTOK("!METALS_FLAG") ; else IFTOK("!LIGHTS_FLAG") ; else IFTOK("!EFFECTS_FLAG") ; #endif else IFTOK("lighting") TmapInfo[texture_count-1].lighting = fl2f(get_float()); else IFTOK("damage") TmapInfo[texture_count-1].damage = fl2f(get_float()); else IFTOK("volatile") TmapInfo[texture_count-1].flags |= TMI_VOLATILE; //else IFTOK("Num_effects") Num_effects = get_int(); else IFTOK("Num_wall_anims") Num_wall_anims = get_int(); else IFTOK("clip_num") clip_num = get_int(); else IFTOK("dest_bm") { char *p = strtok( NULL, space_tab ); if (p) dest_bm = p; else dest_bm.clear(); } else IFTOK("dest_vclip") dest_vclip = get_int(); else IFTOK("dest_eclip") dest_eclip = get_int(); else IFTOK("dest_size") dest_size = fl2f(get_float()); else IFTOK("crit_clip") crit_clip = get_int(); else IFTOK("crit_flag") crit_flag = get_int(); else IFTOK("sound_num") sound_num = get_int(); else IFTOK("frames") frames = get_int(); else IFTOK("time") play_time = get_float(); else IFTOK("obj_eclip") obj_eclip = get_int(); else IFTOK("hit_sound") hit_sound = get_int(); else IFTOK("abm_flag") abm_flag = get_int(); else IFTOK("tmap1_flag") tmap1_flag = get_int(); else IFTOK("vlighting") vlighting = get_float(); else IFTOK("rod_flag") rod_flag = get_int(); else IFTOK("superx") get_int(); else IFTOK("open_sound") wall_open_sound = get_int(); else IFTOK("close_sound") wall_close_sound = get_int(); else IFTOK("explodes") wall_explodes = get_int(); else IFTOK("blastable") wall_blastable = get_int(); else IFTOK("hidden") wall_hidden = get_int(); else IFTOK("$ROBOT_AI") bm_read_robot_ai(arg, skip); else IFTOK("$POWERUP") {bm_read_powerup(arg, 0); continue;} else IFTOK("$POWERUP_UNUSED") {bm_read_powerup(arg, 1); continue;} else IFTOK("$HOSTAGE") {bm_read_hostage(arg); continue;} else IFTOK("$ROBOT") {bm_read_robot(arg, skip); continue;} else IFTOK("$WEAPON") {bm_read_weapon(arg, skip, 0); continue;} else IFTOK("$WEAPON_UNUSED") {bm_read_weapon(arg, skip, 1); continue;} else IFTOK("$OBJECT") {bm_read_object(arg, skip); continue;} else IFTOK("$PLAYER_SHIP") {bm_read_player_ship(arg, skip); continue;} else { //not a special token, must be a bitmap! // Remove any illegal/unwanted spaces and tabs at this point. while ((*arg=='\t') || (*arg==' ')) arg++; if (*arg == '\0') { break; } // Otherwise, 'arg' is apparently a bitmap filename. // Load bitmap and process it below: bm_read_some_file(dest_bm, arg, skip); } arg = strtok( NULL, equal_space ); continue; } } NumTextures = texture_count; Num_tmaps = tmap_count; Assert(N_robot_types == Num_robot_ais); //should be one ai info per robot verify_textures(); //check for refereced but unused clip count for (unsigned i = 0; i < MAX_EFFECTS; ++i) if ( ( (Effects[i].changing_wall_texture!=-1) || (Effects[i].changing_object_texture!=-1) ) && (Effects[i].vc.num_frames==~0u) ) Error("EClip %d referenced (by polygon object?), but not defined",i); return 0; } } void verify_textures() { grs_bitmap * bmp; int j; j=0; for (uint_fast32_t i=0; ibm_w!=64)||(bmp->bm_h!=64)||(bmp->bm_rowsize!=64) ) { j++; } } if (j) Error("There are game textures that are not 64x64"); } static void set_lighting_flag(grs_bitmap &bmp) { bmp.set_flag_mask(vlighting < 0, BM_FLAG_NO_LIGHTING); } static void set_texture_name(const char *name) { TmapInfo[texture_count].filename.copy_if(name, FILENAME_LEN); REMOVE_DOTS(&TmapInfo[texture_count].filename[0u]); } static void bm_read_eclip(const std::string &dest_bm, const char *const arg, int skip) { bitmap_index bitmap; Assert(clip_num < MAX_EFFECTS); if (clip_num+1 > Num_effects) Num_effects = clip_num+1; Effects[clip_num].flags = 0; if (!abm_flag) { bitmap = bm_load_sub(skip, arg); Effects[clip_num].vc.play_time = fl2f(play_time); Effects[clip_num].vc.num_frames = frames; Effects[clip_num].vc.frame_time = fl2f(play_time)/frames; Assert(clip_count < frames); Effects[clip_num].vc.frames[clip_count] = bitmap; set_lighting_flag(GameBitmaps[bitmap.index]); Assert(!obj_eclip); //obj eclips for non-abm files not supported! Assert(crit_flag==0); if (clip_count == 0) { Effects[clip_num].changing_wall_texture = texture_count; Assert(tmap_count < MAX_TEXTURES); tmap_count++; Textures[texture_count] = bitmap; set_texture_name(arg); Assert(texture_count < MAX_TEXTURES); texture_count++; TmapInfo[texture_count].eclip_num = clip_num; NumTextures = texture_count; } clip_count++; } else { abm_flag = 0; array bm; ab_load(skip, arg, bm, &Effects[clip_num].vc.num_frames ); Effects[clip_num].vc.play_time = fl2f(play_time); Effects[clip_num].vc.frame_time = Effects[clip_num].vc.play_time/Effects[clip_num].vc.num_frames; clip_count = 0; set_lighting_flag(GameBitmaps[bm[clip_count].index]); Effects[clip_num].vc.frames[clip_count] = bm[clip_count]; if (!obj_eclip && !crit_flag) { Effects[clip_num].changing_wall_texture = texture_count; Assert(tmap_count < MAX_TEXTURES); tmap_count++; Textures[texture_count] = bm[clip_count]; set_texture_name( arg ); Assert(texture_count < MAX_TEXTURES); TmapInfo[texture_count].eclip_num = clip_num; texture_count++; NumTextures = texture_count; } if (obj_eclip) { if (Effects[clip_num].changing_object_texture == -1) { //first time referenced Effects[clip_num].changing_object_texture = N_ObjBitmaps; // XChange ObjectBitmaps N_ObjBitmaps++; } ObjBitmaps[Effects[clip_num].changing_object_texture] = Effects[clip_num].vc.frames[0]; } //if for an object, Effects_bm_ptrs set in object load for(clip_count=1;clip_count < Effects[clip_num].vc.num_frames; clip_count++) { set_lighting_flag(GameBitmaps[bm[clip_count].index]); Effects[clip_num].vc.frames[clip_count] = bm[clip_count]; } } Effects[clip_num].crit_clip = crit_clip; Effects[clip_num].sound_num = sound_num; if (!dest_bm.empty()) { //deal with bitmap for blown up clip char short_name[13]; int i; strcpy(short_name,dest_bm.c_str()); REMOVE_DOTS(short_name); for (i=0;i(TmapInfo[i].filename),short_name)) break; if (i==texture_count) { Textures[texture_count] = bm_load_sub(skip, dest_bm.c_str()); TmapInfo[texture_count].filename.copy_if(short_name); texture_count++; Assert(texture_count < MAX_TEXTURES); NumTextures = texture_count; } Effects[clip_num].dest_bm_num = i; if (dest_vclip==-1) Error("Desctuction vclip missing on line %d",linenum); if (dest_size==-1) Error("Desctuction vclip missing on line %d",linenum); Effects[clip_num].dest_vclip = dest_vclip; Effects[clip_num].dest_size = dest_size; Effects[clip_num].dest_eclip = dest_eclip; } else { Effects[clip_num].dest_bm_num = -1; Effects[clip_num].dest_eclip = eclip_none; } if (crit_flag) Effects[clip_num].flags |= EF_CRITICAL; } static void bm_read_gauges(const char *const arg, int skip) { bitmap_index bitmap; unsigned i, num_abm_frames; if (!abm_flag) { bitmap = bm_load_sub(skip, arg); Assert(clip_count < MAX_GAUGE_BMS); Gauges[clip_count] = bitmap; clip_count++; } else { abm_flag = 0; array bm; ab_load(skip, arg, bm, &num_abm_frames ); for (i=clip_count; i= Num_wall_anims) Num_wall_anims = clip_num+1; } else { unsigned nframes; if (WallAnims[clip_num].num_frames != wclip_frames_none) Error( "AB_Wall clip %d is already used!", clip_num ); abm_flag = 0; array bm; ab_load(skip, arg, bm, &nframes ); WallAnims[clip_num].num_frames = nframes; WallAnims[clip_num].play_time = fl2f(play_time); //WallAnims[clip_num].frame_time = fl2f(play_time)/nframes; WallAnims[clip_num].open_sound = wall_open_sound; WallAnims[clip_num].close_sound = wall_close_sound; WallAnims[clip_num].close_sound = wall_close_sound; strcpy(&WallAnims[clip_num].filename[0], arg); REMOVE_DOTS(&WallAnims[clip_num].filename[0]); if (clip_num >= Num_wall_anims) Num_wall_anims = clip_num+1; set_lighting_flag(GameBitmaps[bm[clip_count].index]); for (clip_count=0;clip_count < WallAnims[clip_num].num_frames; clip_count++) { Textures[texture_count] = bm[clip_count]; set_lighting_flag(GameBitmaps[bm[clip_count].index]); WallAnims[clip_num].frames[clip_count] = texture_count; REMOVE_DOTS(arg); snprintf(&TmapInfo[texture_count].filename[0u], TmapInfo[texture_count].filename.size(), "%s#%d", arg, clip_count); Assert(texture_count < MAX_TEXTURES); texture_count++; NumTextures = texture_count; } } } static void bm_read_vclip(const char *const arg, int skip) { bitmap_index bi; Assert(clip_num < VCLIP_MAXNUM); if (!abm_flag) { if ( (Vclip[clip_num].num_frames != ~0u) && (clip_count==0) ) Error( "Vclip %d is already used!", clip_num ); bi = bm_load_sub(skip, arg); Vclip[clip_num].play_time = fl2f(play_time); Vclip[clip_num].num_frames = frames; Vclip[clip_num].frame_time = fl2f(play_time)/frames; Vclip[clip_num].light_value = fl2f(vlighting); Vclip[clip_num].sound_num = sound_num; set_lighting_flag(GameBitmaps[bi.index]); Assert(clip_count < frames); Vclip[clip_num].frames[clip_count++] = bi; if (rod_flag) { rod_flag=0; Vclip[clip_num].flags |= VF_ROD; } } else { abm_flag = 0; if ( (Vclip[clip_num].num_frames != ~0u) ) Error( "AB_Vclip %d is already used!", clip_num ); array bm; ab_load(skip, arg, bm, &Vclip[clip_num].num_frames ); if (rod_flag) { //int i; rod_flag=0; Vclip[clip_num].flags |= VF_ROD; } Vclip[clip_num].play_time = fl2f(play_time); Vclip[clip_num].frame_time = fl2f(play_time)/Vclip[clip_num].num_frames; Vclip[clip_num].light_value = fl2f(vlighting); Vclip[clip_num].sound_num = sound_num; set_lighting_flag(GameBitmaps[bm[clip_count].index]); for (clip_count=0;clip_count < Vclip[clip_num].num_frames; clip_count++) { set_lighting_flag(GameBitmaps[bm[clip_count].index]); Vclip[clip_num].frames[clip_count] = bm[clip_count]; } } } // ------------------------------------------------------------------------------ static void get4fix(array &fixp) { char *curtext; range_for (auto &i, fixp) { curtext = strtok(NULL, space_tab); i = fl2f(atof(curtext)); } } // ------------------------------------------------------------------------------ static void get4byte(array &bytep) { char *curtext; range_for (auto &i, bytep) { curtext = strtok(NULL, space_tab); i = atoi(curtext); } } // ------------------------------------------------------------------------------ // Convert field of view from an angle in 0..360 to cosine. static void adjust_field_of_view(array &fovp) { fixang tt; float ff; range_for (auto &i, fovp) { ff = - f2fl(i); if (ff > 179) { ff = 179; } ff = ff/360; tt = fl2f(ff); i = fix_cos(tt); } } static void clear_to_end_of_line(char *&arg) { arg = NULL; } static void bm_read_sound(char *&arg, int skip, int pc_shareware) { int alt_sound_num; const int read_sound_num = get_int(); alt_sound_num = pc_shareware ? read_sound_num : get_int(); if ( read_sound_num>=MAX_SOUNDS ) Error( "Too many sound files.\n" ); if (read_sound_num >= num_sounds) num_sounds = read_sound_num+1; arg = strtok(NULL, space_tab); Sounds[read_sound_num] = ds_load(skip, arg); if ( alt_sound_num == 0 ) AltSounds[read_sound_num] = sound_num; else if (alt_sound_num < 0 ) AltSounds[read_sound_num] = 255; else AltSounds[read_sound_num] = alt_sound_num; if (Sounds[read_sound_num] == 255) Error("Can't load soundfile <%s>",arg); } // ------------------------------------------------------------------------------ static void bm_read_robot_ai(char *&arg, int skip) { char *robotnum_text; int robotnum; robot_info *robptr; robotnum_text = strtok(NULL, space_tab); robotnum = atoi(robotnum_text); Assert(robotnum < MAX_ROBOT_TYPES); robptr = &Robot_info[robotnum]; Assert(robotnum == Num_robot_ais); //make sure valid number if (skip) { Num_robot_ais++; clear_to_end_of_line(arg); return; } Num_robot_ais++; get4fix(robptr->field_of_view); get4fix(robptr->firing_wait); get4byte(robptr->rapidfire_count); get4fix(robptr->turn_time); array fire_power, // damage done by a hit from this robot shield; // shield strength of this robot get4fix(fire_power); get4fix(shield); get4fix(robptr->max_speed); get4fix(robptr->circle_distance); get4byte(robptr->evade_speed); robptr->always_0xabcd = 0xabcd; adjust_field_of_view(robptr->field_of_view); } // ---------------------------------------------------------------------------------------------- //this will load a bitmap for a polygon models. it puts the bitmap into //the array ObjBitmaps[], and also deals with animating bitmaps //returns a pointer to the bitmap static grs_bitmap *load_polymodel_bitmap(int skip, const char *name) { assert(N_ObjBitmaps < ObjBitmaps.size()); // Assert( N_ObjBitmaps == N_ObjBitmapPtrs ); if (name[0] == '%') { //an animating bitmap! int eclip_num; eclip_num = atoi(name+1); if (Effects[eclip_num].changing_object_texture == -1) { //first time referenced Effects[eclip_num].changing_object_texture = N_ObjBitmaps; ObjBitmapPtrs[N_ObjBitmapPtrs++] = N_ObjBitmaps; N_ObjBitmaps++; } else { ObjBitmapPtrs[N_ObjBitmapPtrs++] = Effects[eclip_num].changing_object_texture; } return NULL; } else { ObjBitmaps[N_ObjBitmaps] = bm_load_sub(skip, name); ObjBitmapPtrs[N_ObjBitmapPtrs++] = N_ObjBitmaps; N_ObjBitmaps++; return &GameBitmaps[ObjBitmaps[N_ObjBitmaps-1].index]; } } #define MAX_MODEL_VARIANTS 4 // ------------------------------------------------------------------------------ static void bm_read_robot(char *&arg, int skip) { char *model_name[MAX_MODEL_VARIANTS]; int n_models,i; int first_bitmap_num[MAX_MODEL_VARIANTS]; char *equal_ptr; auto exp1_vclip_num=vclip_none; auto exp1_sound_num=sound_none; auto exp2_vclip_num=vclip_none; auto exp2_sound_num=sound_none; fix lighting = F1_0/2; // Default fix strength = F1_0*10; // Default strength fix mass = f1_0*4; fix drag = f1_0/2; weapon_id_type weapon_type = weapon_id_type::LASER_ID_L1; int contains_count=0, contains_id=0, contains_prob=0, contains_type=0; int score_value=1000; int cloak_type=0; // Default = this robot does not cloak int attack_type=0; // Default = this robot attacks by firing (1=lunge) int boss_flag=0; // Default = robot is not a boss. int see_sound = ROBOT_SEE_SOUND_DEFAULT; int attack_sound = ROBOT_ATTACK_SOUND_DEFAULT; int claw_sound = ROBOT_CLAW_SOUND_DEFAULT; Assert(N_robot_types < MAX_ROBOT_TYPES); if (skip) { Robot_info[N_robot_types].model_num = -1; N_robot_types++; Num_total_object_types++; clear_to_end_of_line(arg); return; } model_name[0] = strtok( NULL, space_tab ); first_bitmap_num[0] = N_ObjBitmapPtrs; n_models = 1; // Process bitmaps bm_flag=BM_ROBOT; arg = strtok( NULL, space_tab ); while (arg!=NULL) { equal_ptr = strchr( arg, '=' ); if ( equal_ptr ) { *equal_ptr='\0'; equal_ptr++; // if we have john=cool, arg is 'john' and equal_ptr is 'cool' if (!d_stricmp( arg, "exp1_vclip" )) { exp1_vclip_num = atoi(equal_ptr); } else if (!d_stricmp( arg, "exp2_vclip" )) { exp2_vclip_num = atoi(equal_ptr); } else if (!d_stricmp( arg, "exp1_sound" )) { exp1_sound_num = atoi(equal_ptr); } else if (!d_stricmp( arg, "exp2_sound" )) { exp2_sound_num = atoi(equal_ptr); } else if (!d_stricmp( arg, "lighting" )) { lighting = fl2f(atof(equal_ptr)); if ( (lighting < 0) || (lighting > F1_0 )) { Error( "In bitmaps.tbl, lighting value of %.2f is out of range 0..1.\n", f2fl(lighting)); } } else if (!d_stricmp( arg, "weapon_type" )) { weapon_type = static_cast(atoi(equal_ptr)); } else if (!d_stricmp( arg, "strength" )) { strength = i2f(atoi(equal_ptr)); } else if (!d_stricmp( arg, "mass" )) { mass = fl2f(atof(equal_ptr)); } else if (!d_stricmp( arg, "drag" )) { drag = fl2f(atof(equal_ptr)); } else if (!d_stricmp( arg, "contains_id" )) { contains_id = atoi(equal_ptr); } else if (!d_stricmp( arg, "contains_type" )) { contains_type = atoi(equal_ptr); } else if (!d_stricmp( arg, "contains_count" )) { contains_count = atoi(equal_ptr); } else if (!d_stricmp( arg, "contains_prob" )) { contains_prob = atoi(equal_ptr); } else if (!d_stricmp( arg, "cloak_type" )) { cloak_type = atoi(equal_ptr); } else if (!d_stricmp( arg, "attack_type" )) { attack_type = atoi(equal_ptr); } else if (!d_stricmp( arg, "boss" )) { boss_flag = atoi(equal_ptr); } else if (!d_stricmp( arg, "score_value" )) { score_value = atoi(equal_ptr); } else if (!d_stricmp( arg, "see_sound" )) { see_sound = atoi(equal_ptr); } else if (!d_stricmp( arg, "attack_sound" )) { attack_sound = atoi(equal_ptr); } else if (!d_stricmp( arg, "claw_sound" )) { claw_sound = atoi(equal_ptr); } else if (!d_stricmp( arg, "name" )) { #if DXX_USE_EDITOR auto &name = Robot_names[N_robot_types]; const auto len = strlen(equal_ptr); assert(len < name.size()); // Oops, name too long. memcpy(name.data(), &equal_ptr[1], len - 2); name[len - 2] = 0; #endif } else if (!d_stricmp( arg, "simple_model" )) { model_name[n_models] = equal_ptr; first_bitmap_num[n_models] = N_ObjBitmapPtrs; n_models++; } } else { // Must be a texture specification... load_polymodel_bitmap(skip, arg); } arg = strtok( NULL, space_tab ); } //clear out anim info range_for (auto &g, Robot_info[N_robot_types].anim_states) range_for (auto &s, g) s.n_joints = 0; //inialize to zero first_bitmap_num[n_models] = N_ObjBitmapPtrs; for (i=0;i F1_0 )) { Error( "In bitmaps.tbl, lighting value of %.2f is out of range 0..1.\n", f2fl(lighting)); } } else if (!d_stricmp( arg, "strength" )) { strength = fl2f(atof(equal_ptr)); } } else { // Must be a texture specification... load_polymodel_bitmap(skip, arg); } arg = strtok( NULL, space_tab ); } if ( model_name_dead ) n_normal_bitmaps = first_bitmap_num_dead-first_bitmap_num; else n_normal_bitmaps = N_ObjBitmapPtrs-first_bitmap_num; model_num = load_polygon_model(model_name,n_normal_bitmaps,first_bitmap_num,NULL); if (type == OL_CONTROL_CENTER) read_model_guns(model_name, Reactors[0]); if ( model_name_dead ) Dead_modelnums[model_num] = load_polygon_model(model_name_dead,N_ObjBitmapPtrs-first_bitmap_num_dead,first_bitmap_num_dead,NULL); else Dead_modelnums[model_num] = -1; if (type == -1) Error("No object type specfied for object in BITMAPS.TBL on line %d\n",linenum); ObjType[Num_total_object_types] = type; ObjId[Num_total_object_types] = model_num; ObjStrength[Num_total_object_types] = strength; Num_total_object_types++; if (type == OL_EXIT) { exit_modelnum = model_num; destroyed_exit_modelnum = Dead_modelnums[model_num]; } } void bm_read_player_ship(char *&arg, int skip) { char *model_name_dying=NULL; char *model_name[MAX_MODEL_VARIANTS]; int n_models=0,i; int first_bitmap_num[MAX_MODEL_VARIANTS]; char *equal_ptr; robot_info ri; int last_multi_bitmap_num=-1; // Process bitmaps bm_flag = BM_NONE; arg = strtok( NULL, space_tab ); Player_ship->mass = Player_ship->drag = 0; //stupid defaults Player_ship->expl_vclip_num = -1; while (arg!=NULL) { equal_ptr = strchr( arg, '=' ); if ( equal_ptr ) { *equal_ptr='\0'; equal_ptr++; // if we have john=cool, arg is 'john' and equal_ptr is 'cool' if (!d_stricmp( arg, "model" )) { Assert(n_models==0); model_name[0] = equal_ptr; first_bitmap_num[0] = N_ObjBitmapPtrs; n_models = 1; } else if (!d_stricmp( arg, "simple_model" )) { model_name[n_models] = equal_ptr; first_bitmap_num[n_models] = N_ObjBitmapPtrs; n_models++; if (First_multi_bitmap_num!=-1 && last_multi_bitmap_num==-1) last_multi_bitmap_num=N_ObjBitmapPtrs; } else if (!d_stricmp( arg, "mass" )) Player_ship->mass = fl2f(atof(equal_ptr)); else if (!d_stricmp( arg, "drag" )) Player_ship->drag = fl2f(atof(equal_ptr)); // else if (!d_stricmp( arg, "low_thrust" )) // Player_ship->low_thrust = fl2f(atof(equal_ptr)); else if (!d_stricmp( arg, "max_thrust" )) Player_ship->max_thrust = fl2f(atof(equal_ptr)); else if (!d_stricmp( arg, "reverse_thrust" )) Player_ship->reverse_thrust = fl2f(atof(equal_ptr)); else if (!d_stricmp( arg, "brakes" )) Player_ship->brakes = fl2f(atof(equal_ptr)); else if (!d_stricmp( arg, "wiggle" )) Player_ship->wiggle = fl2f(atof(equal_ptr)); else if (!d_stricmp( arg, "max_rotthrust" )) Player_ship->max_rotthrust = fl2f(atof(equal_ptr)); else if (!d_stricmp( arg, "dying_pof" )) model_name_dying = equal_ptr; else if (!d_stricmp( arg, "expl_vclip_num" )) Player_ship->expl_vclip_num=atoi(equal_ptr); } else if (!d_stricmp( arg, "multi_textures" )) { First_multi_bitmap_num = N_ObjBitmapPtrs; first_bitmap_num[n_models] = N_ObjBitmapPtrs; } else // Must be a texture specification... load_polymodel_bitmap(skip, arg); arg = strtok( NULL, space_tab ); } if (First_multi_bitmap_num!=-1 && last_multi_bitmap_num==-1) last_multi_bitmap_num=N_ObjBitmapPtrs; if (First_multi_bitmap_num==-1) first_bitmap_num[n_models] = N_ObjBitmapPtrs; Assert(last_multi_bitmap_num-First_multi_bitmap_num == (MAX_PLAYERS-1)*2); for (i=0;imodel_num = model_num; else Polygon_models[last_model_num].simpler_model = model_num+1; last_model_num = model_num; } if ( model_name_dying ) { Assert(n_models); Dying_modelnums[Player_ship->model_num] = load_polygon_model(model_name_dying,first_bitmap_num[1]-first_bitmap_num[0],first_bitmap_num[0],NULL); } Assert(ri.n_guns == N_PLAYER_GUNS); //calc player gun positions { polymodel *pm; robot_info *r; vms_vector pnt; int mn; //submodel number int gun_num; r = &ri; pm = &Polygon_models[Player_ship->model_num]; for (gun_num=0;gun_numn_guns;gun_num++) { pnt = r->gun_points[gun_num]; mn = r->gun_submodels[gun_num]; //instance up the tree for this gun while (mn != 0) { vm_vec_add2(pnt,pm->submodel_offsets[mn]); mn = pm->submodel_parents[mn]; } Player_ship->gun_points[gun_num] = pnt; } } } void bm_read_some_file(const std::string &dest_bm, char *&arg, int skip) { switch (bm_flag) { case BM_COCKPIT: { bitmap_index bitmap; bitmap = bm_load_sub(skip, arg); Assert( Num_cockpits < N_COCKPIT_BITMAPS ); cockpit_bitmap[Num_cockpits++] = bitmap; //bm_flag = BM_NONE; } break; case BM_GAUGES: bm_read_gauges(arg, skip); break; case BM_WEAPON: bm_read_weapon(arg, skip, 0); break; case BM_VCLIP: bm_read_vclip(arg, skip); break; case BM_ECLIP: bm_read_eclip(dest_bm, arg, skip); break; case BM_TEXTURES: { bitmap_index bitmap; bitmap = bm_load_sub(skip, arg); Assert(tmap_count < MAX_TEXTURES); tmap_count++; Textures[texture_count] = bitmap; set_texture_name( arg ); Assert(texture_count < MAX_TEXTURES); texture_count++; NumTextures = texture_count; } break; case BM_WCLIP: bm_read_wclip(arg, skip); break; default: break; } } // ------------------------------------------------------------------------------ // If unused_flag is set, then this is just a placeholder. Don't actually reference vclips or load bbms. void bm_read_weapon(char *&arg, int skip, int unused_flag) { int i,n; int n_models=0; char *equal_ptr; char *pof_file_inner=NULL; char *model_name[MAX_MODEL_VARIANTS]; int first_bitmap_num[MAX_MODEL_VARIANTS]; int lighted; //flag for whether is a texture is lighted Assert(N_weapon_types < MAX_WEAPON_TYPES); n = N_weapon_types; N_weapon_types++; if (unused_flag) { clear_to_end_of_line(arg); return; } if (skip) { clear_to_end_of_line(arg); return; } // Initialize weapon array Weapon_info[n].render_type = WEAPON_RENDER_NONE; // 0=laser, 1=blob, 2=object Weapon_info[n].bitmap.index = 0; Weapon_info[n].model_num = -1; Weapon_info[n].model_num_inner = -1; Weapon_info[n].blob_size = 0x1000; // size of blob Weapon_info[n].flash_vclip = -1; Weapon_info[n].flash_sound = SOUND_LASER_FIRED; Weapon_info[n].flash_size = 0; Weapon_info[n].robot_hit_vclip = vclip_none; Weapon_info[n].robot_hit_sound = sound_none; Weapon_info[n].wall_hit_vclip = vclip_none; Weapon_info[n].wall_hit_sound = sound_none; Weapon_info[n].impact_size = 0; for (i=0; iadd_flags(BM_FLAG_NO_LIGHTING); lighted = 1; //default for next bitmap is lighted } arg = strtok( NULL, space_tab ); } first_bitmap_num[n_models] = N_ObjBitmapPtrs; for (i=0;i &>(t.filename), t.flags, t.lighting, t.damage, t.eclip_num)); ASSERT_SERIAL_UDT_MESSAGE_SIZE(tmap_info, 26); #if 0 static void tmap_info_write(PHYSFS_File *fp, const tmap_info &ti) { PHYSFSX_serialize_write(fp, ti); } void bm_write_all(PHYSFS_File *fp) { PHYSFS_write( fp, &NumTextures, sizeof(int), 1); range_for (const bitmap_index &bi, Textures) PHYSFS_write( fp, &bi, sizeof(bi), 1); range_for (const tmap_info &ti, TmapInfo) tmap_info_write(fp, ti); PHYSFS_write( fp, Sounds, sizeof(ubyte), MAX_SOUNDS); PHYSFS_write( fp, AltSounds, sizeof(ubyte), MAX_SOUNDS); PHYSFS_write( fp, &Num_vclips, sizeof(int), 1); range_for (const vclip &vc, Vclip) vclip_write(fp, vc); PHYSFS_write( fp, &Num_effects, sizeof(int), 1); range_for (const eclip &ec, Effects) eclip_write(fp, ec); PHYSFS_write( fp, &Num_wall_anims, sizeof(int), 1); range_for (const auto &w, WallAnims) wclip_write(fp, w); PHYSFS_write( fp, &N_robot_types, sizeof(int), 1); PHYSFS_write( fp, Robot_info, sizeof(robot_info), MAX_ROBOT_TYPES); PHYSFS_write( fp, &N_robot_joints, sizeof(int), 1); range_for (auto &r, Robot_joints) jointpos_write(fp, r); PHYSFS_write( fp, &N_weapon_types, sizeof(int), 1); range_for (const auto &w, Weapon_info) weapon_info_write(fp, w); PHYSFS_write( fp, &N_powerup_types, sizeof(int), 1); range_for (const auto &p, Powerup_info) powerup_type_info_write(fp, p); PHYSFS_write( fp, &N_polygon_models, sizeof(int), 1); range_for (const auto &p, partial_const_range(Polygon_models, N_polygon_models)) polymodel_write(fp, p); range_for (const auto &p, partial_const_range(Polygon_models, N_polygon_models)) PHYSFS_write( fp, p.model_data, sizeof(ubyte), p.model_data_size); PHYSFS_write( fp, Gauges, sizeof(bitmap_index), MAX_GAUGE_BMS); PHYSFS_write( fp, Dying_modelnums, sizeof(int), MAX_POLYGON_MODELS); PHYSFS_write( fp, Dead_modelnums, sizeof(int), MAX_POLYGON_MODELS); PHYSFS_write( fp, ObjBitmaps, sizeof(bitmap_index), MAX_OBJ_BITMAPS); PHYSFS_write( fp, ObjBitmapPtrs, sizeof(ushort), MAX_OBJ_BITMAPS); PHYSFS_write( fp, &only_player_ship, sizeof(player_ship), 1); PHYSFS_write( fp, &Num_cockpits, sizeof(int), 1); PHYSFS_write( fp, cockpit_bitmap, sizeof(bitmap_index), N_COCKPIT_BITMAPS); PHYSFS_write( fp, Sounds, sizeof(ubyte), MAX_SOUNDS); PHYSFS_write( fp, AltSounds, sizeof(ubyte), MAX_SOUNDS); PHYSFS_write( fp, &Num_total_object_types, sizeof(int), 1); PHYSFS_write( fp, ObjType, sizeof(sbyte), MAX_OBJTYPE); PHYSFS_write( fp, ObjId, sizeof(sbyte), MAX_OBJTYPE); PHYSFS_write( fp, ObjStrength, sizeof(fix), MAX_OBJTYPE); PHYSFS_write( fp, &First_multi_bitmap_num, sizeof(int), 1); PHYSFS_write( fp, &Reactors[0].n_guns, sizeof(int), 1); PHYSFS_write( fp, Reactors[0].gun_points, sizeof(vms_vector), MAX_CONTROLCEN_GUNS); PHYSFS_write( fp, Reactors[0].gun_dirs, sizeof(vms_vector), MAX_CONTROLCEN_GUNS); PHYSFS_write( fp, &exit_modelnum, sizeof(int), 1); PHYSFS_write( fp, &destroyed_exit_modelnum, sizeof(int), 1); } #endif