#ifdef SYSTEM_WIN32 #pragma warning(disable:4996) #endif #include "cmdlib.h" #include "filelib.h" #include "messages.h" #include "hlassert.h" #include "log.h" #include "mathlib.h" #include "bspfile.h" #include "scriplib.h" #include "blockmem.h" #include //SILENCER //============================================================================= int g_max_map_miptex = DEFAULT_MAX_MAP_MIPTEX; int g_max_map_lightdata = DEFAULT_MAX_MAP_LIGHTDATA; int g_nummodels; dmodel_t g_dmodels[MAX_MAP_MODELS]; int g_dmodels_checksum; int g_visdatasize; byte g_dvisdata[MAX_MAP_VISIBILITY]; int g_dvisdata_checksum; int g_lightdatasize; byte* g_dlightdata; int g_dlightdata_checksum; int g_texdatasize; byte* g_dtexdata; // (dmiptexlump_t) int g_dtexdata_checksum; int g_entdatasize; char g_dentdata[MAX_MAP_ENTSTRING]; int g_dentdata_checksum; int g_numleafs; dleaf_t g_dleafs[MAX_MAP_LEAFS]; int g_dleafs_checksum; int g_numplanes; dplane_t g_dplanes[MAX_INTERNAL_MAP_PLANES]; int g_dplanes_checksum; int g_numvertexes; dvertex_t g_dvertexes[MAX_MAP_VERTS]; int g_dvertexes_checksum; int g_numnodes; dnode_t g_dnodes[MAX_MAP_NODES]; int g_dnodes_checksum; int g_numtexinfo; texinfo_t g_texinfo[MAX_MAP_TEXINFO]; int g_texinfo_checksum; int g_numfaces; dface_t g_dfaces[MAX_MAP_FACES]; int g_dfaces_checksum; int g_numclipnodes; dclipnode_t g_dclipnodes[MAX_MAP_CLIPNODES]; int g_dclipnodes_checksum; int g_numedges; dedge_t g_dedges[MAX_MAP_EDGES]; int g_dedges_checksum; int g_nummarksurfaces; unsigned short g_dmarksurfaces[MAX_MAP_MARKSURFACES]; int g_dmarksurfaces_checksum; int g_numsurfedges; int g_dsurfedges[MAX_MAP_SURFEDGES]; int g_dsurfedges_checksum; int g_numentities; entity_t g_entities[MAX_MAP_ENTITIES]; /* * =============== * FastChecksum * =============== */ static int FastChecksum(const void* const buffer, int bytes) { int checksum = 0; char* buf = (char*)buffer; while (bytes--) { checksum = rotl(checksum, 4) ^ (*buf); buf++; } return checksum; } /* * =============== * CompressVis * =============== */ int CompressVis(const byte* const src, const unsigned int src_length, byte* dest, unsigned int dest_length) { unsigned int j; byte* dest_p = dest; unsigned int current_length = 0; for (j = 0; j < src_length; j++) { current_length++; hlassume(current_length <= dest_length, assume_COMPRESSVIS_OVERFLOW); *dest_p = src[j]; dest_p++; if (src[j]) { continue; } unsigned char rep = 1; for (j++; j < src_length; j++) { if (src[j] || rep == 255) { break; } else { rep++; } } current_length++; hlassume(current_length <= dest_length, assume_COMPRESSVIS_OVERFLOW); *dest_p = rep; dest_p++; j--; } return dest_p - dest; } // ===================================================================================== // DecompressVis // // ===================================================================================== void DecompressVis(const byte* src, byte* const dest, const unsigned int dest_length) { unsigned int current_length = 0; int c; byte* out; int row; row = (g_numleafs + 7) >> 3; out = dest; do { if (*src) { current_length++; hlassume(current_length <= dest_length, assume_DECOMPRESSVIS_OVERFLOW); *out = *src; out++; src++; continue; } c = src[1]; src += 2; while (c) { current_length++; hlassume(current_length <= dest_length, assume_DECOMPRESSVIS_OVERFLOW); *out = 0; out++; c--; if (out - dest >= row) { return; } } } while (out - dest < row); } // // ===================================================================================== // // ===================================================================================== // SwapBSPFile // byte swaps all data in a bsp file // ===================================================================================== static void SwapBSPFile(const bool todisk) { int i, j, c; dmodel_t* d; dmiptexlump_t* mtl; // models for (i = 0; i < g_nummodels; i++) { d = &g_dmodels[i]; for (j = 0; j < MAX_MAP_HULLS; j++) { d->headnode[j] = LittleLong(d->headnode[j]); } d->visleafs = LittleLong(d->visleafs); d->firstface = LittleLong(d->firstface); d->numfaces = LittleLong(d->numfaces); for (j = 0; j < 3; j++) { d->mins[j] = LittleFloat(d->mins[j]); d->maxs[j] = LittleFloat(d->maxs[j]); d->origin[j] = LittleFloat(d->origin[j]); } } // // vertexes // for (i = 0; i < g_numvertexes; i++) { for (j = 0; j < 3; j++) { g_dvertexes[i].point[j] = LittleFloat(g_dvertexes[i].point[j]); } } // // planes // for (i = 0; i < g_numplanes; i++) { for (j = 0; j < 3; j++) { g_dplanes[i].normal[j] = LittleFloat(g_dplanes[i].normal[j]); } g_dplanes[i].dist = LittleFloat(g_dplanes[i].dist); g_dplanes[i].type = (planetypes)LittleLong(g_dplanes[i].type); } // // texinfos // for (i = 0; i < g_numtexinfo; i++) { for (j = 0; j < 8; j++) { g_texinfo[i].vecs[0][j] = LittleFloat(g_texinfo[i].vecs[0][j]); } g_texinfo[i].miptex = LittleLong(g_texinfo[i].miptex); g_texinfo[i].flags = LittleLong(g_texinfo[i].flags); } // // faces // for (i = 0; i < g_numfaces; i++) { g_dfaces[i].texinfo = LittleShort(g_dfaces[i].texinfo); g_dfaces[i].planenum = LittleShort(g_dfaces[i].planenum); g_dfaces[i].side = LittleShort(g_dfaces[i].side); g_dfaces[i].lightofs = LittleLong(g_dfaces[i].lightofs); g_dfaces[i].firstedge = LittleLong(g_dfaces[i].firstedge); g_dfaces[i].numedges = LittleShort(g_dfaces[i].numedges); } // // nodes // for (i = 0; i < g_numnodes; i++) { g_dnodes[i].planenum = LittleLong(g_dnodes[i].planenum); for (j = 0; j < 3; j++) { g_dnodes[i].mins[j] = LittleShort(g_dnodes[i].mins[j]); g_dnodes[i].maxs[j] = LittleShort(g_dnodes[i].maxs[j]); } g_dnodes[i].children[0] = LittleShort(g_dnodes[i].children[0]); g_dnodes[i].children[1] = LittleShort(g_dnodes[i].children[1]); g_dnodes[i].firstface = LittleShort(g_dnodes[i].firstface); g_dnodes[i].numfaces = LittleShort(g_dnodes[i].numfaces); } // // leafs // for (i = 0; i < g_numleafs; i++) { g_dleafs[i].contents = LittleLong(g_dleafs[i].contents); for (j = 0; j < 3; j++) { g_dleafs[i].mins[j] = LittleShort(g_dleafs[i].mins[j]); g_dleafs[i].maxs[j] = LittleShort(g_dleafs[i].maxs[j]); } g_dleafs[i].firstmarksurface = LittleShort(g_dleafs[i].firstmarksurface); g_dleafs[i].nummarksurfaces = LittleShort(g_dleafs[i].nummarksurfaces); g_dleafs[i].visofs = LittleLong(g_dleafs[i].visofs); } // // clipnodes // for (i = 0; i < g_numclipnodes; i++) { g_dclipnodes[i].planenum = LittleLong(g_dclipnodes[i].planenum); g_dclipnodes[i].children[0] = LittleShort(g_dclipnodes[i].children[0]); g_dclipnodes[i].children[1] = LittleShort(g_dclipnodes[i].children[1]); } // // miptex // if (g_texdatasize) { mtl = (dmiptexlump_t*)g_dtexdata; if (todisk) { c = mtl->nummiptex; } else { c = LittleLong(mtl->nummiptex); } mtl->nummiptex = LittleLong(mtl->nummiptex); for (i = 0; i < c; i++) { mtl->dataofs[i] = LittleLong(mtl->dataofs[i]); } } // // marksurfaces // for (i = 0; i < g_nummarksurfaces; i++) { g_dmarksurfaces[i] = LittleShort(g_dmarksurfaces[i]); } // // surfedges // for (i = 0; i < g_numsurfedges; i++) { g_dsurfedges[i] = LittleLong(g_dsurfedges[i]); } // // edges // for (i = 0; i < g_numedges; i++) { g_dedges[i].v[0] = LittleShort(g_dedges[i].v[0]); g_dedges[i].v[1] = LittleShort(g_dedges[i].v[1]); } } // ===================================================================================== // CopyLump // balh // ===================================================================================== static int CopyLump(int lump, void* dest, int size, const dheader_t* const header) { int length, ofs; length = header->lumps[lump].filelen; ofs = header->lumps[lump].fileofs; if (length % size) { Error("LoadBSPFile: odd lump size"); } //special handling for tex and lightdata to keep things from exploding - KGP if(lump == LUMP_TEXTURES && dest == (void*)g_dtexdata) { hlassume(g_max_map_miptex > length, assume_MAX_MAP_MIPTEX); } else if(lump == LUMP_LIGHTING && dest == (void*)g_dlightdata) { hlassume(g_max_map_lightdata > length, assume_MAX_MAP_LIGHTING); } memcpy(dest, (byte*) header + ofs, length); return length / size; } // ===================================================================================== // LoadBSPFile // balh // ===================================================================================== void LoadBSPFile(const char* const filename) { dheader_t* header; LoadFile(filename, (char**)&header); LoadBSPImage(header); } // ===================================================================================== // LoadBSPImage // balh // ===================================================================================== void LoadBSPImage(dheader_t* const header) { unsigned int i; // swap the header for (i = 0; i < sizeof(dheader_t) / 4; i++) { ((int*)header)[i] = LittleLong(((int*)header)[i]); } if (header->version != BSPVERSION) { Error("BSP is version %i, not %i", header->version, BSPVERSION); } g_nummodels = CopyLump(LUMP_MODELS, g_dmodels, sizeof(dmodel_t), header); g_numvertexes = CopyLump(LUMP_VERTEXES, g_dvertexes, sizeof(dvertex_t), header); g_numplanes = CopyLump(LUMP_PLANES, g_dplanes, sizeof(dplane_t), header); g_numleafs = CopyLump(LUMP_LEAFS, g_dleafs, sizeof(dleaf_t), header); g_numnodes = CopyLump(LUMP_NODES, g_dnodes, sizeof(dnode_t), header); g_numtexinfo = CopyLump(LUMP_TEXINFO, g_texinfo, sizeof(texinfo_t), header); g_numclipnodes = CopyLump(LUMP_CLIPNODES, g_dclipnodes, sizeof(dclipnode_t), header); g_numfaces = CopyLump(LUMP_FACES, g_dfaces, sizeof(dface_t), header); g_nummarksurfaces = CopyLump(LUMP_MARKSURFACES, g_dmarksurfaces, sizeof(g_dmarksurfaces[0]), header); g_numsurfedges = CopyLump(LUMP_SURFEDGES, g_dsurfedges, sizeof(g_dsurfedges[0]), header); g_numedges = CopyLump(LUMP_EDGES, g_dedges, sizeof(dedge_t), header); g_texdatasize = CopyLump(LUMP_TEXTURES, g_dtexdata, 1, header); g_visdatasize = CopyLump(LUMP_VISIBILITY, g_dvisdata, 1, header); g_lightdatasize = CopyLump(LUMP_LIGHTING, g_dlightdata, 1, header); g_entdatasize = CopyLump(LUMP_ENTITIES, g_dentdata, 1, header); Free(header); // everything has been copied out // // swap everything // SwapBSPFile(false); g_dmodels_checksum = FastChecksum(g_dmodels, g_nummodels * sizeof(g_dmodels[0])); g_dvertexes_checksum = FastChecksum(g_dvertexes, g_numvertexes * sizeof(g_dvertexes[0])); g_dplanes_checksum = FastChecksum(g_dplanes, g_numplanes * sizeof(g_dplanes[0])); g_dleafs_checksum = FastChecksum(g_dleafs, g_numleafs * sizeof(g_dleafs[0])); g_dnodes_checksum = FastChecksum(g_dnodes, g_numnodes * sizeof(g_dnodes[0])); g_texinfo_checksum = FastChecksum(g_texinfo, g_numtexinfo * sizeof(g_texinfo[0])); g_dclipnodes_checksum = FastChecksum(g_dclipnodes, g_numclipnodes * sizeof(g_dclipnodes[0])); g_dfaces_checksum = FastChecksum(g_dfaces, g_numfaces * sizeof(g_dfaces[0])); g_dmarksurfaces_checksum = FastChecksum(g_dmarksurfaces, g_nummarksurfaces * sizeof(g_dmarksurfaces[0])); g_dsurfedges_checksum = FastChecksum(g_dsurfedges, g_numsurfedges * sizeof(g_dsurfedges[0])); g_dedges_checksum = FastChecksum(g_dedges, g_numedges * sizeof(g_dedges[0])); g_dtexdata_checksum = FastChecksum(g_dtexdata, g_numedges * sizeof(g_dtexdata[0])); g_dvisdata_checksum = FastChecksum(g_dvisdata, g_visdatasize * sizeof(g_dvisdata[0])); g_dlightdata_checksum = FastChecksum(g_dlightdata, g_lightdatasize * sizeof(g_dlightdata[0])); g_dentdata_checksum = FastChecksum(g_dentdata, g_entdatasize * sizeof(g_dentdata[0])); } // // ===================================================================================== // // ===================================================================================== // AddLump // balh // ===================================================================================== static void AddLump(int lumpnum, void* data, int len, dheader_t* header, FILE* bspfile) { lump_t* lump =&header->lumps[lumpnum]; lump->fileofs = LittleLong(ftell(bspfile)); lump->filelen = LittleLong(len); SafeWrite(bspfile, data, (len + 3) & ~3); } // ===================================================================================== // WriteBSPFile // Swaps the bsp file in place, so it should not be referenced again // ===================================================================================== void WriteBSPFile(const char* const filename) { dheader_t outheader; dheader_t* header; FILE* bspfile; header = &outheader; memset(header, 0, sizeof(dheader_t)); SwapBSPFile(true); header->version = LittleLong(BSPVERSION); bspfile = SafeOpenWrite(filename); SafeWrite(bspfile, header, sizeof(dheader_t)); // overwritten later // LUMP TYPE DATA LENGTH HEADER BSPFILE AddLump(LUMP_PLANES, g_dplanes, g_numplanes * sizeof(dplane_t), header, bspfile); AddLump(LUMP_LEAFS, g_dleafs, g_numleafs * sizeof(dleaf_t), header, bspfile); AddLump(LUMP_VERTEXES, g_dvertexes, g_numvertexes * sizeof(dvertex_t), header, bspfile); AddLump(LUMP_NODES, g_dnodes, g_numnodes * sizeof(dnode_t), header, bspfile); AddLump(LUMP_TEXINFO, g_texinfo, g_numtexinfo * sizeof(texinfo_t), header, bspfile); AddLump(LUMP_FACES, g_dfaces, g_numfaces * sizeof(dface_t), header, bspfile); AddLump(LUMP_CLIPNODES, g_dclipnodes, g_numclipnodes * sizeof(dclipnode_t), header, bspfile); AddLump(LUMP_MARKSURFACES, g_dmarksurfaces, g_nummarksurfaces * sizeof(g_dmarksurfaces[0]), header, bspfile); AddLump(LUMP_SURFEDGES, g_dsurfedges, g_numsurfedges * sizeof(g_dsurfedges[0]), header, bspfile); AddLump(LUMP_EDGES, g_dedges, g_numedges * sizeof(dedge_t), header, bspfile); AddLump(LUMP_MODELS, g_dmodels, g_nummodels * sizeof(dmodel_t), header, bspfile); AddLump(LUMP_LIGHTING, g_dlightdata, g_lightdatasize, header, bspfile); AddLump(LUMP_VISIBILITY,g_dvisdata, g_visdatasize, header, bspfile); AddLump(LUMP_ENTITIES, g_dentdata, g_entdatasize, header, bspfile); AddLump(LUMP_TEXTURES, g_dtexdata, g_texdatasize, header, bspfile); fseek(bspfile, 0, SEEK_SET); SafeWrite(bspfile, header, sizeof(dheader_t)); fclose(bspfile); } // // ===================================================================================== // #define ENTRIES(a) (sizeof(a)/sizeof(*(a))) #define ENTRYSIZE(a) (sizeof(*(a))) // ===================================================================================== // ArrayUsage // blah // ===================================================================================== static int ArrayUsage(const char* const szItem, const int items, const int maxitems, const int itemsize) { float percentage = maxitems ? items * 100.0 / maxitems : 0.0; //SILENCER: Increased max spacing value from 7 to 9, since values have grown larger. Log("%-12s %9i/%-9i %9i/%-9i (%4.1f%%)\n", szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage); return items * itemsize; } // ===================================================================================== // GlobUsage // print out global ussage line in chart // ===================================================================================== static int GlobUsage(const char* const szItem, const int itemstorage, const int maxstorage) { float percentage = maxstorage ? itemstorage * 100.0 / maxstorage : 0.0; Log("%-12s [variable] %9i/%-9i (%4.1f%%)\n", szItem, itemstorage, maxstorage, percentage); return itemstorage; } // ===================================================================================== // PrintBSPFileSizes // Dumps info about current file // ===================================================================================== void PrintBSPFileSizes() { int numtextures = g_texdatasize ? ((dmiptexlump_t*)g_dtexdata)->nummiptex : 0; int totalmemory = 0; Log("\n"); Log("Object names Objects/Maxobjs Memory / Maxmem Fullness\n"); Log("------------ ------------------- ------------------- --------\n"); totalmemory += ArrayUsage("models", g_nummodels, ENTRIES(g_dmodels), ENTRYSIZE(g_dmodels)); totalmemory += ArrayUsage("planes", g_numplanes, MAX_MAP_PLANES, ENTRYSIZE(g_dplanes)); totalmemory += ArrayUsage("vertexes", g_numvertexes, ENTRIES(g_dvertexes), ENTRYSIZE(g_dvertexes)); totalmemory += ArrayUsage("nodes", g_numnodes, ENTRIES(g_dnodes), ENTRYSIZE(g_dnodes)); totalmemory += ArrayUsage("texinfos", g_numtexinfo, ENTRIES(g_texinfo), ENTRYSIZE(g_texinfo)); totalmemory += ArrayUsage("faces", g_numfaces, ENTRIES(g_dfaces), ENTRYSIZE(g_dfaces)); totalmemory += ArrayUsage("clipnodes", g_numclipnodes, 32767, ENTRYSIZE(g_dclipnodes)); totalmemory += ArrayUsage("leaves", g_numleafs, ENTRIES(g_dleafs), ENTRYSIZE(g_dleafs)); totalmemory += ArrayUsage("marksurfaces", g_nummarksurfaces, ENTRIES(g_dmarksurfaces), ENTRYSIZE(g_dmarksurfaces)); totalmemory += ArrayUsage("surfedges", g_numsurfedges, ENTRIES(g_dsurfedges), ENTRYSIZE(g_dsurfedges)); totalmemory += ArrayUsage("edges", g_numedges, ENTRIES(g_dedges), ENTRYSIZE(g_dedges)); totalmemory += GlobUsage("texdata", g_texdatasize, g_max_map_miptex); totalmemory += GlobUsage("lightdata", g_lightdatasize, g_max_map_lightdata); totalmemory += GlobUsage("visdata", g_visdatasize, sizeof(g_dvisdata)); totalmemory += GlobUsage("entdata", g_entdatasize, sizeof(g_dentdata)); Log("=> Total BSP file data space used: %d bytes.\n", totalmemory); //SILENCER --> if(!strcmp(g_Program, "hlbsp")) { //Only warn in HLBSP if(g_numleafs > MAX_MAP_LEAFS) { Log("\n"); Warning("Exceeded MAX_MAP_LEAFS. File write failure immediate.\n"); } else if(g_numleafs > 8192) { Log("\n"); Warning("Surpassed 8192 leafs, which is the old maximum.\n" " If you encounter problems when running your map,\n" " consider this the most likely cause. There exist no\n" " records about this ever causing problems, though.\n"); } } //<-- SILENCER Log("Textures referenced: %i\n\n", numtextures); } // ===================================================================================== // ParseEpair // entity key/value pairs // ===================================================================================== epair_t* ParseEpair() { epair_t* e; e = (epair_t*)Alloc(sizeof(epair_t)); if (strlen(g_token) >= MAX_KEY - 1) Error("ParseEpair: Key token too long (%i > MAX_KEY)", (int)strlen(g_token)); e->key = _strdup(g_token); GetToken(false); if (strlen(g_token) >= ZHLT3_MAX_VALUE - 1) Error("ParseEpar: Value token too long (%i > ZHLT3_MAX_VALUE)", (int)strlen(g_token)); e->value = _strdup(g_token); return e; } /* * ================ * ParseEntity * ================ */ #ifdef ZHLT_INFO_COMPILE_PARAMETERS // AJM: each tool should have its own version of GetParamsFromEnt which parseentity calls extern void GetParamsFromEnt(entity_t* mapent); #endif //PROTECTOR --> int brush_copy_num[MAX_MAP_ENTITIES]; int num_brush_copies = 0; void ParseBrushCopies() { if (num_brush_copies>0) { if(num_brush_copies>1) Log("\nFound %i phlt_copy_brush entities. Parsing...\n", num_brush_copies); else Log("\nFound 1 phlt_copy_brush entity. Parsing...\n"); for (int i = 0; i < num_brush_copies; i++) { Verbose(" Parsing phlt_copy_brush %i:\n",i); int v = brush_copy_num[i]; entity_t *copy_brush = &g_entities[v]; char targetname[MAX_KEY]; strcpy_s(targetname, ValueForKey(copy_brush, "phlt_cpm_target")); if (!strcmp(targetname,"")) { Log(" "); Warning("No copy target for phlt_copy_brush %i (%s).\n" " Using default. (models/chumtoad.mdl)",i,targetname); SetKeyValue(copy_brush, "model", "models/chumtoad.mdl"); } else { entity_t *target_ent = FindTargetEntity(targetname); if (!target_ent) { Log(" "); Warning("Could not find target %s for phlt_copy_brush %i.\n" " Using default. (models/chumtoad.mdl)",targetname,i); SetKeyValue(copy_brush, "model", "models/chumtoad.mdl"); } else { char model_num[MAX_KEY]; strcpy_s(model_num, ValueForKey(target_ent, "model")); if (!strcmp(model_num,"")) { Log(" "); Warning("No model found at entity %s for phlt_copy_brush %i.\n" " Using default. (models/chumtoad.mdl)",targetname,i); SetKeyValue(copy_brush, "model", "models/chumtoad.mdl"); } else { SetKeyValue(copy_brush, "model", model_num); } } } char classname[MAX_KEY]; strcpy_s(classname, ValueForKey(copy_brush, "phlt_cpm_class")); Verbose(" Parsed. (Copy target: %s)\n",targetname); if (!strcmp(classname,"")) { Log(" "); Warning("Created new func_illusionary as default, since no\n" " classname was given. However, classname should be given!"); SetKeyValue(copy_brush, "classname", "func_illusionary"); } else { Verbose(" Created new %s.\n",classname); SetKeyValue(copy_brush, "classname", classname); } RemoveKeyValue(copy_brush, "phlt_cpm_target"); RemoveKeyValue(copy_brush, "phlt_cpm_class"); } Log("Done.\n"); } } //<-- PROTECTOR //SILENCER --> /* // UNFINISHED! int countdown_num[MAX_MAP_ENTITIES]; int num_countdowns = 0; void UnfoldCountdownPackages() { if (num_countdowns > 0) { const int BASES = 16; const int TNAMES = 32; const int TARGETS = 16; if(num_countdowns > 1) { Log("\nFound %i shlt_countdown entities. Parsing...\n", num_countdowns); } else { Log("\nFound 1 shlt_countdown entity. Parsing...\n"); } for (int a = 0; a < num_countdowns; a++) { Verbose(" Parsing shlt_countdown %i.\n", a); int v = countdown_num[a]; entity_t* countdown = &g_entities[v]; unsigned long units[TNAMES]; char start_name[TNAMES][ZHLT3_MAX_VALUE]; char base_change_target[TARGETS][ZHLT3_MAX_VALUE]; char curkey[MAX_KEY]; unsigned long total_units = 1; bool redundant[TNAMES]; bool redundant_target[TARGETS]; bool min_need = false; // Read count_units# and start_name# from 1 to 32 for(int b = 0; b < max(TNAMES, TARGETS); b++) { if(b < TNAMES) { sprintf_s(curkey, "count_units%i", b + 1); units[b] = IntForKey(countdown, curkey); sprintf_s(curkey, "start_name%i", b + 1); strcpy_s(start_name[b], ValueForKey(countdown, curkey)); if(units[b] < 1 || strlen(start_name[b]) == 0) { redundant[b] = true; } else { redundant[b] = false; min_need = true; if(units[b] > total_units) total_units = units[b]; } } if(b < TARGETS) { sprintf_s(curkey, "base_change_target%i", b + 1); strcpy_s(base_change_target[b], ValueForKey(countdown, curkey)); if(strlen(base_change_target[b]) == 0) { redundant_target[b] = true; } else redundant_target[b] = false; } } if(!min_need) { Warning("Zero targetnames specified in shlt_countdown. Skipping."); continue; } char separator_char = ValueForKey(countdown, "separator_char")[0]; char dynamic_indicator[ZHLT3_MAX_VALUE]; strcpy_s(dynamic_indicator, ValueForKey(countdown, "dynamic_indicator")); // Get positions of separator chars in dynamic_indicator int separators = 0; unsigned int separator_pos[TNAMES-1]; unsigned int len = (int)strlen(dynamic_indicator); for(unsigned int c = 0; c < len; c++) { if(dynamic_indicator[c] == separator_char) { if(separators == TNAMES - 1) Error("More than %i unit bases/%i separator chars in shlt_countdown's time specs.", BASES, TNAMES - 1); separator_pos[separators] = c; separators++; } } if(separators % 2 == 0) Error("Even amount of separator chars (%i) in time specs do not match definition.", separators); unsigned long unit_max[BASES]; unsigned long unit_digits[BASES]; int unit_bases = 0; // Save the units' base sizes into unit_max[#] for(int d = 0; d < (separators+1) / 2; d++) { if(d) { if(separator_pos[2*d-1] + 1 == separator_pos[2*d]) Error("Directly consecutive separator-char-pair in shlt_countdown's time specs."); unsigned int e; for(e = separator_pos[2*d-1]; e < separator_pos[2*d]; e++) { if(dynamic_indicator[e + 1] != separator_char) curkey[e - separator_pos[2*d-1]] = dynamic_indicator[e + 1]; } curkey[e] = '\0'; unit_max[d] = atoi(curkey); } else { // For the biggest unit base, this can be "infinite", thus // not using what we set this to later in the code. Just // make sure that this is > 0, because of the check below. unit_max[0] = 1; } if(unit_max[d] < 1) Error("Unit base #%i size of shlt_countdown < 1.", d + 1); unit_digits[d] = (int)strlen(curkey); unit_bases += 1; } if(unit_bases < 1) Error("No unit bases on shlt_countdown."); int unit_start_pos[BASES]; int dynlen = (int)strlen(dynamic_indicator); // Remove the separator chars from dynamic_indicator // and maintain exact unit base positions. for(int f = 0; f < separators; f++) { int g = separator_pos[f] - f; for(int h = 0; h < dynlen - g; h++) { if(g + h + 1 == ZHLT3_MAX_VALUE) dynamic_indicator[g + h] = '\0'; else dynamic_indicator[g + h] = dynamic_indicator[g + h + 1]; } separator_pos[f] = g; dynlen--; } unit_start_pos[0] = separator_pos[0]; for(int f = 1; f < separators; f+=2) unit_start_pos[f] = separator_pos[f*2-1]; int interval_base = IntForKey(countdown, "interval_base"); if(interval_base < 1) { Error("Interval base of shlt_countdown < 1."); } else if(interval_base > unit_bases) { Error("Interval base of shlt_countdown > bases."); } char finish_target[ZHLT3_MAX_VALUE]; strcpy_s(finish_target, ValueForKey(countdown, "finish_target")); char finish_killtarget[ZHLT3_MAX_VALUE]; strcpy_s(finish_killtarget, ValueForKey(countdown, "finish_killtarget")); float delay = FloatForKey(countdown, "delay"); int activator_only = IntForKey(countdown, "activator_only"); if(activator_only < 0 || activator_only > 2) { Warning("Activator_only setting in shlt_countdown entity out of bounds. Setting to default. (0)"); activator_only = 0; } // Size in seconds of the biggest (left-hand) unit float biggest_unit_size = FloatForKey(countdown, "biggest_unit_size"); bool count_upwards = BoolForKey(countdown, "count_upwards"); char finish_message[ZHLT3_MAX_VALUE]; strcpy_s(finish_message, ValueForKey(countdown, "finish_message")); char finish_message_time[128]; strcpy_s(finish_message_time, ValueForKey(countdown, "finish_message_time")); if(atof(finish_message_time) < 0.0) { Warning("Finish message time < 0.0. Setting to 0.0."); strcpy_s(finish_message_time, "0.0"); } char stop_message[ZHLT3_MAX_VALUE]; strcpy_s(stop_message, ValueForKey(countdown, "stop_message")); char stop_message_time[128]; strcpy_s(stop_message_time, ValueForKey(countdown, "stop_message_time")); if(atof(stop_message_time) < 0.0) { Warning("Stop message time < 0.0. Setting to 0.0."); strcpy_s(stop_message_time, "0.0"); } // Read x1, x2, y1 and y2 and perform error checks char x1[5]; strcpy_s(x1, ValueForKey(countdown, "x1")); if(atof(x1) < 0.0 && atof(x1) != -1.0) { Warning("X1 < 0.0 but not -1. Setting to -1."); strcpy_s(x1, "-1"); } else if(atof(x1) > 1.0) { Warning("X1 > 1.0. Setting to 1.0."); strcpy_s(x1, "1.0"); } char x2[5]; strcpy_s(x2, ValueForKey(countdown, "x2")); if(atof(x2) < 0.0 && atof(x2) != -1.0) { Warning("X2 < 0.0 but not -1. Setting to -1."); strcpy_s(x2, "-1"); } else if(atof(x2) > 1.0) { Warning("X2 > 1.0. Setting to 1.0."); strcpy_s(x2, "1.0"); } if(atof(x1) == -1.0 && atof(x2) != -1.0) { Error("X1 is -1, while X2 is >= 0.0."); } else if(atof(x2) == -1.0 && atof(x1) != -1.0) { Error("X2 is -1, while X1 is >= 0.0."); } char y1[5]; strcpy_s(y1, ValueForKey(countdown, "y1")); if(atof(y1) < 0.0 && atof(y1) != -1.0) { Warning("Y1 < 0.0 but not -1. Setting to -1."); strcpy_s(y1, "-1"); } else if(atof(y1) > 1.0) { Warning("Y1 > 1.0. Setting to 1.0."); strcpy_s(y1, "1.0"); } char y2[5]; strcpy_s(y2, ValueForKey(countdown, "y2")); if(atof(y2) < 0.0 && atof(y2) != -1.0) { Warning("Y2 < 0.0 but not -1. Setting to -1."); strcpy_s(y2, "-1"); } else if(atof(y2) > 1.0) { Warning("Y2 > 1.0. Setting to 1.0."); strcpy_s(y2, "1.0"); } if(atof(y1) == -1.0 && atof(y2) != -1.0) { Error("Y1 is -1, while Y2 is >= 0.0."); } else if(atof(y2) == -1.0 && atof(y1) != -1.0) { Error("Y2 is -1, while Y1 is >= 0.0."); } // It will prove to be comfortable to have // these as both, string and float. float x1f = atof(x1); float x2f = atof(x2); float y1f = atof(y1); float y2f = atof(y2); float move_time = FloatForKey(countdown, "move_time"); float move_delay = FloatForKey(countdown, "move_delay"); float move_rdelay = FloatForKey(countdown, "move_rdelay"); int countdown_color_from[3]; GetIntVectorForKey(countdown, "countdown_color_from", countdown_color_from); int countdown_color_to[3]; GetIntVectorForKey(countdown, "countdown_color_to", countdown_color_to); int finish_color[3]; GetIntVectorForKey(countdown, "finish_color", finish_color); int stop_color[3]; GetIntVectorForKey(countdown, "stop_color", stop_color); int channel = IntForKey(countdown, "channel"); if(channel < 1) { Warning("Channel number < 1. Setting to 1."); channel = 1; } else if(channel > 4) { Warning("Channel number > 4. Setting to 4."); channel = 4; } unsigned long flash_amount = IntForKey(countdown, "flash_amount"); unsigned long flash_amount2 = IntForKey(countdown, "flash_amount2"); unsigned long flash_interval = IntForKey(countdown, "flash_interval"); unsigned long flash_interval_base = IntForKey(countdown, "flash_interval_base"); int flash_color[3]; GetIntVectorForKey(countdown, "flash_color", flash_color); int flash_relativity = IntForKey(countdown, "flash_relativity"); if(flash_relativity < 1) { Warning("Flash relativity option < 1. Setting to 1."); flash_relativity = 1; } else if(flash_relativity > 3) { Warning("Flash relativity option > 3. Setting to 1."); flash_relativity = 1; } bool stoptrigger_handling = BoolForKey(countdown, "stoptrigger_handling"); float max_refresh_delay = FloatForKey(countdown, "max_refresh_delay"); float min_refresh_delay = FloatForKey(countdown, "min_refresh_delay"); float min_refresh_random = FloatForKey(countdown, "min_refresh_random"); // We are going to pile all the new entities // in one spot, so read the origin as well. int origin[3]; GetIntVectorForKey(countdown, "origin", origin); // We don't need you anymore! MarkEntityAsRedundant(countdown); // <- Done with the hacky reading and checking relevant keyvalues from entity // Do the actual work -> // Get the length in seconds one // unit of every base has. long double unit_time[BASES]; for(int i = 0; i < unit_bases; i++) { unit_time[i] = biggest_unit_size; for(int j = 0; j < i; j++) { unit_time[i] /= unit_max[j]; } } // Shift unit amounts over bases depending // on which is the interval base to match // scale of smallest (right-hand) base. interval_base = unit_bases - interval_base; flash_interval_base = unit_bases - flash_interval_base; for(int j = 0; j < unit_bases; j++) { if(!j) { for(int i = interval_base + 1; i < unit_bases; i++) { total_units *= unit_max[i]; } } if(redundant[j]) continue; for(int i = interval_base + 1; i < unit_bases; i++) { units[j] *= unit_max[i]; } } // Calculate start values for the bases unsigned long base_cur_value[BASES]; if(!count_upwards) { if(unit_bases == 1) { base_cur_value[0] = total_units; } else { long double factor = 1.0; for(int j = 2; j < unit_bases; j++) { factor *= unit_max[j]; } long double this_units = (total_units / factor); unsigned long this_unitsf = (unsigned long)(this_units); base_cur_value[1] = this_unitsf % unit_max[1]; base_cur_value[0] = (unsigned long)(this_units / unit_max[1]); long double multiply = this_units - this_unitsf; int i = 1; while(i < unit_bases - 1) { i++; if(i < unit_bases - 1) { base_cur_value[i] = (unsigned long)(multiply * unit_max[i]); multiply = multiply * unit_max[i] - base_cur_value[i]; } else { base_cur_value[i] = ceil(multiply * unit_max[i]); } } } } Log("Highest start time: "); for(int i = 0; i < unit_bases; i++) { if(!i) { Log("%i", base_cur_value[i]); } else { char cp[ZHLT3_MAX_VALUE]; itoa(unit_max[i] - 1, cp, 10); int len = (int)(strlen(cp)); Log("%0*i", len, base_cur_value[i]); } if(i != unit_bases - 1) { Log(":"); } } Log("\n"); int mm_load = 0; unsigned long cdir = 0; if(!count_upwards) cdir = total_units; unsigned long total_units_cp = total_units; long double timeline = 0.0; bool processed = false; unsigned long offset = 0; if(flash_relativity == 3) { offset = total_units_cp % flash_interval; } // Calculate which game_texts will require a version // for the message flash effect (different color) bool* req_flashversion = (bool*) malloc(sizeof(bool) * total_units_cp); for(unsigned long i = total_units_cp; i > 0; i--) { if(((i + offset) % (flash_interval * unit_max[unit_bases - flash_interval_base])) == 0) { for(unsigned long j = 0; j < flash_amount2; j++) { if(flash_amount > 1) { // This is wrong and needs rewriting: for(unsigned long k = 0; k < flash_amount; k++) { if((req_flashversion + total_units_cp - i + (j * unit_max[unit_bases - flash_interval_base])) >= 0) { *(req_flashversion + total_units_cp - i + (j * unit_max[unit_bases - flash_interval_base])) = true; } } } else { for(unsigned long k = 0; k < flash_amount; k++) { if((req_flashversion + total_units_cp - i + (j * unit_max[unit_bases - flash_interval_base]) + (k * unit_max[unit_bases - flash_interval_base + 1])) >= 0) { *(req_flashversion + total_units_cp - i + (j * unit_max[unit_bases - flash_interval_base]) + (k * unit_max[unit_bases - flash_interval_base + 1])) = true; } } } } } else { *(req_flashversion + total_units_cp - i) = false; } } if(flash_relativity == 1) { *(req_flashversion + 0) = true; } // Determine and emit the game_texts which incarnate the // actual countdown. For this I use a counter for the // time (long double timeline) and a counter for the // smallest unit base (unsigned long total_units_cp) // simultaneously/parallel. while(!processed) { // Format cur_msg to be the current message to // be output by the current game_text entity. char cur_msg[ZHLT3_MAX_VALUE]; strcpy_s(cur_msg, dynamic_indicator); int shifter = 0; for(int j = 0; j < unit_bases; j++) { char cur_val[ZHLT3_MAX_VALUE]; if(j) { sprintf_s(cur_val, "%0*i", unit_digits, base_cur_value[j]); } else { itoa(base_cur_value[0], cur_val, 10); unit_digits[0] = (int)strlen(cur_val); } itoa(base_cur_value[j], cur_val, 10); strins(cur_msg, ZHLT3_MAX_VALUE - 1, cur_val, separator_pos[j] + shifter, false, ' '); shifter += unit_digits[j]; } // Calculate screen position of message float newxf = x1f; float newyf = y1f; if(timeline >= move_delay) { if(timeline < (move_delay + move_time)) { if(x1f >= 0.0) { newxf = x1f + ((x2f - x1f) * ((timeline - move_delay) / move_time)); } if(y1f >= 0.0) { newyf = y1f + ((y2f - y1f) * ((timeline - move_delay) / move_time)); } } else { newxf = x2f; newyf = y2f; } timeline += move_rdelay; } // Calculate color blending int countdown_color_new[3]; for(int cchannel = 0; cchannel < 3; cchannel++) { countdown_color_new[cchannel] = countdown_color_from[cchannel] + ((countdown_color_from[cchannel] - countdown_color_to[cchannel]) * (1.0 / (total_units_cp / total_units))); } entity_t* game_text = AddEntity("game_text", origin); // Count down by one unit - set boolean processed to // true when trying to lower base_cur_value[0] to -1. if(timeline / unit_time[unit_bases - 1] >= total_units_cp - total_units + 1) { for(int j = unit_bases - 1; j >= 0; j--) { if(base_cur_value[j] > 0) { base_cur_value[j]--; break; } else { if(!j) { processed = true; } else { base_cur_value[j] = unit_max[j] - 1; } } } } } Free(req_flashversion); } Log("Done.\n"); } } */ //Inserts one string into another for a length of maxNewLen chars at position //insAtPos. If overwrite is true, inserted String will overwrite source string //from position insAtPos onwards. void strins(char* srcDestStr, const int maxNewLen, const char* insStr, const int insAtPos, const bool overwrite, const char fillGap) { int srcDestCurPos; int insCurPos = 0; int srcDestLen = srcDestCurPos = (int)strlen(srcDestStr); int insLen = (int)strlen(insStr); int fetch = 0; char* srcDestStrBackup = (char*) malloc(sizeof(char) * (srcDestLen + 1)); while(srcDestCurPos < maxNewLen) { if(srcDestLen < insAtPos) { *(srcDestStr + srcDestCurPos) = fillGap; srcDestCurPos++; } else break; } while(srcDestCurPos < maxNewLen) { if(insCurPos < insLen) { *(srcDestStr + srcDestCurPos) = *(insStr + insCurPos); srcDestCurPos++; insCurPos++; fetch++; } } if(overwrite) { Free(srcDestStrBackup); *(srcDestStr + srcDestCurPos) = '\0'; return; } while(srcDestCurPos < maxNewLen) { if(srcDestCurPos < srcDestLen + fetch) { *(srcDestStr + srcDestCurPos) = *(srcDestStrBackup + srcDestCurPos - fetch); srcDestCurPos++; } else break; } Free(srcDestStrBackup); *(srcDestStr + srcDestCurPos) = '\0'; } int wildcard_num[MAX_MAP_ENTITIES]; int num_wildcards = 0; void DoWildcards() { if (num_wildcards > 0) { if(num_wildcards > 1) { Log("\nFound %i shlt_entity_wildcard entities. Parsing...\n", num_wildcards); Warning("One shlt_entity_wildcard entity should be sufficient, but found %i.", num_wildcards); } else { Log("\nFound 1 shlt_entity_wildcard entity. Parsing...\n"); } for (int a = 0; a < num_wildcards; a++) { Verbose(" Parsing shlt_entity_wildcard %i:\n", a); int v = wildcard_num[a]; entity_t* wildcard = &g_entities[v]; int amount = IntForKey(wildcard, "wildcard_amount"); if(amount <= 0) { Warning("Read too low value (%i) from Wildcard amount - Removing entity.", amount); wildcard->epairs = 0; } else { float delay = FloatForKey(wildcard, "delay"); char killtarget[ZHLT3_MAX_VALUE]; strcpy_s(killtarget, ValueForKey(wildcard, "killtarget")); if(delay < 0.5) { Warning("Delay of %.6f seconds on shlt_entity_wildcard \"%s\" might be too low!", delay, killtarget); } Verbose("Emitting %i additional info_target entities", amount - 1); SetKeyValue(wildcard, "classname", "trigger_auto"); SetKeyValue(wildcard, "spawnflags", "1"); RemoveKeyValue(wildcard, "wildcard_amount"); int origin[3]; GetIntVectorForKey(wildcard, "origin", origin); for(int i = 1; i < amount; i++) { entity_t* target = AddEntity("info_target", origin); SetKeyValue(target, "targetname", killtarget); } } } } } //<-- SILENCER bool ParseEntity(int * lNumAddress, int * lListAddress) { epair_t* e; entity_t* mapent; /* All entities parsed? */ if (!GetToken(true)) { //UnfoldCountdownPackages(); DoWildcards(); ParseBrushCopies(); /* Changes to entity work? */ if(num_brush_copies > 0 /* || num_countdowns > 0 */ || num_wildcards > 0) { UnparseEntities(); } return false; } if (strcmp(g_token, "{")) { Error("ParseEntity: { not found"); } if (g_numentities == MAX_MAP_ENTITIES) { Error("g_numentities == MAX_MAP_ENTITIES"); } mapent = &g_entities[g_numentities]; g_numentities++; while (1) { if (!GetToken(true)) { Error("ParseEntity: EOF without closing brace"); } if (!strcmp(g_token, "}")) { break; } e = ParseEpair(); e->next = mapent->epairs; mapent->epairs = e; } #ifdef ZHLT_INFO_COMPILE_PARAMETERS // AJM if (!strcmp(ValueForKey(mapent, "classname"), "info_compile_parameters")) { Log("Map entity info_compile_parameters detected, using compile settings\n"); GetParamsFromEnt(mapent); } #endif // Since the classnames of the following entity types are changed // when detected, the following if-checks can only return true in // the ParseEntities()-call from hlbsp, since hlbsp is the first // tool to call ParseEntities(). //PROTECTOR --> if (!strcmp(ValueForKey(mapent, "classname"), "phlt_copy_brush")) { brush_copy_num[num_brush_copies] = g_numentities - 1; num_brush_copies++; } //SILENCER --> /*else if(!strcmp(ValueForKey(mapent, "classname"), "shlt_countdown")) { countdown_num[num_countdowns] = g_numentities - 1; num_countdowns++; }*/ else if(!strcmp(ValueForKey(mapent, "classname"), "shlt_entity_wildcard")) { wildcard_num[num_wildcards] = g_numentities - 1; num_wildcards++; } //<-- SILENCER //<-- PROTECTOR //SILENCER --> if(!strcmp(g_Program, "hlrad")) { if(!strcmp(ValueForKey(mapent, "classname"), "light") || !strcmp(ValueForKey(mapent, "classname"), "light_spot")) { if(!ValueForKey(mapent, "targetname")[0]) { *(lListAddress + *(lNumAddress)) = g_numentities - 1; ++*(lNumAddress); } } } //<--SILENCER return true; } // ===================================================================================== // ParseEntities // Parses the dentdata string into entities // ===================================================================================== void ParseEntities(int * lNumAddress, int * lListAddress) { g_numentities = 0; ParseFromMemory(g_dentdata, g_entdatasize); if(!strcmp(g_Program, "hlrad")) { *(lNumAddress) = 0; } while (ParseEntity(lNumAddress, lListAddress)) { } } // ===================================================================================== // UnparseEntities // Generates the dentdata string from all the entities // ===================================================================================== void UnparseEntities() { char* buf; char* end; epair_t* ep; char line[MAXTOKEN]; int i; buf = g_dentdata; end = buf; *end = 0; for (i = 0; i < g_numentities; i++) { ep = g_entities[i].epairs; if (!ep) { continue; // ent got removed } strcat(end, "{\n"); end += 2; for (ep = g_entities[i].epairs; ep; ep = ep->next) { sprintf(line, "\"%s\" \"%s\"\n", ep->key, ep->value); strcat(end, line); end += strlen(line); } strcat(end, "}\n"); end += 2; if (end > buf + MAX_MAP_ENTSTRING) { Error("Entity text too long."); } } g_entdatasize = end - buf + 1; } // SILENCER --> // ===================================================================================== // MarkEntityAsRedundant // strips epairs from entity to provide for its removal in UnparseEntities() // ===================================================================================== void MarkEntityAsRedundant(entity_t* ent) { ent->epairs = 0; // Do not lower g_numentities, since all we are doing with // this, is to take some entity anywhere out of the chain of // entities. } // ===================================================================================== // AddEntity // adds an Entity at given origin // ===================================================================================== entity_t *AddEntity(const char* const classname, int neworigin[3]) { if (g_numentities == MAX_MAP_ENTITIES) { Error("g_numentities == MAX_MAP_ENTITIES"); } char value[ZHLT3_MAX_VALUE]; /* Origin-vector should be in ints - all entities ** ** in *.map have int[3]-based origin */ sprintf_s(value, "%i %i %i", neworigin[0], neworigin[1], neworigin[2]); SetKeyValue(&g_entities[g_numentities], "classname", classname); SetKeyValue(&g_entities[g_numentities], "origin", value); g_numentities++; return &g_entities[g_numentities - 1]; } // ===================================================================================== // RemoveKeyValue // removes a keyvalue from an entity // ===================================================================================== void RemoveKeyValue(entity_t* ent, const char* const key) { epair_t* ep; epair_t* prevep; for(ep = ent->epairs; ep; ep = ep->next) { if(!strcmp(ep->key, key)) { if(!prevep) ent->epairs = ep->next; else prevep->next = ep->next; Free(ep); return; } prevep = ep; } } // <-- SILENCER // ===================================================================================== // SetKeyValue // creates/changes a keyvalue // ===================================================================================== void SetKeyValue(entity_t* ent, const char* const key, const char* const value) { epair_t* ep; for (ep = ent->epairs; ep; ep = ep->next) { if (!strcmp(ep->key, key)) { Free(ep->value); ep->value = strdup(value); return; } } // If the key does not exist, create it and apply value ep = (epair_t*)Alloc(sizeof(*ep)); ep->next = ent->epairs; ent->epairs = ep; ep->key = strdup(key); ep->value = strdup(value); } // ===================================================================================== // ValueForKey // returns the value for a passed entity and key // ===================================================================================== const char* ValueForKey(const entity_t* const ent, const char* const key) { epair_t* ep; for (ep = ent->epairs; ep; ep = ep->next) { if (!strcmp(ep->key, key)) { return ep->value; } } return ""; } // ===================================================================================== // IntForKey // ===================================================================================== int IntForKey(const entity_t* const ent, const char* const key) { return atoi(ValueForKey(ent, key)); } // ===================================================================================== // FloatForKey // ===================================================================================== vec_t FloatForKey(const entity_t* const ent, const char* const key) { return atof(ValueForKey(ent, key)); } // SILENCER --> // ===================================================================================== // BoolForKey // ===================================================================================== bool BoolForKey(const entity_t* const ent, const char* const key) { return atoi(ValueForKey(ent, key)) != 0; } // <-- SILENCER // ===================================================================================== // GetVectorForKey // returns value for key in vec[0-2] // ===================================================================================== void GetVectorForKey(const entity_t* const ent, const char* const key, vec3_t vec) { const char* k; double v1, v2, v3; k = ValueForKey(ent, key); // scanf into doubles, then assign, so it is vec_t size independent v1 = v2 = v3 = 0; sscanf(k, "%lf %lf %lf", &v1, &v2, &v3); vec[0] = v1; vec[1] = v2; vec[2] = v3; } void GetIntVectorForKey(const entity_t* const ent, const char* const key, int vec[3]) { const char* k; double v1, v2, v3; k = ValueForKey(ent, key); // scanf into doubles, then assign, so it is vec_t size independent v1 = v2 = v3 = 0; sscanf(k, "%lf %lf %lf", &v1, &v2, &v3); vec[0] = (int)v1; vec[1] = (int)v2; vec[2] = (int)v3; } // ===================================================================================== // FindTargetEntity // // ===================================================================================== entity_t *FindTargetEntity(const char* const target) { int i; const char* n; for (i = 0; i < g_numentities; i++) { n = ValueForKey(&g_entities[i], "targetname"); if (!strcmp(n, target)) { return &g_entities[i]; } } return NULL; } void dtexdata_init() { g_dtexdata = (byte*)AllocBlock(g_max_map_miptex); hlassume(g_dtexdata != NULL, assume_NoMemory); g_dlightdata = (byte*)AllocBlock(g_max_map_lightdata); hlassume(g_dlightdata != NULL, assume_NoMemory); } void CDECL dtexdata_free() { FreeBlock(g_dtexdata); g_dtexdata = NULL; FreeBlock(g_dlightdata); g_dlightdata = NULL; } // ===================================================================================== // GetTextureByNumber // Touchy function, can fail with a page fault if all the data isnt kosher // (i.e. map was compiled with missing textures) // ===================================================================================== char* GetTextureByNumber(int texturenumber) { texinfo_t* info; miptex_t* miptex; int ofs; info = &g_texinfo[texturenumber]; ofs = ((dmiptexlump_t*)g_dtexdata)->dataofs[info->miptex]; miptex = (miptex_t*)(&g_dtexdata[ofs]); return miptex->name; } // ===================================================================================== // EntityForModel // returns entity addy for given modelnum // ===================================================================================== entity_t* EntityForModel(const int modnum) { int i; const char* s; char name[16]; sprintf(name, "*%i", modnum); // search the entities for one using modnum for (i = 0; i < g_numentities; i++) { s = ValueForKey(&g_entities[i], "model"); if (!strcmp(s, name)) { return &g_entities[i]; } } return &g_entities[0]; }