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)
86 _MapInfo_filtered = MapInfo_FilterGametype_Recursive(gametype, 0, _MapInfo_globcount);
87 if(!_MapInfo_filtered)
89 dprint("Autogenerated a .mapinfo, bailing out to avoid loop counter\n");
92 MapInfo_count = HugeSetOfIntegers_length(_MapInfo_filtered);
93 dprint("Filter ", ftos(gametype), " results in ", _MapInfo_filtered, "\n");
97 // load info about the i-th map into the MapInfo_Map_* globals
98 float MapInfo_Get_ByID(float i); // 1 on success, 0 on failure
100 float _MapInfo_Generate(string pFilename)
106 float inWorldspawn, l;
108 float spawns, diameter;
109 vector mapMins, mapMaxs;
111 fn = strcat("maps/", pFilename, ".ent");
112 fh = fopen(fn, FILE_READ);
115 fn = strcat("maps/", pFilename, ".bsp");
116 fh = fopen(fn, FILE_READ);
120 dprint("Analyzing ", fn, " to generate initial mapinfo\n");
122 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH; // DM always works
123 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH; // Rune always works
124 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS; // LMS always works
130 if not((s = fgets(fh)))
132 if(inWorldspawn == 1)
133 if(startsWith(s, "}"))
137 if(startsWith(s, "\"classname\" \"worldspawn\""))
139 else if((v = extractRestOfLine(s, "\"message\" \"")))
141 for(l = strlen(v) - 1; l > 0; --l)
142 if(substring(v, l, 1) == "\"")
144 MapInfo_Map_title = substring(v, 0, l);
149 if((v = extractRestOfLine(s, "\"origin\" \"")))
151 for(l = strlen(v) - 1; l > 0; --l)
152 if(substring(v, l, 1) == "\"")
154 o = stov(strcat("'", substring(v, 0, l), "'"));
155 mapMins_x = min(mapMins_x, o_x);
156 mapMins_y = min(mapMins_y, o_y);
157 mapMins_z = min(mapMins_z, o_z);
158 mapMaxs_x = max(mapMaxs_x, o_x);
159 mapMaxs_y = max(mapMaxs_y, o_y);
160 mapMaxs_z = max(mapMaxs_z, o_z);
162 else if((v = extractRestOfLine(s, "\"classname\" \"")))
164 if(startsWith(v, "dom_controlpoint\""))
165 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;
166 else if(startsWith(v, "item_flag_team2\""))
167 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
168 else if(startsWith(v, "runematch_spawn_point\""))
169 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH;
170 else if(startsWith(v, "target_assault_roundend\""))
171 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
172 else if(startsWith(v, "onslaught_generator\""))
173 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
174 else if(startsWith(v, "info_player_team1\""))
176 else if(startsWith(v, "info_player_team2\""))
178 else if(startsWith(v, "info_player_deathmatch\""))
180 else if(startsWith(v, "info_player_start\""))
187 print(strcat(fn, " ended still in worldspawn, BUG"));
190 diameter = vlen(mapMaxs - mapMins);
191 if(spawns >= 8 && diameter > 2048)
192 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
194 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ARENA;
195 if(spawns >= 16 && diameter > 4096)
196 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;
198 dprint(fn, ": types = ", ftos(MapInfo_Map_supportedGametypes), " spawns ", ftos(spawns), " diameter ", ftos(diameter), "\n");
202 // load info about a map by name into the MapInfo_Map_* globals
203 float MapInfo_Get_ByName(string pFilename, float pAllowGenerate)
209 // default all generic fields so they have "good" values in case something fails
210 MapInfo_Map_title = "Untitled1";
211 MapInfo_Map_description = "Bleh.";
212 MapInfo_Map_supportedGametypes = 0;
214 fn = strcat("maps/", pFilename, ".mapinfo");
215 fh = fopen(fn, FILE_READ);
220 if(!_MapInfo_Generate(pFilename))
222 fh = fopen(fn, FILE_WRITE);
223 fputs(fh, strcat("title ", MapInfo_Map_title, "\n"));
224 fputs(fh, strcat("description ", MapInfo_Map_description, "\n"));
225 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH) fputs(fh, "type dm 30 20\n");
226 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH) fputs(fh, "type tdm 50 20 2\n"); // TODO count tdm_team entities
227 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DOMINATION) fputs(fh, "type dom 200 20 2\n"); // TODO count tdm_team entities
228 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CTF) fputs(fh, "type ctf 300 20\n");
229 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RUNEMATCH) fputs(fh, "type rune 200 20\n");
230 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_LMS) fputs(fh, "type lms 9 20\n");
231 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ARENA) fputs(fh, "type arena 10 20\n");
232 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_KEYHUNT) fputs(fh, "type kh 1000 20\n");
233 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ASSAULT) fputs(fh, "type as 20\n");
234 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ONSLAUGHT) fputs(fh, "type ons 20\n");
240 if not((s = fgets(fh)))
245 MapInfo_Map_title = substring(s, 6, strlen(s) - 6); // without "title"
246 else if(t == "description")
247 MapInfo_Map_description = substring(s, 12, strlen(s) - 12);
251 if (t == "dm") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH;
252 else if(t == "tdm") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
253 else if(t == "dom") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;
254 else if(t == "ctf") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
255 else if(t == "rune") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH;
256 else if(t == "lms") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS;
257 else if(t == "arena") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ARENA;
258 else if(t == "kh") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;
259 else if(t == "as") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
260 else if(t == "ons") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
262 dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
265 dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
268 if(MapInfo_Map_supportedGametypes != 0)
270 dprint("Map ", pFilename, " supports no game types, ignored\n");