1 // HUGE SET - stored in a string
2 string HugeSetOfIntegers_empty()
6 float HugeSetOfIntegers_get(string pArr, float i)
8 return stof(substring(pArr, i * 4, 4));
10 float HugeSetOfIntegers_length(string pArr)
12 return strlen(pArr) / 4;
14 string HugeSetOfIntegers_concat(string a1, string a2)
16 return strcat(a1, a2);
18 string HugeSetOfIntegers_insert(string a1, float n, string a2)
19 // special concat function to build up large lists in less time by binary concatenation
22 s = strcat(" ", ftos(n));
23 return strcat(a1, substring(s, strlen(s) - 4, 4), a2);
26 // generic string stuff
27 float startsWith(string haystack, string needle)
29 return substring(haystack, 0, strlen(needle)) == needle;
31 string extractRestOfLine(string haystack, string needle)
33 if(startsWith(haystack, needle))
34 return substring(haystack, strlen(needle), strlen(haystack) - strlen(needle));
38 // GLOB HANDLING (for all BSP files)
39 float _MapInfo_globopen;
40 float _MapInfo_globcount;
41 float _MapInfo_globhandle;
42 string _MapInfo_GlobItem(float i)
45 s = search_getfilename(_MapInfo_globhandle, i);
46 return substring(s, 5, strlen(s) - 9); // without maps/ and .bsp
49 void MapInfo_Enumerate()
52 search_end(_MapInfo_globhandle);
53 _MapInfo_globhandle = search_begin("maps/*.bsp", TRUE, TRUE);
54 _MapInfo_globcount = search_getsize(_MapInfo_globhandle);
55 _MapInfo_globopen = 1;
58 // filter the info by game type mask (updates MapInfo_count)
59 string _MapInfo_filtered;
60 string MapInfo_FilterGametype_Recursive(float pGametype, float pBegin, float pEnd)
66 return HugeSetOfIntegers_empty();
68 m = floor((pBegin + pEnd) / 2);
70 l = MapInfo_FilterGametype_Recursive(pGametype, pBegin, m);
72 return string_null; // BAIL OUT
73 if(MapInfo_Get_ByName(_MapInfo_GlobItem(m), 1) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame.
74 return string_null; // BAIL OUT
75 r = MapInfo_FilterGametype_Recursive(pGametype, m + 1, pEnd);
77 return string_null; // BAIL OUT
79 if(MapInfo_Map_supportedGametypes & pGametype)
80 return HugeSetOfIntegers_insert(l, m, r);
82 return HugeSetOfIntegers_concat(l, r);
84 float MapInfo_FilterGametype(float gametype)
87 strunzone(_MapInfo_filtered);
88 _MapInfo_filtered = MapInfo_FilterGametype_Recursive(gametype, 0, _MapInfo_globcount);
89 if(!_MapInfo_filtered)
91 dprint("Autogenerated a .mapinfo, bailing out to avoid loop counter\n");
94 _MapInfo_filtered = strzone(_MapInfo_filtered);
95 MapInfo_count = HugeSetOfIntegers_length(_MapInfo_filtered);
96 dprint("Filter ", ftos(gametype), " results in ", _MapInfo_filtered, "\n");
100 // load info about the i-th map into the MapInfo_Map_* globals
101 float MapInfo_Get_ByID(float i); // 1 on success, 0 on failure
103 float _MapInfo_Generate(string pFilename)
109 float inWorldspawn, l;
111 float spawns, diameter;
112 vector mapMins, mapMaxs;
114 fn = strcat("maps/", pFilename, ".ent");
115 fh = fopen(fn, FILE_READ);
118 fn = strcat("maps/", pFilename, ".bsp");
119 fh = fopen(fn, FILE_READ);
123 dprint("Analyzing ", fn, " to generate initial mapinfo\n");
125 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH; // DM always works
126 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH; // Rune always works
127 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS; // LMS always works
133 if not((s = fgets(fh)))
135 if(inWorldspawn == 1)
136 if(startsWith(s, "}"))
140 if(startsWith(s, "\"classname\" \"worldspawn\""))
142 else if((v = extractRestOfLine(s, "\"message\" \"")))
144 for(l = strlen(v) - 1; l > 0; --l)
145 if(substring(v, l, 1) == "\"")
147 MapInfo_Map_title = substring(v, 0, l);
152 if((v = extractRestOfLine(s, "\"origin\" \"")))
154 for(l = strlen(v) - 1; l > 0; --l)
155 if(substring(v, l, 1) == "\"")
157 o = stov(strcat("'", substring(v, 0, l), "'"));
158 mapMins_x = min(mapMins_x, o_x);
159 mapMins_y = min(mapMins_y, o_y);
160 mapMins_z = min(mapMins_z, o_z);
161 mapMaxs_x = max(mapMaxs_x, o_x);
162 mapMaxs_y = max(mapMaxs_y, o_y);
163 mapMaxs_z = max(mapMaxs_z, o_z);
165 else if((v = extractRestOfLine(s, "\"classname\" \"")))
167 if(startsWith(v, "dom_controlpoint\""))
168 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;
169 else if(startsWith(v, "item_flag_team2\""))
170 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
171 else if(startsWith(v, "runematch_spawn_point\""))
172 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH;
173 else if(startsWith(v, "target_assault_roundend\""))
174 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
175 else if(startsWith(v, "onslaught_generator\""))
176 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
177 else if(startsWith(v, "info_player_team1\""))
179 else if(startsWith(v, "info_player_team2\""))
181 else if(startsWith(v, "info_player_deathmatch\""))
183 else if(startsWith(v, "info_player_start\""))
190 print(strcat(fn, " ended still in worldspawn, BUG"));
193 diameter = vlen(mapMaxs - mapMins);
194 if(spawns >= 8 && diameter > 2048)
195 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
197 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ARENA;
198 if(spawns >= 16 && diameter > 4096)
199 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;
201 dprint(fn, ": types = ", ftos(MapInfo_Map_supportedGametypes), " spawns ", ftos(spawns), " diameter ", ftos(diameter), "\n");
205 // load info about a map by name into the MapInfo_Map_* globals
206 float MapInfo_Get_ByName(string pFilename, float pAllowGenerate)
212 // default all generic fields so they have "good" values in case something fails
213 MapInfo_Map_title = "Untitled1";
214 MapInfo_Map_description = "Bleh.";
215 MapInfo_Map_supportedGametypes = 0;
217 fn = strcat("maps/", pFilename, ".mapinfo");
218 fh = fopen(fn, FILE_READ);
223 if(!_MapInfo_Generate(pFilename))
225 fh = fopen(fn, FILE_WRITE);
226 fputs(fh, strcat("title ", MapInfo_Map_title, "\n"));
227 fputs(fh, strcat("description ", MapInfo_Map_description, "\n"));
228 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH) fputs(fh, "type dm 30 20\n");
229 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH) fputs(fh, "type tdm 50 20 2\n"); // TODO count tdm_team entities
230 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DOMINATION) fputs(fh, "type dom 200 20 2\n"); // TODO count tdm_team entities
231 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CTF) fputs(fh, "type ctf 300 20\n");
232 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RUNEMATCH) fputs(fh, "type rune 200 20\n");
233 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_LMS) fputs(fh, "type lms 9 20\n");
234 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ARENA) fputs(fh, "type arena 10 20\n");
235 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_KEYHUNT) fputs(fh, "type kh 1000 20\n");
236 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ASSAULT) fputs(fh, "type as 20\n");
237 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ONSLAUGHT) fputs(fh, "type ons 20\n");
243 if not((s = fgets(fh)))
248 MapInfo_Map_title = substring(s, 6, strlen(s) - 6); // without "title"
249 else if(t == "description")
250 MapInfo_Map_description = substring(s, 12, strlen(s) - 12);
254 if (t == "dm") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH;
255 else if(t == "tdm") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
256 else if(t == "dom") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;
257 else if(t == "ctf") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
258 else if(t == "rune") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH;
259 else if(t == "lms") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS;
260 else if(t == "arena") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ARENA;
261 else if(t == "kh") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;
262 else if(t == "as") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
263 else if(t == "ons") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
265 dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
268 dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
271 if(MapInfo_Map_supportedGametypes != 0)
273 dprint("Map ", pFilename, " supports no game types, ignored\n");