1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file p_setup.c
12 /// \brief Do all the WAD I/O, get map description, set up initial state and misc. LUTs
13
14 #include "doomdef.h"
15 #include "d_main.h"
16 #include "byteptr.h"
17 #include "g_game.h"
18
19 #include "p_local.h"
20 #include "p_setup.h"
21 #include "p_spec.h"
22 #include "p_saveg.h"
23
24 #include "i_sound.h" // for I_PlayCD()..
25 #include "i_video.h" // for I_FinishUpdate()..
26 #include "r_sky.h"
27 #include "i_system.h"
28
29 #include "r_data.h"
30 #include "r_things.h" // for R_AddSpriteDefs
31 #include "r_textures.h"
32 #include "r_patch.h"
33 #include "r_picformats.h"
34 #include "r_sky.h"
35 #include "r_draw.h"
36
37 #include "s_sound.h"
38 #include "st_stuff.h"
39 #include "w_wad.h"
40 #include "z_zone.h"
41 #include "r_splats.h"
42
43 #include "hu_stuff.h"
44 #include "console.h"
45
46 #include "m_misc.h"
47 #include "m_fixed.h"
48 #include "m_random.h"
49
50 #include "dehacked.h" // for map headers
51 #include "r_main.h"
52 #include "m_cond.h" // for emblems
53
54 #include "m_argv.h"
55
56 #include "p_polyobj.h"
57
58 #include "v_video.h"
59
60 #include "filesrch.h" // refreshdirmenu
61
62 #include "lua_hud.h" // level title
63
64 #include "f_finale.h" // wipes
65
66 #include "md5.h" // map MD5
67
68 // for LUAh_MapLoad
69 #include "lua_script.h"
70 #include "lua_hook.h"
71
72 #ifdef _WIN32
73 #include <malloc.h>
74 #include <math.h>
75 #endif
76 #ifdef HWRENDER
77 #include "hardware/hw_main.h"
78 #include "hardware/hw_light.h"
79 #include "hardware/hw_model.h"
80 #endif
81
82 #include "p_slopes.h"
83
84 #include "fastcmp.h" // textmap parsing
85
86 #include "taglist.h"
87
88 //
89 // Map MD5, calculated on level load.
90 // Sent to clients in PT_SERVERINFO.
91 //
92 unsigned char mapmd5[16];
93
94 //
95 // MAP related Lookup tables.
96 // Store VERTEXES, LINEDEFS, SIDEDEFS, etc.
97 //
98
99 boolean udmf;
100 size_t numvertexes, numsegs, numsectors, numsubsectors, numnodes, numlines, numsides, nummapthings;
101 vertex_t *vertexes;
102 seg_t *segs;
103 sector_t *sectors;
104 subsector_t *subsectors;
105 node_t *nodes;
106 line_t *lines;
107 side_t *sides;
108 mapthing_t *mapthings;
109 sector_t *spawnsectors;
110 line_t *spawnlines;
111 side_t *spawnsides;
112 INT32 numstarposts;
113 UINT16 bossdisabled;
114 boolean stoppedclock;
115 boolean levelloading;
116 UINT8 levelfadecol;
117
118 // BLOCKMAP
119 // Created from axis aligned bounding box
120 // of the map, a rectangular array of
121 // blocks of size ...
122 // Used to speed up collision detection
123 // by spatial subdivision in 2D.
124 //
125 // Blockmap size.
126 INT32 bmapwidth, bmapheight; // size in mapblocks
127
128 INT32 *blockmap; // INT32 for large maps
129 // offsets in blockmap are from here
130 INT32 *blockmaplump; // Big blockmap
131
132 // origin of block map
133 fixed_t bmaporgx, bmaporgy;
134 // for thing chains
135 mobj_t **blocklinks;
136
137 // REJECT
138 // For fast sight rejection.
139 // Speeds up enemy AI by skipping detailed LineOf Sight calculation.
140 // Without special effect, this could be used as a PVS lookup as well.
141 //
142 UINT8 *rejectmatrix;
143
144 // Maintain single and multi player starting spots.
145 INT32 numdmstarts, numcoopstarts, numredctfstarts, numbluectfstarts;
146
147 mapthing_t *deathmatchstarts[MAX_DM_STARTS];
148 mapthing_t *playerstarts[MAXPLAYERS];
149 mapthing_t *bluectfstarts[MAXPLAYERS];
150 mapthing_t *redctfstarts[MAXPLAYERS];
151
152 // Maintain waypoints
153 mobj_t *waypoints[NUMWAYPOINTSEQUENCES][WAYPOINTSEQUENCESIZE];
154 UINT16 numwaypoints[NUMWAYPOINTSEQUENCES];
155
P_AddWaypoint(UINT8 sequence,UINT8 id,mobj_t * waypoint)156 void P_AddWaypoint(UINT8 sequence, UINT8 id, mobj_t *waypoint)
157 {
158 waypoints[sequence][id] = waypoint;
159 if (id >= numwaypoints[sequence])
160 numwaypoints[sequence] = id + 1;
161 }
162
P_ResetWaypoints(void)163 static void P_ResetWaypoints(void)
164 {
165 UINT16 sequence, id;
166 for (sequence = 0; sequence < NUMWAYPOINTSEQUENCES; sequence++)
167 {
168 for (id = 0; id < numwaypoints[sequence]; id++)
169 waypoints[sequence][id] = NULL;
170
171 numwaypoints[sequence] = 0;
172 }
173 }
174
P_GetFirstWaypoint(UINT8 sequence)175 mobj_t *P_GetFirstWaypoint(UINT8 sequence)
176 {
177 return waypoints[sequence][0];
178 }
179
P_GetLastWaypoint(UINT8 sequence)180 mobj_t *P_GetLastWaypoint(UINT8 sequence)
181 {
182 return waypoints[sequence][numwaypoints[sequence] - 1];
183 }
184
P_GetPreviousWaypoint(mobj_t * current,boolean wrap)185 mobj_t *P_GetPreviousWaypoint(mobj_t *current, boolean wrap)
186 {
187 UINT8 sequence = current->threshold;
188 UINT8 id = current->health;
189
190 if (id == 0)
191 {
192 if (!wrap)
193 return NULL;
194
195 id = numwaypoints[sequence] - 1;
196 }
197 else
198 id--;
199
200 return waypoints[sequence][id];
201 }
202
P_GetNextWaypoint(mobj_t * current,boolean wrap)203 mobj_t *P_GetNextWaypoint(mobj_t *current, boolean wrap)
204 {
205 UINT8 sequence = current->threshold;
206 UINT8 id = current->health;
207
208 if (id == numwaypoints[sequence] - 1)
209 {
210 if (!wrap)
211 return NULL;
212
213 id = 0;
214 }
215 else
216 id++;
217
218 return waypoints[sequence][id];
219 }
220
P_GetClosestWaypoint(UINT8 sequence,mobj_t * mo)221 mobj_t *P_GetClosestWaypoint(UINT8 sequence, mobj_t *mo)
222 {
223 UINT8 wp;
224 mobj_t *mo2, *result = NULL;
225 fixed_t bestdist = 0;
226 fixed_t curdist;
227
228 for (wp = 0; wp < numwaypoints[sequence]; wp++)
229 {
230 mo2 = waypoints[sequence][wp];
231
232 if (!mo2)
233 continue;
234
235 curdist = P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z);
236
237 if (result && curdist > bestdist)
238 continue;
239
240 result = mo2;
241 bestdist = curdist;
242 }
243
244 return result;
245 }
246
247 // Return true if all waypoints are in the same location
P_IsDegeneratedWaypointSequence(UINT8 sequence)248 boolean P_IsDegeneratedWaypointSequence(UINT8 sequence)
249 {
250 mobj_t *first, *waypoint;
251 UINT8 wp;
252
253 if (numwaypoints[sequence] <= 1)
254 return true;
255
256 first = waypoints[sequence][0];
257
258 for (wp = 1; wp < numwaypoints[sequence]; wp++)
259 {
260 waypoint = waypoints[sequence][wp];
261
262 if (!waypoint)
263 continue;
264
265 if (waypoint->x != first->x)
266 return false;
267
268 if (waypoint->y != first->y)
269 return false;
270
271 if (waypoint->z != first->z)
272 return false;
273 }
274
275 return true;
276 }
277
278
279 /** Logs an error about a map being corrupt, then terminate.
280 * This allows reporting highly technical errors for usefulness, without
281 * confusing a novice map designer who simply needs to run ZenNode.
282 *
283 * If logging is disabled in this compile, or the log file is not opened, the
284 * full technical details are printed in the I_Error() message.
285 *
286 * \param msg The message to log. This message can safely result from a call
287 * to va(), since that function is not used here.
288 * \todo Fix the I_Error() message. On some implementations the logfile may
289 * not be called log.txt.
290 * \sa CON_LogMessage, I_Error
291 */
CorruptMapError(const char * msg)292 FUNCNORETURN static ATTRNORETURN void CorruptMapError(const char *msg)
293 {
294 // don't use va() because the calling function probably uses it
295 char mapnum[10];
296
297 sprintf(mapnum, "%hd", gamemap);
298 CON_LogMessage("Map ");
299 CON_LogMessage(mapnum);
300 CON_LogMessage(" is corrupt: ");
301 CON_LogMessage(msg);
302 CON_LogMessage("\n");
303 I_Error("Invalid or corrupt map.\nLook in log file or text console for technical details.");
304 }
305
306 /** Sets a header's flickies to be equivalent to the original Freed Animals
307 *
308 * \param i The header to set flickies for
309 */
P_SetDemoFlickies(INT16 i)310 void P_SetDemoFlickies(INT16 i)
311 {
312 mapheaderinfo[i]->numFlickies = 5;
313 mapheaderinfo[i]->flickies = Z_Realloc(mapheaderinfo[i]->flickies, 5*sizeof(mobjtype_t), PU_STATIC, NULL);
314 mapheaderinfo[i]->flickies[0] = MT_FLICKY_02/*MT_BUNNY*/;
315 mapheaderinfo[i]->flickies[1] = MT_FLICKY_01/*MT_BIRD*/;
316 mapheaderinfo[i]->flickies[2] = MT_FLICKY_12/*MT_MOUSE*/;
317 mapheaderinfo[i]->flickies[3] = MT_FLICKY_11/*MT_COW*/;
318 mapheaderinfo[i]->flickies[4] = MT_FLICKY_03/*MT_CHICKEN*/;
319 }
320
321 /** Clears a header's flickies
322 *
323 * \param i The header to clear flickies for
324 */
P_DeleteFlickies(INT16 i)325 void P_DeleteFlickies(INT16 i)
326 {
327 if (mapheaderinfo[i]->flickies)
328 Z_Free(mapheaderinfo[i]->flickies);
329 mapheaderinfo[i]->flickies = NULL;
330 mapheaderinfo[i]->numFlickies = 0;
331 }
332
333 #define NUMLAPS_DEFAULT 4
334
335 /** Clears the data from a single map header.
336 *
337 * \param i Map number to clear header for.
338 * \sa P_ClearMapHeaderInfo
339 */
P_ClearSingleMapHeaderInfo(INT16 i)340 static void P_ClearSingleMapHeaderInfo(INT16 i)
341 {
342 const INT16 num = (INT16)(i-1);
343 mapheaderinfo[num]->lvlttl[0] = '\0';
344 mapheaderinfo[num]->selectheading[0] = '\0';
345 mapheaderinfo[num]->subttl[0] = '\0';
346 mapheaderinfo[num]->ltzzpatch[0] = '\0';
347 mapheaderinfo[num]->ltzztext[0] = '\0';
348 mapheaderinfo[num]->ltactdiamond[0] = '\0';
349 mapheaderinfo[num]->actnum = 0;
350 mapheaderinfo[num]->typeoflevel = 0;
351 mapheaderinfo[num]->nextlevel = (INT16)(i + 1);
352 mapheaderinfo[num]->marathonnext = 0;
353 mapheaderinfo[num]->startrings = 0;
354 mapheaderinfo[num]->sstimer = 90;
355 mapheaderinfo[num]->ssspheres = 1;
356 mapheaderinfo[num]->gravity = FRACUNIT/2;
357 mapheaderinfo[num]->keywords[0] = '\0';
358 snprintf(mapheaderinfo[num]->musname, 7, "%sM", G_BuildMapName(i));
359 mapheaderinfo[num]->musname[6] = 0;
360 mapheaderinfo[num]->mustrack = 0;
361 mapheaderinfo[num]->muspos = 0;
362 mapheaderinfo[num]->musinterfadeout = 0;
363 mapheaderinfo[num]->musintername[0] = 0;
364 mapheaderinfo[num]->muspostbossname[0] = 0;
365 mapheaderinfo[num]->muspostbosstrack = 0;
366 mapheaderinfo[num]->muspostbosspos = 0;
367 mapheaderinfo[num]->muspostbossfadein = 0;
368 mapheaderinfo[num]->musforcereset = -1;
369 mapheaderinfo[num]->forcecharacter[0] = '\0';
370 mapheaderinfo[num]->weather = 0;
371 mapheaderinfo[num]->skynum = 1;
372 mapheaderinfo[num]->skybox_scalex = 16;
373 mapheaderinfo[num]->skybox_scaley = 16;
374 mapheaderinfo[num]->skybox_scalez = 16;
375 mapheaderinfo[num]->interscreen[0] = '#';
376 mapheaderinfo[num]->runsoc[0] = '#';
377 mapheaderinfo[num]->scriptname[0] = '#';
378 mapheaderinfo[num]->precutscenenum = 0;
379 mapheaderinfo[num]->cutscenenum = 0;
380 mapheaderinfo[num]->countdown = 0;
381 mapheaderinfo[num]->palette = UINT16_MAX;
382 mapheaderinfo[num]->numlaps = NUMLAPS_DEFAULT;
383 mapheaderinfo[num]->unlockrequired = -1;
384 mapheaderinfo[num]->levelselect = 0;
385 mapheaderinfo[num]->bonustype = 0;
386 mapheaderinfo[num]->maxbonuslives = -1;
387 mapheaderinfo[num]->levelflags = 0;
388 mapheaderinfo[num]->menuflags = 0;
389 #if 1 // equivalent to "FlickyList = DEMO"
390 P_SetDemoFlickies(num);
391 #else // equivalent to "FlickyList = NONE"
392 P_DeleteFlickies(num);
393 #endif
394 P_DeleteGrades(num);
395 mapheaderinfo[num]->customopts = NULL;
396 mapheaderinfo[num]->numCustomOptions = 0;
397 }
398
399 /** Allocates a new map-header structure.
400 *
401 * \param i Index of header to allocate.
402 */
P_AllocMapHeader(INT16 i)403 void P_AllocMapHeader(INT16 i)
404 {
405 if (!mapheaderinfo[i])
406 {
407 mapheaderinfo[i] = Z_Malloc(sizeof(mapheader_t), PU_STATIC, NULL);
408 mapheaderinfo[i]->flickies = NULL;
409 mapheaderinfo[i]->grades = NULL;
410 }
411 P_ClearSingleMapHeaderInfo(i + 1);
412 }
413
414 /** NiGHTS Grades are a special structure,
415 * we initialize them here.
416 *
417 * \param i Index of header to allocate grades for
418 * \param mare The mare we're adding grades for
419 * \param grades the string from DeHackEd, we work with it ourselves
420 */
P_AddGradesForMare(INT16 i,UINT8 mare,char * gtext)421 void P_AddGradesForMare(INT16 i, UINT8 mare, char *gtext)
422 {
423 INT32 g;
424 char *spos = gtext;
425
426 CONS_Debug(DBG_SETUP, "Map %d Mare %d: ", i+1, (UINT16)mare+1);
427
428 if (mapheaderinfo[i]->numGradedMares < mare+1)
429 {
430 mapheaderinfo[i]->numGradedMares = mare+1;
431 mapheaderinfo[i]->grades = Z_Realloc(mapheaderinfo[i]->grades, sizeof(nightsgrades_t) * mapheaderinfo[i]->numGradedMares, PU_STATIC, NULL);
432 }
433
434 for (g = 0; g < 6; ++g)
435 {
436 // Allow "partial" grading systems
437 if (spos != NULL)
438 {
439 mapheaderinfo[i]->grades[mare].grade[g] = atoi(spos);
440 CONS_Debug(DBG_SETUP, "%u ", atoi(spos));
441 // Grab next comma
442 spos = strchr(spos, ',');
443 if (spos)
444 ++spos;
445 }
446 else
447 {
448 // Grade not reachable
449 mapheaderinfo[i]->grades[mare].grade[g] = UINT32_MAX;
450 }
451 }
452
453 CONS_Debug(DBG_SETUP, "\n");
454 }
455
456 /** And this removes the grades safely.
457 *
458 * \param i The header to remove grades from
459 */
P_DeleteGrades(INT16 i)460 void P_DeleteGrades(INT16 i)
461 {
462 if (mapheaderinfo[i]->grades)
463 Z_Free(mapheaderinfo[i]->grades);
464
465 mapheaderinfo[i]->grades = NULL;
466 mapheaderinfo[i]->numGradedMares = 0;
467 }
468
469 /** And this fetches the grades
470 *
471 * \param pscore The player's score.
472 * \param map The game map.
473 * \param mare The mare to test.
474 */
P_GetGrade(UINT32 pscore,INT16 map,UINT8 mare)475 UINT8 P_GetGrade(UINT32 pscore, INT16 map, UINT8 mare)
476 {
477 INT32 i;
478
479 // Determining the grade
480 if (mapheaderinfo[map-1] && mapheaderinfo[map-1]->grades && mapheaderinfo[map-1]->numGradedMares >= mare + 1)
481 {
482 INT32 pgrade = 0;
483 for (i = 0; i < 6; ++i)
484 {
485 if (pscore >= mapheaderinfo[map-1]->grades[mare].grade[i])
486 ++pgrade;
487 }
488 return (UINT8)pgrade;
489 }
490 return 0;
491 }
492
P_HasGrades(INT16 map,UINT8 mare)493 UINT8 P_HasGrades(INT16 map, UINT8 mare)
494 {
495 // Determining the grade
496 // Mare 0 is treated as overall and is true if ANY grades exist
497 if (mapheaderinfo[map-1] && mapheaderinfo[map-1]->grades
498 && (mare == 0 || mapheaderinfo[map-1]->numGradedMares >= mare))
499 return true;
500 return false;
501 }
502
P_GetScoreForGrade(INT16 map,UINT8 mare,UINT8 grade)503 UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade)
504 {
505 // Get the score for the grade... if it exists
506 if (grade == GRADE_F || grade > GRADE_S || !P_HasGrades(map, mare)) return 0;
507
508 return mapheaderinfo[map-1]->grades[mare].grade[grade-1];
509 }
510
511 //
512 // levelflats
513 //
514 #define MAXLEVELFLATS 256
515
516 size_t numlevelflats;
517 levelflat_t *levelflats;
518 levelflat_t *foundflats;
519
520 //SoM: Other files want this info.
P_PrecacheLevelFlats(void)521 size_t P_PrecacheLevelFlats(void)
522 {
523 lumpnum_t lump;
524 size_t i;
525
526 //SoM: 4/18/2000: New flat code to make use of levelflats.
527 flatmemory = 0;
528 for (i = 0; i < numlevelflats; i++)
529 {
530 if (levelflats[i].type == LEVELFLAT_FLAT)
531 {
532 lump = levelflats[i].u.flat.lumpnum;
533 if (devparm)
534 flatmemory += W_LumpLength(lump);
535 R_GetFlat(lump);
536 }
537 }
538 return flatmemory;
539 }
540
541 /*
542 levelflat refers to an array of level flats,
543 or NULL if we want to allocate it now.
544 */
545 static INT32
Ploadflat(levelflat_t * levelflat,const char * flatname,boolean resize)546 Ploadflat (levelflat_t *levelflat, const char *flatname, boolean resize)
547 {
548 #ifndef NO_PNG_LUMPS
549 UINT8 buffer[8];
550 #endif
551
552 lumpnum_t flatnum;
553 int texturenum;
554 UINT8 *flatpatch;
555 size_t lumplength;
556
557 size_t i;
558
559 // Scan through the already found flats, return if it matches.
560 for (i = 0; i < numlevelflats; i++)
561 {
562 if (strnicmp(levelflat[i].name, flatname, 8) == 0)
563 return i;
564 }
565
566 if (resize)
567 {
568 // allocate new flat memory
569 levelflats = Z_Realloc(levelflats, (numlevelflats + 1) * sizeof(*levelflats), PU_LEVEL, NULL);
570 levelflat = levelflats + numlevelflats;
571 }
572 else
573 {
574 if (numlevelflats >= MAXLEVELFLATS)
575 I_Error("Too many flats in level\n");
576
577 levelflat += numlevelflats;
578 }
579
580 // Store the name.
581 strlcpy(levelflat->name, flatname, sizeof (levelflat->name));
582 strupr(levelflat->name);
583
584 /* If we can't find a flat, try looking for a texture! */
585 if (( flatnum = R_GetFlatNumForName(levelflat->name) ) == LUMPERROR)
586 {
587 if (( texturenum = R_CheckTextureNumForName(levelflat->name) ) == -1)
588 {
589 // check for REDWALL
590 if (( texturenum = R_CheckTextureNumForName("REDWALL") ) != -1)
591 goto texturefound;
592 // check for REDFLR
593 else if (( flatnum = R_GetFlatNumForName("REDFLR") ) != LUMPERROR)
594 goto flatfound;
595 // nevermind
596 levelflat->type = LEVELFLAT_NONE;
597 }
598 else
599 {
600 texturefound:
601 levelflat->type = LEVELFLAT_TEXTURE;
602 levelflat->u.texture. num = texturenum;
603 levelflat->u.texture.lastnum = texturenum;
604 /* start out unanimated */
605 levelflat->u.texture.basenum = -1;
606 }
607 }
608 else
609 {
610 flatfound:
611 /* This could be a flat, patch, or PNG. */
612 flatpatch = W_CacheLumpNum(flatnum, PU_CACHE);
613 lumplength = W_LumpLength(flatnum);
614 if (Picture_CheckIfDoomPatch((softwarepatch_t *)flatpatch, lumplength))
615 levelflat->type = LEVELFLAT_PATCH;
616 else
617 {
618 #ifndef NO_PNG_LUMPS
619 /*
620 Only need eight bytes for PNG headers.
621 FIXME: Put this elsewhere.
622 */
623 W_ReadLumpHeader(flatnum, buffer, 8, 0);
624 if (Picture_IsLumpPNG(buffer, lumplength))
625 levelflat->type = LEVELFLAT_PNG;
626 else
627 #endif/*NO_PNG_LUMPS*/
628 levelflat->type = LEVELFLAT_FLAT;/* phew */
629 }
630 if (flatpatch)
631 Z_Free(flatpatch);
632
633 levelflat->u.flat. lumpnum = flatnum;
634 levelflat->u.flat.baselumpnum = LUMPERROR;
635 }
636
637 #ifndef ZDEBUG
638 CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name);
639 #endif
640
641 return ( numlevelflats++ );
642 }
643
644 // Auxiliary function. Find a flat in the active wad files,
645 // allocate an id for it, and set the levelflat (to speedup search)
P_AddLevelFlat(const char * flatname,levelflat_t * levelflat)646 INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat)
647 {
648 return Ploadflat(levelflat, flatname, false);
649 }
650
651 // help function for Lua and $$$.sav reading
652 // same as P_AddLevelFlat, except this is not setup so we must realloc levelflats to fit in the new flat
653 // no longer a static func in lua_maplib.c because p_saveg.c also needs it
654 //
P_AddLevelFlatRuntime(const char * flatname)655 INT32 P_AddLevelFlatRuntime(const char *flatname)
656 {
657 return Ploadflat(levelflats, flatname, true);
658 }
659
660 // help function for $$$.sav checking
661 // this simply returns the flat # for the name given
662 //
P_CheckLevelFlat(const char * flatname)663 INT32 P_CheckLevelFlat(const char *flatname)
664 {
665 size_t i;
666 levelflat_t *levelflat = levelflats;
667
668 //
669 // scan through the already found flats
670 //
671 for (i = 0; i < numlevelflats; i++, levelflat++)
672 if (strnicmp(levelflat->name,flatname,8)==0)
673 break;
674
675 if (i == numlevelflats)
676 return 0; // ??? flat was not found, this should not happen!
677
678 // level flat id
679 return (INT32)i;
680 }
681
682 //
683 // P_ReloadRings
684 // Used by NiGHTS, clears all ring/sphere/hoop/etc items and respawns them
685 //
P_ReloadRings(void)686 void P_ReloadRings(void)
687 {
688 mobj_t *mo;
689 thinker_t *th;
690 size_t i, numHoops = 0;
691 // Okay, if you have more than 4000 hoops in your map,
692 // you're insane.
693 mapthing_t *hoopsToRespawn[4096];
694 mapthing_t *mt = mapthings;
695
696 // scan the thinkers to find rings/spheres/hoops to unset
697 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
698 {
699 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
700 continue;
701
702 mo = (mobj_t *)th;
703
704 if (mo->type == MT_HOOPCENTER)
705 {
706 // Hoops give me a headache
707 if (mo->threshold == 4242) // Dead hoop
708 {
709 hoopsToRespawn[numHoops++] = mo->spawnpoint;
710 P_RemoveMobj(mo);
711 }
712 continue;
713 }
714 if (!(mo->type == MT_RING || mo->type == MT_COIN
715 || mo->type == MT_BLUESPHERE || mo->type == MT_BOMBSPHERE
716 || mo->type == MT_NIGHTSCHIP || mo->type == MT_NIGHTSSTAR))
717 continue;
718
719 // Don't auto-disintegrate things being pulled to us
720 if (mo->flags2 & MF2_NIGHTSPULL)
721 continue;
722
723 P_RemoveMobj(mo);
724 }
725
726 // Reiterate through mapthings
727 for (i = 0; i < nummapthings; i++, mt++)
728 {
729 // Notice an omission? We handle hoops differently.
730 if (mt->type == mobjinfo[MT_RING].doomednum || mt->type == mobjinfo[MT_COIN].doomednum
731 || mt->type == mobjinfo[MT_REDTEAMRING].doomednum || mt->type == mobjinfo[MT_BLUETEAMRING].doomednum
732 || mt->type == mobjinfo[MT_BLUESPHERE].doomednum || mt->type == mobjinfo[MT_BOMBSPHERE].doomednum)
733 {
734 mt->mobj = NULL;
735 P_SetBonusTime(P_SpawnMapThing(mt));
736 }
737 else if (mt->type >= 600 && mt->type <= 609) // Item patterns
738 {
739 mt->mobj = NULL;
740 P_SpawnItemPattern(mt, true);
741 }
742 }
743 for (i = 0; i < numHoops; i++)
744 {
745 P_SpawnHoop(hoopsToRespawn[i]);
746 }
747 }
748
P_SwitchSpheresBonusMode(boolean bonustime)749 void P_SwitchSpheresBonusMode(boolean bonustime)
750 {
751 mobj_t *mo;
752 thinker_t *th;
753
754 // scan the thinkers to find spheres to switch
755 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
756 {
757 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
758 continue;
759
760 mo = (mobj_t *)th;
761
762 if (mo->type != MT_BLUESPHERE && mo->type != MT_NIGHTSCHIP
763 && mo->type != MT_FLINGBLUESPHERE && mo->type != MT_FLINGNIGHTSCHIP)
764 continue;
765
766 if (!mo->health)
767 continue;
768
769 P_SetMobjState(mo, ((bonustime) ? mo->info->raisestate : mo->info->spawnstate));
770 }
771 }
772
773 #ifdef SCANTHINGS
P_ScanThings(INT16 mapnum,INT16 wadnum,INT16 lumpnum)774 void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum)
775 {
776 size_t i, n;
777 UINT8 *data, *datastart;
778 UINT16 type, maprings;
779 INT16 tol;
780 UINT32 flags;
781
782 tol = mapheaderinfo[mapnum-1]->typeoflevel;
783 if (!(tol & TOL_SP))
784 return;
785 flags = mapheaderinfo[mapnum-1]->levelflags;
786
787 n = W_LumpLengthPwad(wadnum, lumpnum) / (5 * sizeof (INT16));
788 //CONS_Printf("%u map things found!\n", n);
789
790 maprings = 0;
791 data = datastart = W_CacheLumpNumPwad(wadnum, lumpnum, PU_STATIC);
792 for (i = 0; i < n; i++)
793 {
794 data += 3 * sizeof (INT16); // skip x y position, angle
795 type = READUINT16(data) & 4095;
796 data += sizeof (INT16); // skip options
797
798 switch (type)
799 {
800 case 300: // MT_RING
801 case 1800: // MT_COIN
802 case 308: // red team ring
803 case 309: // blue team ring
804 maprings++;
805 break;
806 case 400: // MT_SUPERRINGBOX
807 case 414: // red ring box
808 case 415: // blue ring box
809 case 603: // 10 diagonal rings
810 maprings += 10;
811 break;
812 case 600: // 5 vertical rings
813 case 601: // 5 vertical rings
814 case 602: // 5 diagonal rings
815 maprings += 5;
816 break;
817 case 604: // 8 circle rings
818 case 609: // 16 circle rings & wings
819 maprings += 8;
820 break;
821 case 605: // 16 circle rings
822 maprings += 16;
823 break;
824 case 608: // 8 circle rings & wings
825 maprings += 4;
826 break;
827 }
828 }
829 Z_Free(datastart);
830
831 if (maprings)
832 CONS_Printf("%s has %u rings\n", G_BuildMapName(mapnum), maprings);
833 }
834 #endif
835
P_SpawnEmeraldHunt(void)836 static void P_SpawnEmeraldHunt(void)
837 {
838 INT32 emer[3], num[MAXHUNTEMERALDS], i, randomkey;
839 fixed_t x, y, z;
840
841 for (i = 0; i < numhuntemeralds; i++)
842 num[i] = i;
843
844 for (i = 0; i < 3; i++)
845 {
846 // generate random index, shuffle afterwards
847 randomkey = P_RandomKey(numhuntemeralds--);
848 emer[i] = num[randomkey];
849 num[randomkey] = num[numhuntemeralds];
850 num[numhuntemeralds] = emer[i];
851
852 // spawn emerald
853 x = huntemeralds[emer[i]]->x<<FRACBITS;
854 y = huntemeralds[emer[i]]->y<<FRACBITS;
855 z = P_GetMapThingSpawnHeight(MT_EMERHUNT, huntemeralds[emer[i]], x, y);
856 P_SetMobjStateNF(P_SpawnMobj(x, y, z, MT_EMERHUNT),
857 mobjinfo[MT_EMERHUNT].spawnstate+i);
858 }
859 }
860
P_SpawnMapThings(boolean spawnemblems)861 static void P_SpawnMapThings(boolean spawnemblems)
862 {
863 size_t i;
864 mapthing_t *mt;
865
866 // Spawn axis points first so they are at the front of the list for fast searching.
867 for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
868 {
869 switch (mt->type)
870 {
871 case 1700: // MT_AXIS
872 case 1701: // MT_AXISTRANSFER
873 case 1702: // MT_AXISTRANSFERLINE
874 mt->mobj = NULL;
875 P_SpawnMapThing(mt);
876 break;
877 default:
878 break;
879 }
880 }
881
882 numhuntemeralds = 0;
883
884 for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
885 {
886 if (mt->type == 1700 // MT_AXIS
887 || mt->type == 1701 // MT_AXISTRANSFER
888 || mt->type == 1702) // MT_AXISTRANSFERLINE
889 continue; // These were already spawned
890
891 if (!spawnemblems && mt->type == mobjinfo[MT_EMBLEM].doomednum)
892 continue;
893
894 mt->mobj = NULL;
895
896 if (mt->type >= 600 && mt->type <= 609) // item patterns
897 P_SpawnItemPattern(mt, false);
898 else if (mt->type == 1705 || mt->type == 1713) // hoops
899 P_SpawnHoop(mt);
900 else // Everything else
901 P_SpawnMapThing(mt);
902 }
903
904 // random emeralds for hunt
905 if (numhuntemeralds)
906 P_SpawnEmeraldHunt();
907 }
908
909 // Experimental groovy write function!
P_WriteThings(void)910 void P_WriteThings(void)
911 {
912 size_t i, length;
913 mapthing_t *mt;
914 UINT8 *savebuffer, *savebuf_p;
915 INT16 temp;
916
917 savebuf_p = savebuffer = (UINT8 *)malloc(nummapthings * sizeof (mapthing_t));
918
919 if (!savebuf_p)
920 {
921 CONS_Alert(CONS_ERROR, M_GetText("No more free memory for thing writing!\n"));
922 return;
923 }
924
925 mt = mapthings;
926 for (i = 0; i < nummapthings; i++, mt++)
927 {
928 WRITEINT16(savebuf_p, mt->x);
929 WRITEINT16(savebuf_p, mt->y);
930
931 WRITEINT16(savebuf_p, mt->angle);
932
933 temp = (INT16)(mt->type + ((INT16)mt->extrainfo << 12));
934 WRITEINT16(savebuf_p, temp);
935 WRITEUINT16(savebuf_p, mt->options);
936 }
937
938 length = savebuf_p - savebuffer;
939
940 FIL_WriteFile(va("newthings%d.lmp", gamemap), savebuffer, length);
941 free(savebuffer);
942 savebuf_p = NULL;
943
944 CONS_Printf(M_GetText("newthings%d.lmp saved.\n"), gamemap);
945 }
946
947 //
948 // MAP LOADING FUNCTIONS
949 //
950
P_LoadVertices(UINT8 * data)951 static void P_LoadVertices(UINT8 *data)
952 {
953 mapvertex_t *mv = (mapvertex_t *)data;
954 vertex_t *v = vertexes;
955 size_t i;
956
957 // Copy and convert vertex coordinates, internal representation as fixed.
958 for (i = 0; i < numvertexes; i++, v++, mv++)
959 {
960 v->x = SHORT(mv->x)<<FRACBITS;
961 v->y = SHORT(mv->y)<<FRACBITS;
962 v->floorzset = v->ceilingzset = false;
963 v->floorz = v->ceilingz = 0;
964 }
965 }
966
P_InitializeSector(sector_t * ss)967 static void P_InitializeSector(sector_t *ss)
968 {
969 memset(&ss->soundorg, 0, sizeof(ss->soundorg));
970
971 ss->validcount = 0;
972
973 ss->thinglist = NULL;
974
975 ss->floordata = NULL;
976 ss->ceilingdata = NULL;
977 ss->lightingdata = NULL;
978 ss->fadecolormapdata = NULL;
979
980 ss->heightsec = -1;
981 ss->camsec = -1;
982
983 ss->floorlightsec = ss->ceilinglightsec = -1;
984 ss->crumblestate = CRUMBLE_NONE;
985
986 ss->touching_thinglist = NULL;
987
988 ss->linecount = 0;
989 ss->lines = NULL;
990
991 ss->ffloors = NULL;
992 ss->attached = NULL;
993 ss->attachedsolid = NULL;
994 ss->numattached = 0;
995 ss->maxattached = 1;
996 ss->lightlist = NULL;
997 ss->numlights = 0;
998 ss->moved = true;
999
1000 ss->extra_colormap = NULL;
1001
1002 ss->gravity = NULL;
1003 ss->verticalflip = false;
1004 ss->flags = SF_FLIPSPECIAL_FLOOR;
1005
1006 ss->cullheight = NULL;
1007
1008 ss->floorspeed = ss->ceilspeed = 0;
1009
1010 ss->preciplist = NULL;
1011 ss->touching_preciplist = NULL;
1012
1013 ss->f_slope = NULL;
1014 ss->c_slope = NULL;
1015 ss->hasslope = false;
1016
1017 ss->spawn_lightlevel = ss->lightlevel;
1018
1019 ss->spawn_extra_colormap = NULL;
1020 }
1021
P_LoadSectors(UINT8 * data)1022 static void P_LoadSectors(UINT8 *data)
1023 {
1024 mapsector_t *ms = (mapsector_t *)data;
1025 sector_t *ss = sectors;
1026 size_t i;
1027
1028 // For each counted sector, copy the sector raw data from our cache pointer ms, to the global table pointer ss.
1029 for (i = 0; i < numsectors; i++, ss++, ms++)
1030 {
1031 ss->floorheight = SHORT(ms->floorheight)<<FRACBITS;
1032 ss->ceilingheight = SHORT(ms->ceilingheight)<<FRACBITS;
1033
1034 ss->floorpic = P_AddLevelFlat(ms->floorpic, foundflats);
1035 ss->ceilingpic = P_AddLevelFlat(ms->ceilingpic, foundflats);
1036
1037 ss->lightlevel = SHORT(ms->lightlevel);
1038 ss->special = SHORT(ms->special);
1039 Tag_FSet(&ss->tags, SHORT(ms->tag));
1040
1041 ss->floor_xoffs = ss->floor_yoffs = 0;
1042 ss->ceiling_xoffs = ss->ceiling_yoffs = 0;
1043
1044 ss->floorpic_angle = ss->ceilingpic_angle = 0;
1045
1046 ss->colormap_protected = false;
1047
1048 P_InitializeSector(ss);
1049 }
1050 }
1051
P_InitializeLinedef(line_t * ld)1052 static void P_InitializeLinedef(line_t *ld)
1053 {
1054 vertex_t *v1 = ld->v1;
1055 vertex_t *v2 = ld->v2;
1056 UINT8 j;
1057
1058 ld->dx = v2->x - v1->x;
1059 ld->dy = v2->y - v1->y;
1060
1061 ld->bbox[BOXLEFT] = min(v1->x, v2->x);
1062 ld->bbox[BOXRIGHT] = max(v1->x, v2->x);
1063 ld->bbox[BOXBOTTOM] = min(v1->y, v2->y);
1064 ld->bbox[BOXTOP] = max(v1->y, v2->y);
1065
1066 if (!ld->dx)
1067 ld->slopetype = ST_VERTICAL;
1068 else if (!ld->dy)
1069 ld->slopetype = ST_HORIZONTAL;
1070 else if ((ld->dy > 0) == (ld->dx > 0))
1071 ld->slopetype = ST_POSITIVE;
1072 else
1073 ld->slopetype = ST_NEGATIVE;
1074
1075 ld->frontsector = ld->backsector = NULL;
1076
1077 ld->validcount = 0;
1078 ld->polyobj = NULL;
1079
1080 ld->text = NULL;
1081 ld->callcount = 0;
1082
1083 // cph 2006/09/30 - fix sidedef errors right away.
1084 // cph 2002/07/20 - these errors are fatal if not fixed, so apply them
1085 for (j = 0; j < 2; j++)
1086 if (ld->sidenum[j] != 0xffff && ld->sidenum[j] >= (UINT16)numsides)
1087 {
1088 ld->sidenum[j] = 0xffff;
1089 CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s has out-of-range sidedef number\n", sizeu1((size_t)(ld - lines)));
1090 }
1091
1092 // killough 11/98: fix common wad errors (missing sidedefs):
1093 if (ld->sidenum[0] == 0xffff)
1094 {
1095 ld->sidenum[0] = 0; // Substitute dummy sidedef for missing right side
1096 // cph - print a warning about the bug
1097 CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s missing first sidedef\n", sizeu1((size_t)(ld - lines)));
1098 }
1099
1100 if ((ld->sidenum[1] == 0xffff) && (ld->flags & ML_TWOSIDED))
1101 {
1102 ld->flags &= ~ML_TWOSIDED; // Clear 2s flag for missing left side
1103 // cph - print a warning about the bug
1104 CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s has two-sided flag set, but no second sidedef\n", sizeu1((size_t)(ld - lines)));
1105 }
1106
1107 if (ld->sidenum[0] != 0xffff)
1108 {
1109 sides[ld->sidenum[0]].special = ld->special;
1110 sides[ld->sidenum[0]].line = ld;
1111 }
1112 if (ld->sidenum[1] != 0xffff)
1113 {
1114 sides[ld->sidenum[1]].special = ld->special;
1115 sides[ld->sidenum[1]].line = ld;
1116 }
1117 }
1118
P_SetLinedefV1(size_t i,UINT16 vertex_num)1119 static void P_SetLinedefV1(size_t i, UINT16 vertex_num)
1120 {
1121 if (vertex_num >= numvertexes)
1122 {
1123 CONS_Debug(DBG_SETUP, "P_SetLinedefV1: linedef %s has out-of-range v1 num %u\n", sizeu1(i), vertex_num);
1124 vertex_num = 0;
1125 }
1126 lines[i].v1 = &vertexes[vertex_num];
1127 }
1128
P_SetLinedefV2(size_t i,UINT16 vertex_num)1129 static void P_SetLinedefV2(size_t i, UINT16 vertex_num)
1130 {
1131 if (vertex_num >= numvertexes)
1132 {
1133 CONS_Debug(DBG_SETUP, "P_SetLinedefV2: linedef %s has out-of-range v2 num %u\n", sizeu1(i), vertex_num);
1134 vertex_num = 0;
1135 }
1136 lines[i].v2 = &vertexes[vertex_num];
1137 }
1138
P_LoadLinedefs(UINT8 * data)1139 static void P_LoadLinedefs(UINT8 *data)
1140 {
1141 maplinedef_t *mld = (maplinedef_t *)data;
1142 line_t *ld = lines;
1143 size_t i;
1144
1145 for (i = 0; i < numlines; i++, mld++, ld++)
1146 {
1147 ld->flags = SHORT(mld->flags);
1148 ld->special = SHORT(mld->special);
1149 Tag_FSet(&ld->tags, SHORT(mld->tag));
1150 memset(ld->args, 0, NUMLINEARGS*sizeof(*ld->args));
1151 memset(ld->stringargs, 0x00, NUMLINESTRINGARGS*sizeof(*ld->stringargs));
1152 ld->alpha = FRACUNIT;
1153 ld->executordelay = 0;
1154 P_SetLinedefV1(i, SHORT(mld->v1));
1155 P_SetLinedefV2(i, SHORT(mld->v2));
1156
1157 ld->sidenum[0] = SHORT(mld->sidenum[0]);
1158 ld->sidenum[1] = SHORT(mld->sidenum[1]);
1159
1160 P_InitializeLinedef(ld);
1161 }
1162 }
1163
P_SetSidedefSector(size_t i,UINT16 sector_num)1164 static void P_SetSidedefSector(size_t i, UINT16 sector_num)
1165 {
1166 // cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead
1167 if (sector_num >= numsectors)
1168 {
1169 CONS_Debug(DBG_SETUP, "P_SetSidedefSector: sidedef %s has out-of-range sector num %u\n", sizeu1(i), sector_num);
1170 sector_num = 0;
1171 }
1172 sides[i].sector = §ors[sector_num];
1173 }
1174
P_InitializeSidedef(side_t * sd)1175 static void P_InitializeSidedef(side_t *sd)
1176 {
1177 if (!sd->line)
1178 {
1179 CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1((size_t)(sd - sides)));
1180 sd->line = &lines[0];
1181 sd->special = sd->line->special;
1182 }
1183
1184 sd->text = NULL;
1185 sd->colormap_data = NULL;
1186 }
1187
P_LoadSidedefs(UINT8 * data)1188 static void P_LoadSidedefs(UINT8 *data)
1189 {
1190 mapsidedef_t *msd = (mapsidedef_t*)data;
1191 side_t *sd = sides;
1192 size_t i;
1193
1194 for (i = 0; i < numsides; i++, sd++, msd++)
1195 {
1196 INT16 textureoffset = SHORT(msd->textureoffset);
1197 boolean isfrontside;
1198
1199 P_InitializeSidedef(sd);
1200
1201 isfrontside = sd->line->sidenum[0] == i;
1202
1203 // Repeat count for midtexture
1204 if (((sd->line->flags & (ML_TWOSIDED|ML_EFFECT5)) == (ML_TWOSIDED|ML_EFFECT5))
1205 && !(sd->special >= 300 && sd->special < 500)) // exempt linedef exec specials
1206 {
1207 sd->repeatcnt = (INT16)(((UINT16)textureoffset) >> 12);
1208 sd->textureoffset = (((UINT16)textureoffset) & 2047) << FRACBITS;
1209 }
1210 else
1211 {
1212 sd->repeatcnt = 0;
1213 sd->textureoffset = textureoffset << FRACBITS;
1214 }
1215 sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
1216
1217 P_SetSidedefSector(i, SHORT(msd->sector));
1218
1219 // Special info stored in texture fields!
1220 switch (sd->special)
1221 {
1222 case 606: //SoM: 4/4/2000: Just colormap transfer
1223 case 447: // Change colormap of tagged sectors! -- Monster Iestyn 14/06/18
1224 case 455: // Fade colormaps! mazmazz 9/12/2018 (:flag_us:)
1225 // SoM: R_CreateColormap will only create a colormap in software mode...
1226 // Perhaps we should just call it instead of doing the calculations here.
1227 if (!udmf)
1228 {
1229 sd->colormap_data = R_CreateColormapFromLinedef(msd->toptexture, msd->midtexture, msd->bottomtexture);
1230 sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
1231 }
1232 break;
1233
1234 case 413: // Change music
1235 {
1236 char process[8+1];
1237
1238 sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
1239 if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
1240 {
1241 M_Memcpy(process,msd->bottomtexture,8);
1242 process[8] = '\0';
1243 sd->bottomtexture = get_number(process);
1244 }
1245
1246 if (!(msd->midtexture[0] == '-' && msd->midtexture[1] == '\0') || msd->midtexture[1] != '\0')
1247 {
1248 M_Memcpy(process,msd->midtexture,8);
1249 process[8] = '\0';
1250 sd->midtexture = get_number(process);
1251 }
1252
1253 sd->text = Z_Malloc(7, PU_LEVEL, NULL);
1254 if (isfrontside && !(msd->toptexture[0] == '-' && msd->toptexture[1] == '\0'))
1255 {
1256 M_Memcpy(process,msd->toptexture,8);
1257 process[8] = '\0';
1258
1259 // If they type in O_ or D_ and their music name, just shrug,
1260 // then copy the rest instead.
1261 if ((process[0] == 'O' || process[0] == 'D') && process[7])
1262 M_Memcpy(sd->text, process+2, 6);
1263 else // Assume it's a proper music name.
1264 M_Memcpy(sd->text, process, 6);
1265 sd->text[6] = 0;
1266 }
1267 else
1268 sd->text[0] = 0;
1269 break;
1270 }
1271
1272 case 4: // Speed pad parameters
1273 case 414: // Play SFX
1274 {
1275 sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
1276 if (msd->toptexture[0] != '-' || msd->toptexture[1] != '\0')
1277 {
1278 char process[8+1];
1279 M_Memcpy(process,msd->toptexture,8);
1280 process[8] = '\0';
1281 sd->toptexture = get_number(process);
1282 }
1283 break;
1284 }
1285
1286 case 9: // Mace parameters
1287 case 14: // Bustable block parameters
1288 case 15: // Fan particle spawner parameters
1289 case 334: // Trigger linedef executor: Object dye - Continuous
1290 case 335: // Trigger linedef executor: Object dye - Each time
1291 case 336: // Trigger linedef executor: Object dye - Once
1292 case 425: // Calls P_SetMobjState on calling mobj
1293 case 434: // Custom Power
1294 case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors
1295 case 461: // Spawns an object on the map based on texture offsets
1296 case 463: // Colorizes an object
1297 {
1298 char process[8*3+1];
1299 memset(process,0,8*3+1);
1300 sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
1301 if (msd->toptexture[0] == '-' && msd->toptexture[1] == '\0')
1302 break;
1303 else
1304 M_Memcpy(process,msd->toptexture,8);
1305 if (msd->midtexture[0] != '-' || msd->midtexture[1] != '\0')
1306 M_Memcpy(process+strlen(process), msd->midtexture, 8);
1307 if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
1308 M_Memcpy(process+strlen(process), msd->bottomtexture, 8);
1309 sd->toptexture = get_number(process);
1310 break;
1311 }
1312
1313 case 331: // Trigger linedef executor: Skin - Continuous
1314 case 332: // Trigger linedef executor: Skin - Each time
1315 case 333: // Trigger linedef executor: Skin - Once
1316 case 443: // Calls a named Lua function
1317 case 459: // Control text prompt (named tag)
1318 {
1319 char process[8*3+1];
1320 memset(process,0,8*3+1);
1321 sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
1322 if (msd->toptexture[0] == '-' && msd->toptexture[1] == '\0')
1323 break;
1324 else
1325 M_Memcpy(process,msd->toptexture,8);
1326 if (msd->midtexture[0] != '-' || msd->midtexture[1] != '\0')
1327 M_Memcpy(process+strlen(process), msd->midtexture, 8);
1328 if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
1329 M_Memcpy(process+strlen(process), msd->bottomtexture, 8);
1330 sd->text = Z_Malloc(strlen(process)+1, PU_LEVEL, NULL);
1331 M_Memcpy(sd->text, process, strlen(process)+1);
1332 break;
1333 }
1334
1335 case 259: // Custom FOF
1336 if (!isfrontside)
1337 {
1338 if ((msd->toptexture[0] >= '0' && msd->toptexture[0] <= '9')
1339 || (msd->toptexture[0] >= 'A' && msd->toptexture[0] <= 'F'))
1340 sd->toptexture = axtoi(msd->toptexture);
1341 else
1342 I_Error("Custom FOF (line id %s) needs a value in the linedef's back side upper texture field.", sizeu1(sd->line - lines));
1343
1344 sd->midtexture = R_TextureNumForName(msd->midtexture);
1345 sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
1346 break;
1347 }
1348 // FALLTHRU
1349 default: // normal cases
1350 if (msd->toptexture[0] == '#')
1351 {
1352 char *col = msd->toptexture;
1353 sd->toptexture = sd->bottomtexture =
1354 ((col[1]-'0')*100 + (col[2]-'0')*10 + col[3]-'0') + 1;
1355 sd->midtexture = R_TextureNumForName(msd->midtexture);
1356 }
1357 else
1358 {
1359 sd->midtexture = R_TextureNumForName(msd->midtexture);
1360 sd->toptexture = R_TextureNumForName(msd->toptexture);
1361 sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
1362 }
1363 break;
1364 }
1365 }
1366 }
1367
P_LoadThings(UINT8 * data)1368 static void P_LoadThings(UINT8 *data)
1369 {
1370 mapthing_t *mt;
1371 size_t i;
1372
1373 for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
1374 {
1375 mt->x = READINT16(data);
1376 mt->y = READINT16(data);
1377
1378 mt->angle = READINT16(data);
1379 mt->type = READUINT16(data);
1380 mt->options = READUINT16(data);
1381 mt->extrainfo = (UINT8)(mt->type >> 12);
1382 Tag_FSet(&mt->tags, 0);
1383 mt->scale = FRACUNIT;
1384 memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
1385 memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
1386 mt->pitch = mt->roll = 0;
1387
1388 mt->type &= 4095;
1389
1390 if (mt->type == 1705 || (mt->type == 750 && mt->extrainfo))
1391 mt->z = mt->options; // NiGHTS Hoops use the full flags bits to set the height.
1392 else
1393 mt->z = mt->options >> ZSHIFT;
1394
1395 mt->mobj = NULL;
1396 }
1397 }
1398
1399 // Stores positions for relevant map data spread through a TEXTMAP.
1400 UINT32 mapthingsPos[UINT16_MAX];
1401 UINT32 linesPos[UINT16_MAX];
1402 UINT32 sidesPos[UINT16_MAX];
1403 UINT32 vertexesPos[UINT16_MAX];
1404 UINT32 sectorsPos[UINT16_MAX];
1405
1406 // Determine total amount of map data in TEXTMAP.
TextmapCount(UINT8 * data,size_t size)1407 static boolean TextmapCount(UINT8 *data, size_t size)
1408 {
1409 char *tkn = M_GetToken((char *)data);
1410 UINT8 brackets = 0;
1411
1412 nummapthings = 0;
1413 numlines = 0;
1414 numsides = 0;
1415 numvertexes = 0;
1416 numsectors = 0;
1417
1418 // Look for namespace at the beginning.
1419 if (!fastcmp(tkn, "namespace"))
1420 {
1421 Z_Free(tkn);
1422 CONS_Alert(CONS_ERROR, "No namespace at beginning of lump!\n");
1423 return false;
1424 }
1425 Z_Free(tkn);
1426
1427 // Check if namespace is valid.
1428 tkn = M_GetToken(NULL);
1429 if (!fastcmp(tkn, "srb2"))
1430 CONS_Alert(CONS_WARNING, "Invalid namespace '%s', only 'srb2' is supported.\n", tkn);
1431 Z_Free(tkn);
1432
1433 tkn = M_GetToken(NULL);
1434 while (tkn && M_GetTokenPos() < size)
1435 {
1436 // Avoid anything inside bracketed stuff, only look for external keywords.
1437 if (brackets)
1438 {
1439 if (fastcmp(tkn, "}"))
1440 brackets--;
1441 }
1442 else if (fastcmp(tkn, "{"))
1443 brackets++;
1444 // Check for valid fields.
1445 else if (fastcmp(tkn, "thing"))
1446 mapthingsPos[nummapthings++] = M_GetTokenPos();
1447 else if (fastcmp(tkn, "linedef"))
1448 linesPos[numlines++] = M_GetTokenPos();
1449 else if (fastcmp(tkn, "sidedef"))
1450 sidesPos[numsides++] = M_GetTokenPos();
1451 else if (fastcmp(tkn, "vertex"))
1452 vertexesPos[numvertexes++] = M_GetTokenPos();
1453 else if (fastcmp(tkn, "sector"))
1454 sectorsPos[numsectors++] = M_GetTokenPos();
1455 else
1456 CONS_Alert(CONS_NOTICE, "Unknown field '%s'.\n", tkn);
1457
1458 Z_Free(tkn);
1459 tkn = M_GetToken(NULL);
1460 }
1461
1462 Z_Free(tkn);
1463
1464 if (brackets)
1465 {
1466 CONS_Alert(CONS_ERROR, "Unclosed brackets detected in textmap lump.\n");
1467 return false;
1468 }
1469
1470 return true;
1471 }
1472
ParseTextmapVertexParameter(UINT32 i,char * param,char * val)1473 static void ParseTextmapVertexParameter(UINT32 i, char *param, char *val)
1474 {
1475 if (fastcmp(param, "x"))
1476 vertexes[i].x = FLOAT_TO_FIXED(atof(val));
1477 else if (fastcmp(param, "y"))
1478 vertexes[i].y = FLOAT_TO_FIXED(atof(val));
1479 else if (fastcmp(param, "zfloor"))
1480 {
1481 vertexes[i].floorz = FLOAT_TO_FIXED(atof(val));
1482 vertexes[i].floorzset = true;
1483 }
1484 else if (fastcmp(param, "zceiling"))
1485 {
1486 vertexes[i].ceilingz = FLOAT_TO_FIXED(atof(val));
1487 vertexes[i].ceilingzset = true;
1488 }
1489 }
1490
1491 typedef struct textmap_colormap_s {
1492 boolean used;
1493 INT32 lightcolor;
1494 UINT8 lightalpha;
1495 INT32 fadecolor;
1496 UINT8 fadealpha;
1497 UINT8 fadestart;
1498 UINT8 fadeend;
1499 UINT8 flags;
1500 } textmap_colormap_t;
1501
1502 textmap_colormap_t textmap_colormap = { false, 0, 25, 0, 25, 0, 31, 0 };
1503
ParseTextmapSectorParameter(UINT32 i,char * param,char * val)1504 static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val)
1505 {
1506 if (fastcmp(param, "heightfloor"))
1507 sectors[i].floorheight = atol(val) << FRACBITS;
1508 else if (fastcmp(param, "heightceiling"))
1509 sectors[i].ceilingheight = atol(val) << FRACBITS;
1510 if (fastcmp(param, "texturefloor"))
1511 sectors[i].floorpic = P_AddLevelFlat(val, foundflats);
1512 else if (fastcmp(param, "textureceiling"))
1513 sectors[i].ceilingpic = P_AddLevelFlat(val, foundflats);
1514 else if (fastcmp(param, "lightlevel"))
1515 sectors[i].lightlevel = atol(val);
1516 else if (fastcmp(param, "special"))
1517 sectors[i].special = atol(val);
1518 else if (fastcmp(param, "id"))
1519 Tag_FSet(§ors[i].tags, atol(val));
1520 else if (fastcmp(param, "moreids"))
1521 {
1522 char* id = val;
1523 while (id)
1524 {
1525 Tag_Add(§ors[i].tags, atol(id));
1526 if ((id = strchr(id, ' ')))
1527 id++;
1528 }
1529 }
1530 else if (fastcmp(param, "xpanningfloor"))
1531 sectors[i].floor_xoffs = FLOAT_TO_FIXED(atof(val));
1532 else if (fastcmp(param, "ypanningfloor"))
1533 sectors[i].floor_yoffs = FLOAT_TO_FIXED(atof(val));
1534 else if (fastcmp(param, "xpanningceiling"))
1535 sectors[i].ceiling_xoffs = FLOAT_TO_FIXED(atof(val));
1536 else if (fastcmp(param, "ypanningceiling"))
1537 sectors[i].ceiling_yoffs = FLOAT_TO_FIXED(atof(val));
1538 else if (fastcmp(param, "rotationfloor"))
1539 sectors[i].floorpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
1540 else if (fastcmp(param, "rotationceiling"))
1541 sectors[i].ceilingpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
1542 else if (fastcmp(param, "lightcolor"))
1543 {
1544 textmap_colormap.used = true;
1545 textmap_colormap.lightcolor = atol(val);
1546 }
1547 else if (fastcmp(param, "lightalpha"))
1548 {
1549 textmap_colormap.used = true;
1550 textmap_colormap.lightalpha = atol(val);
1551 }
1552 else if (fastcmp(param, "fadecolor"))
1553 {
1554 textmap_colormap.used = true;
1555 textmap_colormap.fadecolor = atol(val);
1556 }
1557 else if (fastcmp(param, "fadealpha"))
1558 {
1559 textmap_colormap.used = true;
1560 textmap_colormap.fadealpha = atol(val);
1561 }
1562 else if (fastcmp(param, "fadestart"))
1563 {
1564 textmap_colormap.used = true;
1565 textmap_colormap.fadestart = atol(val);
1566 }
1567 else if (fastcmp(param, "fadeend"))
1568 {
1569 textmap_colormap.used = true;
1570 textmap_colormap.fadeend = atol(val);
1571 }
1572 else if (fastcmp(param, "colormapfog") && fastcmp("true", val))
1573 {
1574 textmap_colormap.used = true;
1575 textmap_colormap.flags |= CMF_FOG;
1576 }
1577 else if (fastcmp(param, "colormapfadesprites") && fastcmp("true", val))
1578 {
1579 textmap_colormap.used = true;
1580 textmap_colormap.flags |= CMF_FADEFULLBRIGHTSPRITES;
1581 }
1582 else if (fastcmp(param, "colormapprotected") && fastcmp("true", val))
1583 sectors[i].colormap_protected = true;
1584 }
1585
ParseTextmapSidedefParameter(UINT32 i,char * param,char * val)1586 static void ParseTextmapSidedefParameter(UINT32 i, char *param, char *val)
1587 {
1588 if (fastcmp(param, "offsetx"))
1589 sides[i].textureoffset = atol(val)<<FRACBITS;
1590 else if (fastcmp(param, "offsety"))
1591 sides[i].rowoffset = atol(val)<<FRACBITS;
1592 else if (fastcmp(param, "texturetop"))
1593 sides[i].toptexture = R_TextureNumForName(val);
1594 else if (fastcmp(param, "texturebottom"))
1595 sides[i].bottomtexture = R_TextureNumForName(val);
1596 else if (fastcmp(param, "texturemiddle"))
1597 sides[i].midtexture = R_TextureNumForName(val);
1598 else if (fastcmp(param, "sector"))
1599 P_SetSidedefSector(i, atol(val));
1600 else if (fastcmp(param, "repeatcnt"))
1601 sides[i].repeatcnt = atol(val);
1602 }
1603
ParseTextmapLinedefParameter(UINT32 i,char * param,char * val)1604 static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val)
1605 {
1606 if (fastcmp(param, "id"))
1607 Tag_FSet(&lines[i].tags, atol(val));
1608 else if (fastcmp(param, "moreids"))
1609 {
1610 char* id = val;
1611 while (id)
1612 {
1613 Tag_Add(&lines[i].tags, atol(id));
1614 if ((id = strchr(id, ' ')))
1615 id++;
1616 }
1617 }
1618 else if (fastcmp(param, "special"))
1619 lines[i].special = atol(val);
1620 else if (fastcmp(param, "v1"))
1621 P_SetLinedefV1(i, atol(val));
1622 else if (fastcmp(param, "v2"))
1623 P_SetLinedefV2(i, atol(val));
1624 else if (strlen(param) == 7 && fastncmp(param, "arg", 3) && fastncmp(param + 4, "str", 3))
1625 {
1626 size_t argnum = param[3] - '0';
1627 if (argnum >= NUMLINESTRINGARGS)
1628 return;
1629 lines[i].stringargs[argnum] = Z_Malloc(strlen(val) + 1, PU_LEVEL, NULL);
1630 M_Memcpy(lines[i].stringargs[argnum], val, strlen(val) + 1);
1631 }
1632 else if (fastncmp(param, "arg", 3) && strlen(param) > 3)
1633 {
1634 size_t argnum = atol(param + 3);
1635 if (argnum >= NUMLINEARGS)
1636 return;
1637 lines[i].args[argnum] = atol(val);
1638 }
1639 else if (fastcmp(param, "sidefront"))
1640 lines[i].sidenum[0] = atol(val);
1641 else if (fastcmp(param, "sideback"))
1642 lines[i].sidenum[1] = atol(val);
1643 else if (fastcmp(param, "alpha"))
1644 lines[i].alpha = FLOAT_TO_FIXED(atof(val));
1645 else if (fastcmp(param, "executordelay"))
1646 lines[i].executordelay = atol(val);
1647
1648 // Flags
1649 else if (fastcmp(param, "blocking") && fastcmp("true", val))
1650 lines[i].flags |= ML_IMPASSIBLE;
1651 else if (fastcmp(param, "blockmonsters") && fastcmp("true", val))
1652 lines[i].flags |= ML_BLOCKMONSTERS;
1653 else if (fastcmp(param, "twosided") && fastcmp("true", val))
1654 lines[i].flags |= ML_TWOSIDED;
1655 else if (fastcmp(param, "dontpegtop") && fastcmp("true", val))
1656 lines[i].flags |= ML_DONTPEGTOP;
1657 else if (fastcmp(param, "dontpegbottom") && fastcmp("true", val))
1658 lines[i].flags |= ML_DONTPEGBOTTOM;
1659 else if (fastcmp(param, "skewtd") && fastcmp("true", val))
1660 lines[i].flags |= ML_EFFECT1;
1661 else if (fastcmp(param, "noclimb") && fastcmp("true", val))
1662 lines[i].flags |= ML_NOCLIMB;
1663 else if (fastcmp(param, "noskew") && fastcmp("true", val))
1664 lines[i].flags |= ML_EFFECT2;
1665 else if (fastcmp(param, "midpeg") && fastcmp("true", val))
1666 lines[i].flags |= ML_EFFECT3;
1667 else if (fastcmp(param, "midsolid") && fastcmp("true", val))
1668 lines[i].flags |= ML_EFFECT4;
1669 else if (fastcmp(param, "wrapmidtex") && fastcmp("true", val))
1670 lines[i].flags |= ML_EFFECT5;
1671 else if (fastcmp(param, "effect6") && fastcmp("true", val))
1672 lines[i].flags |= ML_EFFECT6;
1673 else if (fastcmp(param, "nonet") && fastcmp("true", val))
1674 lines[i].flags |= ML_NONET;
1675 else if (fastcmp(param, "netonly") && fastcmp("true", val))
1676 lines[i].flags |= ML_NETONLY;
1677 else if (fastcmp(param, "bouncy") && fastcmp("true", val))
1678 lines[i].flags |= ML_BOUNCY;
1679 else if (fastcmp(param, "transfer") && fastcmp("true", val))
1680 lines[i].flags |= ML_TFERLINE;
1681 }
1682
ParseTextmapThingParameter(UINT32 i,char * param,char * val)1683 static void ParseTextmapThingParameter(UINT32 i, char *param, char *val)
1684 {
1685 if (fastcmp(param, "id"))
1686 Tag_FSet(&mapthings[i].tags, atol(val));
1687 else if (fastcmp(param, "moreids"))
1688 {
1689 char* id = val;
1690 while (id)
1691 {
1692 Tag_Add(&mapthings[i].tags, atol(id));
1693 if ((id = strchr(id, ' ')))
1694 id++;
1695 }
1696 }
1697 else if (fastcmp(param, "x"))
1698 mapthings[i].x = atol(val);
1699 else if (fastcmp(param, "y"))
1700 mapthings[i].y = atol(val);
1701 else if (fastcmp(param, "height"))
1702 mapthings[i].z = atol(val);
1703 else if (fastcmp(param, "angle"))
1704 mapthings[i].angle = atol(val);
1705 else if (fastcmp(param, "pitch"))
1706 mapthings[i].pitch = atol(val);
1707 else if (fastcmp(param, "roll"))
1708 mapthings[i].roll = atol(val);
1709 else if (fastcmp(param, "type"))
1710 mapthings[i].type = atol(val);
1711 else if (fastcmp(param, "scale") || fastcmp(param, "scalex") || fastcmp(param, "scaley"))
1712 mapthings[i].scale = FLOAT_TO_FIXED(atof(val));
1713 // Flags
1714 else if (fastcmp(param, "extra") && fastcmp("true", val))
1715 mapthings[i].options |= MTF_EXTRA;
1716 else if (fastcmp(param, "flip") && fastcmp("true", val))
1717 mapthings[i].options |= MTF_OBJECTFLIP;
1718 else if (fastcmp(param, "objectspecial") && fastcmp("true", val))
1719 mapthings[i].options |= MTF_OBJECTSPECIAL;
1720 else if (fastcmp(param, "ambush") && fastcmp("true", val))
1721 mapthings[i].options |= MTF_AMBUSH;
1722
1723 else if (strlen(param) == 7 && fastncmp(param, "arg", 3) && fastncmp(param + 4, "str", 3))
1724 {
1725 size_t argnum = param[3] - '0';
1726 if (argnum >= NUMMAPTHINGSTRINGARGS)
1727 return;
1728 mapthings[i].stringargs[argnum] = Z_Malloc(strlen(val) + 1, PU_LEVEL, NULL);
1729 M_Memcpy(mapthings[i].stringargs[argnum], val, strlen(val) + 1);
1730 }
1731 else if (fastncmp(param, "arg", 3) && strlen(param) > 3)
1732 {
1733 size_t argnum = atol(param + 3);
1734 if (argnum >= NUMMAPTHINGARGS)
1735 return;
1736 mapthings[i].args[argnum] = atol(val);
1737 }
1738 }
1739
1740 /** From a given position table, run a specified parser function through a {}-encapsuled text.
1741 *
1742 * \param Position of the data to parse, in the textmap.
1743 * \param Structure number (mapthings, sectors, ...).
1744 * \param Parser function pointer.
1745 */
TextmapParse(UINT32 dataPos,size_t num,void (* parser)(UINT32,char *,char *))1746 static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char *, char *))
1747 {
1748 char *param, *val;
1749
1750 M_SetTokenPos(dataPos);
1751 param = M_GetToken(NULL);
1752 if (!fastcmp(param, "{"))
1753 {
1754 Z_Free(param);
1755 CONS_Alert(CONS_WARNING, "Invalid UDMF data capsule!\n");
1756 return;
1757 }
1758 Z_Free(param);
1759
1760 while (true)
1761 {
1762 param = M_GetToken(NULL);
1763 if (fastcmp(param, "}"))
1764 {
1765 Z_Free(param);
1766 break;
1767 }
1768 val = M_GetToken(NULL);
1769 parser(num, param, val);
1770 Z_Free(param);
1771 Z_Free(val);
1772 }
1773 }
1774
1775 /** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
1776 */
TextmapFixFlatOffsets(sector_t * sec)1777 static void TextmapFixFlatOffsets(sector_t *sec)
1778 {
1779 if (sec->floorpic_angle)
1780 {
1781 fixed_t pc = FINECOSINE(sec->floorpic_angle>>ANGLETOFINESHIFT);
1782 fixed_t ps = FINESINE (sec->floorpic_angle>>ANGLETOFINESHIFT);
1783 fixed_t xoffs = sec->floor_xoffs;
1784 fixed_t yoffs = sec->floor_yoffs;
1785 sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
1786 sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
1787 }
1788
1789 if (sec->ceilingpic_angle)
1790 {
1791 fixed_t pc = FINECOSINE(sec->ceilingpic_angle>>ANGLETOFINESHIFT);
1792 fixed_t ps = FINESINE (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
1793 fixed_t xoffs = sec->ceiling_xoffs;
1794 fixed_t yoffs = sec->ceiling_yoffs;
1795 sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
1796 sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
1797 }
1798 }
1799
P_ColorToRGBA(INT32 color,UINT8 alpha)1800 static INT32 P_ColorToRGBA(INT32 color, UINT8 alpha)
1801 {
1802 UINT8 r = (color >> 16) & 0xFF;
1803 UINT8 g = (color >> 8) & 0xFF;
1804 UINT8 b = color & 0xFF;
1805 return R_PutRgbaRGBA(r, g, b, alpha);
1806 }
1807
1808 /** Loads the textmap data, after obtaining the elements count and allocating their respective space.
1809 */
P_LoadTextmap(void)1810 static void P_LoadTextmap(void)
1811 {
1812 UINT32 i;
1813
1814 vertex_t *vt;
1815 sector_t *sc;
1816 line_t *ld;
1817 side_t *sd;
1818 mapthing_t *mt;
1819
1820 CONS_Alert(CONS_NOTICE, "UDMF support is still a work-in-progress; its specs and features are prone to change until it is fully implemented.\n");
1821
1822 /// Given the UDMF specs, some fields are given a default value.
1823 /// If an element's field has a default value set, it is omitted
1824 /// from the textmap, and therefore we have to account for it by
1825 /// preemptively setting that value beforehand.
1826
1827 for (i = 0, vt = vertexes; i < numvertexes; i++, vt++)
1828 {
1829 // Defaults.
1830 vt->x = vt->y = INT32_MAX;
1831 vt->floorzset = vt->ceilingzset = false;
1832 vt->floorz = vt->ceilingz = 0;
1833
1834 TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter);
1835
1836 if (vt->x == INT32_MAX)
1837 I_Error("P_LoadTextmap: vertex %s has no x value set!\n", sizeu1(i));
1838 if (vt->y == INT32_MAX)
1839 I_Error("P_LoadTextmap: vertex %s has no y value set!\n", sizeu1(i));
1840 }
1841
1842 for (i = 0, sc = sectors; i < numsectors; i++, sc++)
1843 {
1844 // Defaults.
1845 sc->floorheight = 0;
1846 sc->ceilingheight = 0;
1847
1848 sc->floorpic = 0;
1849 sc->ceilingpic = 0;
1850
1851 sc->lightlevel = 255;
1852
1853 sc->special = 0;
1854 Tag_FSet(&sc->tags, 0);
1855
1856 sc->floor_xoffs = sc->floor_yoffs = 0;
1857 sc->ceiling_xoffs = sc->ceiling_yoffs = 0;
1858
1859 sc->floorpic_angle = sc->ceilingpic_angle = 0;
1860
1861 sc->colormap_protected = false;
1862
1863 textmap_colormap.used = false;
1864 textmap_colormap.lightcolor = 0;
1865 textmap_colormap.lightalpha = 25;
1866 textmap_colormap.fadecolor = 0;
1867 textmap_colormap.fadealpha = 25;
1868 textmap_colormap.fadestart = 0;
1869 textmap_colormap.fadeend = 31;
1870 textmap_colormap.flags = 0;
1871 TextmapParse(sectorsPos[i], i, ParseTextmapSectorParameter);
1872
1873 P_InitializeSector(sc);
1874 if (textmap_colormap.used)
1875 {
1876 INT32 rgba = P_ColorToRGBA(textmap_colormap.lightcolor, textmap_colormap.lightalpha);
1877 INT32 fadergba = P_ColorToRGBA(textmap_colormap.fadecolor, textmap_colormap.fadealpha);
1878 sc->extra_colormap = sc->spawn_extra_colormap = R_CreateColormap(rgba, fadergba, textmap_colormap.fadestart, textmap_colormap.fadeend, textmap_colormap.flags);
1879 }
1880 TextmapFixFlatOffsets(sc);
1881 }
1882
1883 for (i = 0, ld = lines; i < numlines; i++, ld++)
1884 {
1885 // Defaults.
1886 ld->v1 = ld->v2 = NULL;
1887 ld->flags = 0;
1888 ld->special = 0;
1889 Tag_FSet(&ld->tags, 0);
1890
1891 memset(ld->args, 0, NUMLINEARGS*sizeof(*ld->args));
1892 memset(ld->stringargs, 0x00, NUMLINESTRINGARGS*sizeof(*ld->stringargs));
1893 ld->alpha = FRACUNIT;
1894 ld->executordelay = 0;
1895 ld->sidenum[0] = 0xffff;
1896 ld->sidenum[1] = 0xffff;
1897
1898 TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
1899
1900 if (!ld->v1)
1901 I_Error("P_LoadTextmap: linedef %s has no v1 value set!\n", sizeu1(i));
1902 if (!ld->v2)
1903 I_Error("P_LoadTextmap: linedef %s has no v2 value set!\n", sizeu1(i));
1904 if (ld->sidenum[0] == 0xffff)
1905 I_Error("P_LoadTextmap: linedef %s has no sidefront value set!\n", sizeu1(i));
1906
1907 P_InitializeLinedef(ld);
1908 }
1909
1910 for (i = 0, sd = sides; i < numsides; i++, sd++)
1911 {
1912 // Defaults.
1913 sd->textureoffset = 0;
1914 sd->rowoffset = 0;
1915 sd->toptexture = R_TextureNumForName("-");
1916 sd->midtexture = R_TextureNumForName("-");
1917 sd->bottomtexture = R_TextureNumForName("-");
1918 sd->sector = NULL;
1919 sd->repeatcnt = 0;
1920
1921 TextmapParse(sidesPos[i], i, ParseTextmapSidedefParameter);
1922
1923 if (!sd->sector)
1924 I_Error("P_LoadTextmap: sidedef %s has no sector value set!\n", sizeu1(i));
1925
1926 P_InitializeSidedef(sd);
1927 }
1928
1929 for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
1930 {
1931 // Defaults.
1932 mt->x = mt->y = 0;
1933 mt->angle = mt->pitch = mt->roll = 0;
1934 mt->type = 0;
1935 mt->options = 0;
1936 mt->z = 0;
1937 mt->extrainfo = 0;
1938 Tag_FSet(&mt->tags, 0);
1939 mt->scale = FRACUNIT;
1940 memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
1941 memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
1942 mt->mobj = NULL;
1943
1944 TextmapParse(mapthingsPos[i], i, ParseTextmapThingParameter);
1945 }
1946 }
1947
P_ProcessLinedefsAfterSidedefs(void)1948 static void P_ProcessLinedefsAfterSidedefs(void)
1949 {
1950 size_t i = numlines;
1951 register line_t *ld = lines;
1952 for (; i--; ld++)
1953 {
1954 ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here
1955 ld->backsector = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0;
1956
1957 switch (ld->special)
1958 {
1959 // Compile linedef 'text' from both sidedefs 'text' for appropriate specials.
1960 case 331: // Trigger linedef executor: Skin - Continuous
1961 case 332: // Trigger linedef executor: Skin - Each time
1962 case 333: // Trigger linedef executor: Skin - Once
1963 case 443: // Calls a named Lua function
1964 if (sides[ld->sidenum[0]].text)
1965 {
1966 size_t len = strlen(sides[ld->sidenum[0]].text) + 1;
1967 if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
1968 len += strlen(sides[ld->sidenum[1]].text);
1969 ld->text = Z_Malloc(len, PU_LEVEL, NULL);
1970 M_Memcpy(ld->text, sides[ld->sidenum[0]].text, strlen(sides[ld->sidenum[0]].text) + 1);
1971 if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
1972 M_Memcpy(ld->text + strlen(ld->text) + 1, sides[ld->sidenum[1]].text, strlen(sides[ld->sidenum[1]].text) + 1);
1973 }
1974 break;
1975 case 447: // Change colormap
1976 case 455: // Fade colormap
1977 if (udmf)
1978 break;
1979 if (ld->flags & ML_DONTPEGBOTTOM) // alternate alpha (by texture offsets)
1980 {
1981 extracolormap_t *exc = R_CopyColormap(sides[ld->sidenum[0]].colormap_data, false);
1982 INT16 alpha = max(min(sides[ld->sidenum[0]].textureoffset >> FRACBITS, 25), -25);
1983 INT16 fadealpha = max(min(sides[ld->sidenum[0]].rowoffset >> FRACBITS, 25), -25);
1984
1985 // If alpha is negative, set "subtract alpha" flag and store absolute value
1986 if (alpha < 0)
1987 {
1988 alpha *= -1;
1989 ld->args[2] |= TMCF_SUBLIGHTA;
1990 }
1991 if (fadealpha < 0)
1992 {
1993 fadealpha *= -1;
1994 ld->args[2] |= TMCF_SUBFADEA;
1995 }
1996
1997 exc->rgba = R_GetRgbaRGB(exc->rgba) + R_PutRgbaA(alpha);
1998 exc->fadergba = R_GetRgbaRGB(exc->fadergba) + R_PutRgbaA(fadealpha);
1999
2000 if (!(sides[ld->sidenum[0]].colormap_data = R_GetColormapFromList(exc)))
2001 {
2002 exc->colormap = R_CreateLightTable(exc);
2003 R_AddColormapToList(exc);
2004 sides[ld->sidenum[0]].colormap_data = exc;
2005 }
2006 else
2007 Z_Free(exc);
2008 }
2009 break;
2010 }
2011 }
2012 }
2013
P_LoadMapData(const virtres_t * virt)2014 static boolean P_LoadMapData(const virtres_t *virt)
2015 {
2016 virtlump_t *virtvertexes = NULL, *virtsectors = NULL, *virtsidedefs = NULL, *virtlinedefs = NULL, *virtthings = NULL;
2017
2018 // Count map data.
2019 if (udmf) // Count how many entries for each type we got in textmap.
2020 {
2021 virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
2022 if (!TextmapCount(textmap->data, textmap->size))
2023 return false;
2024 }
2025 else
2026 {
2027 virtthings = vres_Find(virt, "THINGS");
2028 virtvertexes = vres_Find(virt, "VERTEXES");
2029 virtsectors = vres_Find(virt, "SECTORS");
2030 virtsidedefs = vres_Find(virt, "SIDEDEFS");
2031 virtlinedefs = vres_Find(virt, "LINEDEFS");
2032
2033 if (!virtthings)
2034 I_Error("THINGS lump not found");
2035 if (!virtvertexes)
2036 I_Error("VERTEXES lump not found");
2037 if (!virtsectors)
2038 I_Error("SECTORS lump not found");
2039 if (!virtsidedefs)
2040 I_Error("SIDEDEFS lump not found");
2041 if (!virtlinedefs)
2042 I_Error("LINEDEFS lump not found");
2043
2044 // Traditional doom map format just assumes the number of elements from the lump sizes.
2045 numvertexes = virtvertexes->size / sizeof (mapvertex_t);
2046 numsectors = virtsectors->size / sizeof (mapsector_t);
2047 numsides = virtsidedefs->size / sizeof (mapsidedef_t);
2048 numlines = virtlinedefs->size / sizeof (maplinedef_t);
2049 nummapthings = virtthings->size / (5 * sizeof (INT16));
2050 }
2051
2052 if (numvertexes <= 0)
2053 I_Error("Level has no vertices");
2054 if (numsectors <= 0)
2055 I_Error("Level has no sectors");
2056 if (numsides <= 0)
2057 I_Error("Level has no sidedefs");
2058 if (numlines <= 0)
2059 I_Error("Level has no linedefs");
2060
2061 vertexes = Z_Calloc(numvertexes * sizeof (*vertexes), PU_LEVEL, NULL);
2062 sectors = Z_Calloc(numsectors * sizeof (*sectors), PU_LEVEL, NULL);
2063 sides = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
2064 lines = Z_Calloc(numlines * sizeof (*lines), PU_LEVEL, NULL);
2065 mapthings = Z_Calloc(nummapthings * sizeof (*mapthings), PU_LEVEL, NULL);
2066
2067 // Allocate a big chunk of memory as big as our MAXLEVELFLATS limit.
2068 //Fab : FIXME: allocate for whatever number of flats - 512 different flats per level should be plenty
2069 foundflats = calloc(MAXLEVELFLATS, sizeof (*foundflats));
2070 if (foundflats == NULL)
2071 I_Error("Ran out of memory while loading sectors\n");
2072
2073 numlevelflats = 0;
2074
2075 // Load map data.
2076 if (udmf)
2077 P_LoadTextmap();
2078 else
2079 {
2080 P_LoadVertices(virtvertexes->data);
2081 P_LoadSectors(virtsectors->data);
2082 P_LoadLinedefs(virtlinedefs->data);
2083 P_LoadSidedefs(virtsidedefs->data);
2084 P_LoadThings(virtthings->data);
2085 }
2086
2087 P_ProcessLinedefsAfterSidedefs();
2088
2089 R_ClearTextureNumCache(true);
2090
2091 // set the sky flat num
2092 skyflatnum = P_AddLevelFlat(SKYFLATNAME, foundflats);
2093
2094 // copy table for global usage
2095 levelflats = M_Memcpy(Z_Calloc(numlevelflats * sizeof (*levelflats), PU_LEVEL, NULL), foundflats, numlevelflats * sizeof (levelflat_t));
2096 free(foundflats);
2097
2098 // search for animated flats and set up
2099 P_SetupLevelFlatAnims();
2100
2101 return true;
2102 }
2103
P_InitializeSubsector(subsector_t * ss)2104 static void P_InitializeSubsector(subsector_t *ss)
2105 {
2106 ss->sector = NULL;
2107 ss->validcount = 0;
2108 }
2109
P_LoadSubsectors(UINT8 * data)2110 static inline void P_LoadSubsectors(UINT8 *data)
2111 {
2112 mapsubsector_t *ms = (mapsubsector_t*)data;
2113 subsector_t *ss = subsectors;
2114 size_t i;
2115
2116 for (i = 0; i < numsubsectors; i++, ss++, ms++)
2117 {
2118 ss->numlines = SHORT(ms->numsegs);
2119 ss->firstline = SHORT(ms->firstseg);
2120 P_InitializeSubsector(ss);
2121 }
2122 }
2123
P_LoadNodes(UINT8 * data)2124 static void P_LoadNodes(UINT8 *data)
2125 {
2126 UINT8 j, k;
2127 mapnode_t *mn = (mapnode_t*)data;
2128 node_t *no = nodes;
2129 size_t i;
2130
2131 for (i = 0; i < numnodes; i++, no++, mn++)
2132 {
2133 no->x = SHORT(mn->x)<<FRACBITS;
2134 no->y = SHORT(mn->y)<<FRACBITS;
2135 no->dx = SHORT(mn->dx)<<FRACBITS;
2136 no->dy = SHORT(mn->dy)<<FRACBITS;
2137 for (j = 0; j < 2; j++)
2138 {
2139 no->children[j] = SHORT(mn->children[j]);
2140 for (k = 0; k < 4; k++)
2141 no->bbox[j][k] = SHORT(mn->bbox[j][k])<<FRACBITS;
2142 }
2143 }
2144 }
2145
2146 /** Computes the length of a seg in fracunits.
2147 *
2148 * \param seg Seg to compute length for.
2149 * \return Length in fracunits.
2150 */
P_SegLength(seg_t * seg)2151 static fixed_t P_SegLength(seg_t *seg)
2152 {
2153 INT64 dx = (seg->v2->x - seg->v1->x)>>1;
2154 INT64 dy = (seg->v2->y - seg->v1->y)>>1;
2155 return FixedHypot(dx, dy)<<1;
2156 }
2157
2158 #ifdef HWRENDER
2159 /** Computes the length of a seg as a float.
2160 * This is needed for OpenGL.
2161 *
2162 * \param seg Seg to compute length for.
2163 * \return Length as a float.
2164 */
P_SegLengthFloat(seg_t * seg)2165 static inline float P_SegLengthFloat(seg_t *seg)
2166 {
2167 float dx, dy;
2168
2169 // make a vector (start at origin)
2170 dx = FIXED_TO_FLOAT(seg->v2->x - seg->v1->x);
2171 dy = FIXED_TO_FLOAT(seg->v2->y - seg->v1->y);
2172
2173 return (float)hypot(dx, dy);
2174 }
2175 #endif
2176
P_InitializeSeg(seg_t * seg)2177 static void P_InitializeSeg(seg_t *seg)
2178 {
2179 if (seg->linedef)
2180 {
2181 UINT16 side = seg->linedef->sidenum[seg->side];
2182
2183 if (side == 0xffff)
2184 I_Error("P_InitializeSeg: Seg %s refers to side %d of linedef %s, which doesn't exist!\n", sizeu1((size_t)(seg - segs)), seg->side, sizeu1((size_t)(seg->linedef - lines)));
2185
2186 seg->sidedef = &sides[side];
2187
2188 seg->frontsector = seg->sidedef->sector;
2189 seg->backsector = (seg->linedef->flags & ML_TWOSIDED) ? sides[seg->linedef->sidenum[seg->side ^ 1]].sector : NULL;
2190 }
2191
2192 #ifdef HWRENDER
2193 seg->pv1 = seg->pv2 = NULL;
2194
2195 //Hurdler: 04/12/2000: for now, only used in hardware mode
2196 seg->lightmaps = NULL; // list of static lightmap for this seg
2197 #endif
2198
2199 seg->numlights = 0;
2200 seg->rlights = NULL;
2201 seg->polyseg = NULL;
2202 seg->dontrenderme = false;
2203 }
2204
P_LoadSegs(UINT8 * data)2205 static void P_LoadSegs(UINT8 *data)
2206 {
2207 mapseg_t *ms = (mapseg_t*)data;
2208 seg_t *seg = segs;
2209 size_t i;
2210
2211 for (i = 0; i < numsegs; i++, seg++, ms++)
2212 {
2213 seg->v1 = &vertexes[SHORT(ms->v1)];
2214 seg->v2 = &vertexes[SHORT(ms->v2)];
2215
2216 seg->side = SHORT(ms->side);
2217
2218 seg->offset = (SHORT(ms->offset)) << FRACBITS;
2219
2220 seg->angle = (SHORT(ms->angle)) << FRACBITS;
2221
2222 seg->linedef = &lines[SHORT(ms->linedef)];
2223
2224 seg->length = P_SegLength(seg);
2225 #ifdef HWRENDER
2226 seg->flength = (rendermode == render_opengl) ? P_SegLengthFloat(seg) : 0;
2227 #endif
2228
2229 seg->glseg = false;
2230 P_InitializeSeg(seg);
2231 }
2232 }
2233
2234 typedef enum {
2235 NT_DOOM,
2236 NT_XNOD,
2237 NT_ZNOD,
2238 NT_XGLN,
2239 NT_ZGLN,
2240 NT_XGL2,
2241 NT_ZGL2,
2242 NT_XGL3,
2243 NT_ZGL3,
2244 NT_UNSUPPORTED,
2245 NUMNODETYPES
2246 } nodetype_t;
2247
2248 // Find out the BSP format.
P_GetNodetype(const virtres_t * virt,UINT8 ** nodedata)2249 static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata)
2250 {
2251 boolean supported[NUMNODETYPES] = {0};
2252 nodetype_t nodetype = NT_UNSUPPORTED;
2253 char signature[4 + 1];
2254
2255 if (udmf)
2256 {
2257 *nodedata = vres_Find(virt, "ZNODES")->data;
2258 supported[NT_XGLN] = supported[NT_XGL3] = true;
2259 }
2260 else
2261 {
2262 virtlump_t *virtsegs = vres_Find(virt, "SEGS");
2263 virtlump_t *virtssectors;
2264
2265 if (virtsegs && virtsegs->size)
2266 {
2267 *nodedata = vres_Find(virt, "NODES")->data;
2268 return NT_DOOM; // Traditional map format BSP tree.
2269 }
2270
2271 virtssectors = vres_Find(virt, "SSECTORS");
2272
2273 if (virtssectors && virtssectors->size)
2274 { // Possibly GL nodes: NODES ignored, SSECTORS takes precedence as nodes lump, (It is confusing yeah) and has a signature.
2275 *nodedata = virtssectors->data;
2276 supported[NT_XGLN] = supported[NT_ZGLN] = supported[NT_XGL3] = true;
2277 }
2278 else
2279 { // Possibly ZDoom extended nodes: SSECTORS is empty, NODES has a signature.
2280 *nodedata = vres_Find(virt, "NODES")->data;
2281 supported[NT_XNOD] = supported[NT_ZNOD] = true;
2282 }
2283 }
2284
2285 M_Memcpy(signature, *nodedata, 4);
2286 signature[4] = '\0';
2287 (*nodedata) += 4;
2288
2289 if (!strcmp(signature, "XNOD"))
2290 nodetype = NT_XNOD;
2291 else if (!strcmp(signature, "ZNOD"))
2292 nodetype = NT_ZNOD;
2293 else if (!strcmp(signature, "XGLN"))
2294 nodetype = NT_XGLN;
2295 else if (!strcmp(signature, "ZGLN"))
2296 nodetype = NT_ZGLN;
2297 else if (!strcmp(signature, "XGL3"))
2298 nodetype = NT_XGL3;
2299
2300 return supported[nodetype] ? nodetype : NT_UNSUPPORTED;
2301 }
2302
2303 // Extended node formats feature additional vertices; useful for OpenGL, but totally useless in gamelogic.
P_LoadExtraVertices(UINT8 ** data)2304 static boolean P_LoadExtraVertices(UINT8 **data)
2305 {
2306 UINT32 origvrtx = READUINT32((*data));
2307 UINT32 xtrvrtx = READUINT32((*data));
2308 line_t* ld = lines;
2309 vertex_t *oldpos = vertexes;
2310 ssize_t offset;
2311 size_t i;
2312
2313 if (numvertexes != origvrtx) // If native vertex count doesn't match node original vertex count, bail out (broken data?).
2314 {
2315 CONS_Alert(CONS_WARNING, "Vertex count in map data and nodes differ!\n");
2316 return false;
2317 }
2318
2319 if (!xtrvrtx)
2320 return true;
2321
2322 // If extra vertexes were generated, reallocate the vertex array and fix the pointers.
2323 numvertexes += xtrvrtx;
2324 vertexes = Z_Realloc(vertexes, numvertexes*sizeof(*vertexes), PU_LEVEL, NULL);
2325 offset = (size_t)(vertexes - oldpos);
2326
2327 for (i = 0, ld = lines; i < numlines; i++, ld++)
2328 {
2329 ld->v1 += offset;
2330 ld->v2 += offset;
2331 }
2332
2333 // Read extra vertex data.
2334 for (i = origvrtx; i < numvertexes; i++)
2335 {
2336 vertexes[i].x = READFIXED((*data));
2337 vertexes[i].y = READFIXED((*data));
2338 }
2339
2340 return true;
2341 }
2342
P_LoadExtendedSubsectorsAndSegs(UINT8 ** data,nodetype_t nodetype)2343 static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype)
2344 {
2345 size_t i, k;
2346 INT16 m;
2347 seg_t *seg;
2348
2349 // Subsectors
2350 numsubsectors = READUINT32((*data));
2351 subsectors = Z_Calloc(numsubsectors*sizeof(*subsectors), PU_LEVEL, NULL);
2352
2353 for (i = 0; i < numsubsectors; i++)
2354 subsectors[i].numlines = READUINT32((*data));
2355
2356 // Segs
2357 numsegs = READUINT32((*data));
2358 segs = Z_Calloc(numsegs*sizeof(*segs), PU_LEVEL, NULL);
2359
2360 for (i = 0, k = 0; i < numsubsectors; i++)
2361 {
2362 subsectors[i].firstline = k;
2363 P_InitializeSubsector(&subsectors[i]);
2364
2365 switch (nodetype)
2366 {
2367 case NT_XGLN:
2368 case NT_XGL3:
2369 for (m = 0; m < subsectors[i].numlines; m++, k++)
2370 {
2371 UINT32 vertexnum = READUINT32((*data));
2372 UINT16 linenum;
2373
2374 if (vertexnum >= numvertexes)
2375 I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid vertex %d!\n", sizeu1(k), m, vertexnum);
2376
2377 segs[k - 1 + ((m == 0) ? subsectors[i].numlines : 0)].v2 = segs[k].v1 = &vertexes[vertexnum];
2378
2379 READUINT32((*data)); // partner, can be ignored by software renderer
2380
2381 linenum = (nodetype == NT_XGL3) ? READUINT32((*data)) : READUINT16((*data));
2382 if (linenum != 0xFFFF && linenum >= numlines)
2383 I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
2384 segs[k].glseg = (linenum == 0xFFFF);
2385 segs[k].linedef = (linenum == 0xFFFF) ? NULL : &lines[linenum];
2386 segs[k].side = READUINT8((*data));
2387 }
2388 break;
2389
2390 case NT_XNOD:
2391 for (m = 0; m < subsectors[i].numlines; m++, k++)
2392 {
2393 UINT32 v1num = READUINT32((*data));
2394 UINT32 v2num = READUINT32((*data));
2395 UINT16 linenum = READUINT16((*data));
2396
2397 if (v1num >= numvertexes)
2398 I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v1 %d!\n", sizeu1(k), m, v1num);
2399 if (v2num >= numvertexes)
2400 I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v2 %d!\n", sizeu1(k), m, v2num);
2401 if (linenum >= numlines)
2402 I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
2403
2404 segs[k].v1 = &vertexes[v1num];
2405 segs[k].v2 = &vertexes[v2num];
2406 segs[k].linedef = &lines[linenum];
2407 segs[k].side = READUINT8((*data));
2408 segs[k].glseg = false;
2409 }
2410 break;
2411
2412 default:
2413 return false;
2414 }
2415 }
2416
2417 for (i = 0, seg = segs; i < numsegs; i++, seg++)
2418 {
2419 vertex_t *v1 = seg->v1;
2420 vertex_t *v2 = seg->v2;
2421 P_InitializeSeg(seg);
2422 seg->angle = R_PointToAngle2(v1->x, v1->y, v2->x, v2->y);
2423 if (seg->linedef)
2424 segs[i].offset = FixedHypot(v1->x - seg->linedef->v1->x, v1->y - seg->linedef->v1->y);
2425 seg->length = P_SegLength(seg);
2426 #ifdef HWRENDER
2427 seg->flength = (rendermode == render_opengl) ? P_SegLengthFloat(seg) : 0;
2428 #endif
2429 }
2430
2431 return true;
2432 }
2433
2434 // Auxiliary function: Shrink node ID from 32-bit to 16-bit.
ShrinkNodeID(UINT32 x)2435 static UINT16 ShrinkNodeID(UINT32 x) {
2436 UINT16 mask = (x >> 16) & 0xC000;
2437 UINT16 result = x;
2438 return result | mask;
2439 }
2440
P_LoadExtendedNodes(UINT8 ** data,nodetype_t nodetype)2441 static void P_LoadExtendedNodes(UINT8 **data, nodetype_t nodetype)
2442 {
2443 node_t *mn;
2444 size_t i, j, k;
2445 boolean xgl3 = (nodetype == NT_XGL3);
2446
2447 numnodes = READINT32((*data));
2448 nodes = Z_Calloc(numnodes*sizeof(*nodes), PU_LEVEL, NULL);
2449
2450 for (i = 0, mn = nodes; i < numnodes; i++, mn++)
2451 {
2452 // Splitter
2453 mn->x = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
2454 mn->y = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
2455 mn->dx = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
2456 mn->dy = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
2457
2458 // Bounding boxes
2459 for (j = 0; j < 2; j++)
2460 for (k = 0; k < 4; k++)
2461 mn->bbox[j][k] = READINT16((*data)) << FRACBITS;
2462
2463 //Children
2464 mn->children[0] = ShrinkNodeID(READUINT32((*data))); /// \todo Use UINT32 for node children in a future, instead?
2465 mn->children[1] = ShrinkNodeID(READUINT32((*data)));
2466 }
2467 }
2468
P_LoadMapBSP(const virtres_t * virt)2469 static void P_LoadMapBSP(const virtres_t *virt)
2470 {
2471 UINT8 *nodedata = NULL;
2472 nodetype_t nodetype = P_GetNodetype(virt, &nodedata);
2473
2474 switch (nodetype)
2475 {
2476 case NT_DOOM:
2477 {
2478 virtlump_t *virtssectors = vres_Find(virt, "SSECTORS");
2479 virtlump_t* virtnodes = vres_Find(virt, "NODES");
2480 virtlump_t *virtsegs = vres_Find(virt, "SEGS");
2481
2482 numsubsectors = virtssectors->size / sizeof(mapsubsector_t);
2483 numnodes = virtnodes->size / sizeof(mapnode_t);
2484 numsegs = virtsegs->size / sizeof(mapseg_t);
2485
2486 if (numsubsectors <= 0)
2487 I_Error("Level has no subsectors (did you forget to run it through a nodesbuilder?)");
2488 if (numnodes <= 0)
2489 I_Error("Level has no nodes");
2490 if (numsegs <= 0)
2491 I_Error("Level has no segs");
2492
2493 subsectors = Z_Calloc(numsubsectors * sizeof(*subsectors), PU_LEVEL, NULL);
2494 nodes = Z_Calloc(numnodes * sizeof(*nodes), PU_LEVEL, NULL);
2495 segs = Z_Calloc(numsegs * sizeof(*segs), PU_LEVEL, NULL);
2496
2497 P_LoadSubsectors(virtssectors->data);
2498 P_LoadNodes(virtnodes->data);
2499 P_LoadSegs(virtsegs->data);
2500 break;
2501 }
2502 case NT_XNOD:
2503 case NT_XGLN:
2504 case NT_XGL3:
2505 if (!P_LoadExtraVertices(&nodedata))
2506 return;
2507 if (!P_LoadExtendedSubsectorsAndSegs(&nodedata, nodetype))
2508 return;
2509 P_LoadExtendedNodes(&nodedata, nodetype);
2510 break;
2511 default:
2512 CONS_Alert(CONS_WARNING, "Unsupported BSP format detected.\n");
2513 return;
2514 }
2515 return;
2516 }
2517
2518 // Split from P_LoadBlockMap for convenience
2519 // -- Monster Iestyn 08/01/18
P_ReadBlockMapLump(INT16 * wadblockmaplump,size_t count)2520 static void P_ReadBlockMapLump(INT16 *wadblockmaplump, size_t count)
2521 {
2522 size_t i;
2523 blockmaplump = Z_Calloc(sizeof (*blockmaplump) * count, PU_LEVEL, NULL);
2524
2525 // killough 3/1/98: Expand wad blockmap into larger internal one,
2526 // by treating all offsets except -1 as unsigned and zero-extending
2527 // them. This potentially doubles the size of blockmaps allowed,
2528 // because Doom originally considered the offsets as always signed.
2529
2530 blockmaplump[0] = SHORT(wadblockmaplump[0]);
2531 blockmaplump[1] = SHORT(wadblockmaplump[1]);
2532 blockmaplump[2] = (INT32)(SHORT(wadblockmaplump[2])) & 0xffff;
2533 blockmaplump[3] = (INT32)(SHORT(wadblockmaplump[3])) & 0xffff;
2534
2535 for (i = 4; i < count; i++)
2536 {
2537 INT16 t = SHORT(wadblockmaplump[i]); // killough 3/1/98
2538 blockmaplump[i] = t == -1 ? (INT32)-1 : (INT32) t & 0xffff;
2539 }
2540 }
2541
2542 // This needs to be a separate function
2543 // because making both the WAD and PK3 loading code use
2544 // the same functions is trickier than it looks for blockmap
2545 // -- Monster Iestyn 09/01/18
P_LoadBlockMap(UINT8 * data,size_t count)2546 static boolean P_LoadBlockMap(UINT8 *data, size_t count)
2547 {
2548 if (!count || count >= 0x20000)
2549 return false;
2550
2551 //CONS_Printf("Reading blockmap lump for pk3...\n");
2552
2553 // no need to malloc anything, assume the data is uncompressed for now
2554 count /= 2;
2555 P_ReadBlockMapLump((INT16 *)data, count);
2556
2557 bmaporgx = blockmaplump[0]<<FRACBITS;
2558 bmaporgy = blockmaplump[1]<<FRACBITS;
2559 bmapwidth = blockmaplump[2];
2560 bmapheight = blockmaplump[3];
2561
2562 // clear out mobj chains
2563 count = sizeof (*blocklinks)* bmapwidth*bmapheight;
2564 blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
2565 blockmap = blockmaplump+4;
2566
2567 // haleyjd 2/22/06: setup polyobject blockmap
2568 count = sizeof(*polyblocklinks) * bmapwidth * bmapheight;
2569 polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL);
2570 return true;
2571 }
2572
LineInBlock(fixed_t cx1,fixed_t cy1,fixed_t cx2,fixed_t cy2,fixed_t bx1,fixed_t by1)2573 static boolean LineInBlock(fixed_t cx1, fixed_t cy1, fixed_t cx2, fixed_t cy2, fixed_t bx1, fixed_t by1)
2574 {
2575 fixed_t bbox[4];
2576 line_t testline;
2577 vertex_t vtest;
2578
2579 bbox[BOXRIGHT] = bx1 + MAPBLOCKUNITS;
2580 bbox[BOXTOP] = by1 + MAPBLOCKUNITS;
2581 bbox[BOXLEFT] = bx1;
2582 bbox[BOXBOTTOM] = by1;
2583
2584 // Trivial rejection
2585 if (cx1 < bbox[BOXLEFT] && cx2 < bbox[BOXLEFT])
2586 return false;
2587
2588 if (cx1 > bbox[BOXRIGHT] && cx2 > bbox[BOXRIGHT])
2589 return false;
2590
2591 if (cy1 < bbox[BOXBOTTOM] && cy2 < bbox[BOXBOTTOM])
2592 return false;
2593
2594 if (cy1 > bbox[BOXTOP] && cy2 > bbox[BOXTOP])
2595 return false;
2596
2597 // Rats, guess we gotta check
2598 // if the line intersects
2599 // any sides of the block.
2600 cx1 <<= FRACBITS;
2601 cy1 <<= FRACBITS;
2602 cx2 <<= FRACBITS;
2603 cy2 <<= FRACBITS;
2604 bbox[BOXTOP] <<= FRACBITS;
2605 bbox[BOXBOTTOM] <<= FRACBITS;
2606 bbox[BOXLEFT] <<= FRACBITS;
2607 bbox[BOXRIGHT] <<= FRACBITS;
2608
2609 testline.v1 = &vtest;
2610
2611 testline.v1->x = cx1;
2612 testline.v1->y = cy1;
2613 testline.dx = cx2 - cx1;
2614 testline.dy = cy2 - cy1;
2615
2616 if ((testline.dx > 0) ^ (testline.dy > 0))
2617 testline.slopetype = ST_NEGATIVE;
2618 else
2619 testline.slopetype = ST_POSITIVE;
2620
2621 return P_BoxOnLineSide(bbox, &testline) == -1;
2622 }
2623
2624 //
2625 // killough 10/98:
2626 //
2627 // Rewritten to use faster algorithm.
2628 //
2629 // SSN Edit: Killough's wasn't accurate enough, sometimes excluding
2630 // blocks that the line did in fact exist in, so now we use
2631 // a fail-safe approach that puts a 'box' around each line.
2632 //
2633 // Please note: This section of code is not interchangable with TeamTNT's
2634 // code which attempts to fix the same problem.
P_CreateBlockMap(void)2635 static void P_CreateBlockMap(void)
2636 {
2637 register size_t i;
2638 fixed_t minx = INT32_MAX, miny = INT32_MAX, maxx = INT32_MIN, maxy = INT32_MIN;
2639 // First find limits of map
2640
2641 for (i = 0; i < numvertexes; i++)
2642 {
2643 if (vertexes[i].x>>FRACBITS < minx)
2644 minx = vertexes[i].x>>FRACBITS;
2645 else if (vertexes[i].x>>FRACBITS > maxx)
2646 maxx = vertexes[i].x>>FRACBITS;
2647 if (vertexes[i].y>>FRACBITS < miny)
2648 miny = vertexes[i].y>>FRACBITS;
2649 else if (vertexes[i].y>>FRACBITS > maxy)
2650 maxy = vertexes[i].y>>FRACBITS;
2651 }
2652
2653 // Save blockmap parameters
2654 bmaporgx = minx << FRACBITS;
2655 bmaporgy = miny << FRACBITS;
2656 bmapwidth = ((maxx-minx) >> MAPBTOFRAC) + 1;
2657 bmapheight = ((maxy-miny) >> MAPBTOFRAC)+ 1;
2658
2659 // Compute blockmap, which is stored as a 2d array of variable-sized lists.
2660 //
2661 // Pseudocode:
2662 //
2663 // For each linedef:
2664 //
2665 // Map the starting and ending vertices to blocks.
2666 //
2667 // Starting in the starting vertex's block, do:
2668 //
2669 // Add linedef to current block's list, dynamically resizing it.
2670 //
2671 // If current block is the same as the ending vertex's block, exit loop.
2672 //
2673 // Move to an adjacent block by moving towards the ending block in
2674 // either the x or y direction, to the block which contains the linedef.
2675
2676 {
2677 typedef struct
2678 {
2679 INT32 n, nalloc;
2680 INT32 *list;
2681 } bmap_t; // blocklist structure
2682
2683 size_t tot = bmapwidth * bmapheight; // size of blockmap
2684 bmap_t *bmap = calloc(tot, sizeof (*bmap)); // array of blocklists
2685 boolean straight;
2686
2687 if (bmap == NULL) I_Error("%s: Out of memory making blockmap", "P_CreateBlockMap");
2688
2689 for (i = 0; i < numlines; i++)
2690 {
2691 // starting coordinates
2692 INT32 x = (lines[i].v1->x>>FRACBITS) - minx;
2693 INT32 y = (lines[i].v1->y>>FRACBITS) - miny;
2694 INT32 bxstart, bxend, bystart, byend, v2x, v2y, curblockx, curblocky;
2695
2696 v2x = lines[i].v2->x>>FRACBITS;
2697 v2y = lines[i].v2->y>>FRACBITS;
2698
2699 // Draw a "box" around the line.
2700 bxstart = (x >> MAPBTOFRAC);
2701 bystart = (y >> MAPBTOFRAC);
2702
2703 v2x -= minx;
2704 v2y -= miny;
2705
2706 bxend = ((v2x) >> MAPBTOFRAC);
2707 byend = ((v2y) >> MAPBTOFRAC);
2708
2709 if (bxend < bxstart)
2710 {
2711 INT32 temp = bxstart;
2712 bxstart = bxend;
2713 bxend = temp;
2714 }
2715
2716 if (byend < bystart)
2717 {
2718 INT32 temp = bystart;
2719 bystart = byend;
2720 byend = temp;
2721 }
2722
2723 // Catch straight lines
2724 // This fixes the error where straight lines
2725 // directly on a blockmap boundary would not
2726 // be included in the proper blocks.
2727 if (lines[i].v1->y == lines[i].v2->y)
2728 {
2729 straight = true;
2730 bystart--;
2731 byend++;
2732 }
2733 else if (lines[i].v1->x == lines[i].v2->x)
2734 {
2735 straight = true;
2736 bxstart--;
2737 bxend++;
2738 }
2739 else
2740 straight = false;
2741
2742 // Now we simply iterate block-by-block until we reach the end block.
2743 for (curblockx = bxstart; curblockx <= bxend; curblockx++)
2744 for (curblocky = bystart; curblocky <= byend; curblocky++)
2745 {
2746 size_t b = curblocky * bmapwidth + curblockx;
2747
2748 if (b >= tot)
2749 continue;
2750
2751 if (!straight && !(LineInBlock((fixed_t)x, (fixed_t)y, (fixed_t)v2x, (fixed_t)v2y, (fixed_t)(curblockx << MAPBTOFRAC), (fixed_t)(curblocky << MAPBTOFRAC))))
2752 continue;
2753
2754 // Increase size of allocated list if necessary
2755 if (bmap[b].n >= bmap[b].nalloc)
2756 {
2757 // Graue 02-29-2004: make code more readable, don't realloc a null pointer
2758 // (because it crashes for me, and because the comp.lang.c FAQ says so)
2759 if (bmap[b].nalloc == 0)
2760 bmap[b].nalloc = 8;
2761 else
2762 bmap[b].nalloc *= 2;
2763 bmap[b].list = Z_Realloc(bmap[b].list, bmap[b].nalloc * sizeof (*bmap->list), PU_CACHE, &bmap[b].list);
2764 if (!bmap[b].list)
2765 I_Error("Out of Memory in P_CreateBlockMap");
2766 }
2767
2768 // Add linedef to end of list
2769 bmap[b].list[bmap[b].n++] = (INT32)i;
2770 }
2771 }
2772
2773 // Compute the total size of the blockmap.
2774 //
2775 // Compression of empty blocks is performed by reserving two offset words
2776 // at tot and tot+1.
2777 //
2778 // 4 words, unused if this routine is called, are reserved at the start.
2779 {
2780 size_t count = tot + 6; // we need at least 1 word per block, plus reserved's
2781
2782 for (i = 0; i < tot; i++)
2783 if (bmap[i].n)
2784 count += bmap[i].n + 2; // 1 header word + 1 trailer word + blocklist
2785
2786 // Allocate blockmap lump with computed count
2787 blockmaplump = Z_Calloc(sizeof (*blockmaplump) * count, PU_LEVEL, NULL);
2788 }
2789
2790 // Now compress the blockmap.
2791 {
2792 size_t ndx = tot += 4; // Advance index to start of linedef lists
2793 bmap_t *bp = bmap; // Start of uncompressed blockmap
2794
2795 blockmaplump[ndx++] = 0; // Store an empty blockmap list at start
2796 blockmaplump[ndx++] = -1; // (Used for compression)
2797
2798 for (i = 4; i < tot; i++, bp++)
2799 if (bp->n) // Non-empty blocklist
2800 {
2801 blockmaplump[blockmaplump[i] = (INT32)(ndx++)] = 0; // Store index & header
2802 do
2803 blockmaplump[ndx++] = bp->list[--bp->n]; // Copy linedef list
2804 while (bp->n);
2805 blockmaplump[ndx++] = -1; // Store trailer
2806 Z_Free(bp->list); // Free linedef list
2807 }
2808 else // Empty blocklist: point to reserved empty blocklist
2809 blockmaplump[i] = (INT32)tot;
2810
2811 free(bmap); // Free uncompressed blockmap
2812 }
2813 }
2814 {
2815 size_t count = sizeof (*blocklinks) * bmapwidth * bmapheight;
2816 // clear out mobj chains (copied from from P_LoadBlockMap)
2817 blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
2818 blockmap = blockmaplump + 4;
2819
2820 // haleyjd 2/22/06: setup polyobject blockmap
2821 count = sizeof(*polyblocklinks) * bmapwidth * bmapheight;
2822 polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL);
2823 }
2824 }
2825
2826 // PK3 version
2827 // -- Monster Iestyn 09/01/18
P_LoadReject(UINT8 * data,size_t count)2828 static void P_LoadReject(UINT8 *data, size_t count)
2829 {
2830 if (!count) // zero length, someone probably used ZDBSP
2831 {
2832 rejectmatrix = NULL;
2833 CONS_Debug(DBG_SETUP, "P_LoadReject: REJECT lump has size 0, will not be loaded\n");
2834 }
2835 else
2836 {
2837 rejectmatrix = Z_Malloc(count, PU_LEVEL, NULL); // allocate memory for the reject matrix
2838 M_Memcpy(rejectmatrix, data, count); // copy the data into it
2839 }
2840 }
2841
P_LoadMapLUT(const virtres_t * virt)2842 static void P_LoadMapLUT(const virtres_t *virt)
2843 {
2844 virtlump_t* virtblockmap = vres_Find(virt, "BLOCKMAP");
2845 virtlump_t* virtreject = vres_Find(virt, "REJECT");
2846
2847 // Lookup tables
2848 if (virtreject)
2849 P_LoadReject(virtreject->data, virtreject->size);
2850 else
2851 rejectmatrix = NULL;
2852
2853 if (!(virtblockmap && P_LoadBlockMap(virtblockmap->data, virtblockmap->size)))
2854 P_CreateBlockMap();
2855 }
2856
2857 //
2858 // P_LinkMapData
2859 // Builds sector line lists and subsector sector numbers.
2860 // Finds block bounding boxes for sectors.
2861 //
P_LinkMapData(void)2862 static void P_LinkMapData(void)
2863 {
2864 size_t i, j;
2865 line_t *li;
2866 sector_t *sector;
2867 subsector_t *ss = subsectors;
2868 size_t sidei;
2869 seg_t *seg;
2870 fixed_t bbox[4];
2871
2872 // look up sector number for each subsector
2873 for (i = 0; i < numsubsectors; i++, ss++)
2874 {
2875 if (ss->firstline >= numsegs)
2876 CorruptMapError(va("P_LinkMapData: ss->firstline invalid "
2877 "(subsector %s, firstline refers to %d of %s)", sizeu1(i), ss->firstline,
2878 sizeu2(numsegs)));
2879 seg = &segs[ss->firstline];
2880 sidei = (size_t)(seg->sidedef - sides);
2881 if (!seg->sidedef)
2882 CorruptMapError(va("P_LinkMapData: seg->sidedef is NULL "
2883 "(subsector %s, firstline is %d)", sizeu1(i), ss->firstline));
2884 if (seg->sidedef - sides < 0 || seg->sidedef - sides > (UINT16)numsides)
2885 CorruptMapError(va("P_LinkMapData: seg->sidedef refers to sidedef %s of %s "
2886 "(subsector %s, firstline is %d)", sizeu1(sidei), sizeu2(numsides),
2887 sizeu3(i), ss->firstline));
2888 if (!seg->sidedef->sector)
2889 CorruptMapError(va("P_LinkMapData: seg->sidedef->sector is NULL "
2890 "(subsector %s, firstline is %d, sidedef is %s)", sizeu1(i), ss->firstline,
2891 sizeu1(sidei)));
2892 ss->sector = seg->sidedef->sector;
2893 }
2894
2895 // count number of lines in each sector
2896 for (i = 0, li = lines; i < numlines; i++, li++)
2897 {
2898 li->frontsector->linecount++;
2899
2900 if (li->backsector && li->backsector != li->frontsector)
2901 li->backsector->linecount++;
2902 }
2903
2904 // allocate linebuffers for each sector
2905 for (i = 0, sector = sectors; i < numsectors; i++, sector++)
2906 {
2907 if (sector->linecount == 0) // no lines found?
2908 {
2909 sector->lines = NULL;
2910 CONS_Debug(DBG_SETUP, "P_LinkMapData: sector %s has no lines\n", sizeu1(i));
2911 }
2912 else
2913 {
2914 sector->lines = Z_Calloc(sector->linecount * sizeof(line_t*), PU_LEVEL, NULL);
2915
2916 // zero the count, since we'll later use this to track how many we've recorded
2917 sector->linecount = 0;
2918 }
2919 }
2920
2921 // iterate through lines, assigning them to sectors' linebuffers,
2922 // and recalculate the counts in the process
2923 for (i = 0, li = lines; i < numlines; i++, li++)
2924 {
2925 li->frontsector->lines[li->frontsector->linecount++] = li;
2926
2927 if (li->backsector && li->backsector != li->frontsector)
2928 li->backsector->lines[li->backsector->linecount++] = li;
2929 }
2930
2931 // set soundorg's position for each sector
2932 for (i = 0, sector = sectors; i < numsectors; i++, sector++)
2933 {
2934 M_ClearBox(bbox);
2935
2936 if (sector->linecount != 0)
2937 {
2938 for (j = 0; j < sector->linecount; j++)
2939 {
2940 li = sector->lines[j];
2941 M_AddToBox(bbox, li->v1->x, li->v1->y);
2942 M_AddToBox(bbox, li->v2->x, li->v2->y);
2943 }
2944 }
2945
2946 // set the degenmobj_t to the middle of the bounding box
2947 sector->soundorg.x = (((bbox[BOXRIGHT]>>FRACBITS) + (bbox[BOXLEFT]>>FRACBITS))/2)<<FRACBITS;
2948 sector->soundorg.y = (((bbox[BOXTOP]>>FRACBITS) + (bbox[BOXBOTTOM]>>FRACBITS))/2)<<FRACBITS;
2949 sector->soundorg.z = sector->floorheight; // default to sector's floor height
2950 }
2951 }
2952
2953 //For maps in binary format, converts setup of specials to UDMF format.
P_ConvertBinaryMap(void)2954 static void P_ConvertBinaryMap(void)
2955 {
2956 size_t i;
2957
2958 for (i = 0; i < numlines; i++)
2959 {
2960 mtag_t tag = Tag_FGet(&lines[i].tags);
2961
2962 switch (lines[i].special)
2963 {
2964 case 20: //PolyObject first line
2965 {
2966 INT32 check = -1;
2967 INT32 paramline = -1;
2968
2969 TAG_ITER_DECLARECOUNTER(0);
2970
2971 TAG_ITER_LINES(0, tag, check)
2972 {
2973 if (lines[check].special == 22)
2974 {
2975 paramline = check;
2976 break;
2977 }
2978 }
2979
2980 //PolyObject ID
2981 lines[i].args[0] = tag;
2982
2983 //Default: Invisible planes
2984 lines[i].args[3] |= TMPF_INVISIBLEPLANES;
2985
2986 //Linedef executor tag
2987 lines[i].args[4] = 32000 + lines[i].args[0];
2988
2989 if (paramline == -1)
2990 break; // no extra settings to apply, let's leave it
2991
2992 //Parent ID
2993 lines[i].args[1] = lines[paramline].frontsector->special;
2994 //Translucency
2995 lines[i].args[2] = (lines[paramline].flags & ML_DONTPEGTOP)
2996 ? (sides[lines[paramline].sidenum[0]].textureoffset >> FRACBITS)
2997 : ((lines[paramline].frontsector->floorheight >> FRACBITS) / 100);
2998
2999 //Flags
3000 if (lines[paramline].flags & ML_EFFECT1)
3001 lines[i].args[3] |= TMPF_NOINSIDES;
3002 if (lines[paramline].flags & ML_EFFECT2)
3003 lines[i].args[3] |= TMPF_INTANGIBLE;
3004 if (lines[paramline].flags & ML_EFFECT3)
3005 lines[i].args[3] |= TMPF_PUSHABLESTOP;
3006 if (lines[paramline].flags & ML_EFFECT4)
3007 lines[i].args[3] &= ~TMPF_INVISIBLEPLANES;
3008 /*if (lines[paramline].flags & ML_EFFECT5)
3009 lines[i].args[3] |= TMPF_DONTCLIPPLANES;*/
3010 if (lines[paramline].flags & ML_EFFECT6)
3011 lines[i].args[3] |= TMPF_SPLAT;
3012 if (lines[paramline].flags & ML_NOCLIMB)
3013 lines[i].args[3] |= TMPF_EXECUTOR;
3014
3015 break;
3016 }
3017 case 443: //Call Lua function
3018 if (lines[i].text)
3019 {
3020 lines[i].stringargs[0] = Z_Malloc(strlen(lines[i].text) + 1, PU_LEVEL, NULL);
3021 M_Memcpy(lines[i].stringargs[0], lines[i].text, strlen(lines[i].text) + 1);
3022 }
3023 else
3024 CONS_Alert(CONS_WARNING, "Linedef %s is missing the hook name of the Lua function to call! (This should be given in the front texture fields)\n", sizeu1(i));
3025 break;
3026 case 447: //Change colormap
3027 lines[i].args[0] = Tag_FGet(&lines[i].tags);
3028 if (lines[i].flags & ML_EFFECT3)
3029 lines[i].args[2] |= TMCF_RELATIVE;
3030 if (lines[i].flags & ML_EFFECT1)
3031 lines[i].args[2] |= TMCF_SUBLIGHTR|TMCF_SUBFADER;
3032 if (lines[i].flags & ML_NOCLIMB)
3033 lines[i].args[2] |= TMCF_SUBLIGHTG|TMCF_SUBFADEG;
3034 if (lines[i].flags & ML_EFFECT2)
3035 lines[i].args[2] |= TMCF_SUBLIGHTB|TMCF_SUBFADEB;
3036 break;
3037 case 455: //Fade colormap
3038 {
3039 INT32 speed = (INT32)((((lines[i].flags & ML_DONTPEGBOTTOM) || !sides[lines[i].sidenum[0]].rowoffset) && lines[i].sidenum[1] != 0xFFFF) ?
3040 abs(sides[lines[i].sidenum[1]].rowoffset >> FRACBITS)
3041 : abs(sides[lines[i].sidenum[0]].rowoffset >> FRACBITS));
3042
3043 lines[i].args[0] = Tag_FGet(&lines[i].tags);
3044 if (lines[i].flags & ML_EFFECT4)
3045 lines[i].args[2] = speed;
3046 else
3047 lines[i].args[2] = (256 + speed - 1)/speed;
3048 if (lines[i].flags & ML_EFFECT3)
3049 lines[i].args[3] |= TMCF_RELATIVE;
3050 if (lines[i].flags & ML_EFFECT1)
3051 lines[i].args[3] |= TMCF_SUBLIGHTR|TMCF_SUBFADER;
3052 if (lines[i].flags & ML_NOCLIMB)
3053 lines[i].args[3] |= TMCF_SUBLIGHTG|TMCF_SUBFADEG;
3054 if (lines[i].flags & ML_EFFECT2)
3055 lines[i].args[3] |= TMCF_SUBLIGHTB|TMCF_SUBFADEB;
3056 if (lines[i].flags & ML_BOUNCY)
3057 lines[i].args[3] |= TMCF_FROMBLACK;
3058 if (lines[i].flags & ML_EFFECT5)
3059 lines[i].args[3] |= TMCF_OVERRIDE;
3060 break;
3061 }
3062 case 456: //Stop fading colormap
3063 lines[i].args[0] = Tag_FGet(&lines[i].tags);
3064 break;
3065 case 606: //Colormap
3066 lines[i].args[0] = Tag_FGet(&lines[i].tags);
3067 break;
3068 case 700: //Slope front sector floor
3069 case 701: //Slope front sector ceiling
3070 case 702: //Slope front sector floor and ceiling
3071 case 703: //Slope front sector floor and back sector ceiling
3072 case 710: //Slope back sector floor
3073 case 711: //Slope back sector ceiling
3074 case 712: //Slope back sector floor and ceiling
3075 case 713: //Slope back sector floor and front sector ceiling
3076 {
3077 boolean frontfloor = (lines[i].special == 700 || lines[i].special == 702 || lines[i].special == 703);
3078 boolean backfloor = (lines[i].special == 710 || lines[i].special == 712 || lines[i].special == 713);
3079 boolean frontceil = (lines[i].special == 701 || lines[i].special == 702 || lines[i].special == 713);
3080 boolean backceil = (lines[i].special == 711 || lines[i].special == 712 || lines[i].special == 703);
3081
3082 lines[i].args[0] = backfloor ? TMS_BACK : (frontfloor ? TMS_FRONT : TMS_NONE);
3083 lines[i].args[1] = backceil ? TMS_BACK : (frontceil ? TMS_FRONT : TMS_NONE);
3084
3085 if (lines[i].flags & ML_NETONLY)
3086 lines[i].args[2] |= TMSL_NOPHYSICS;
3087 if (lines[i].flags & ML_NONET)
3088 lines[i].args[2] |= TMSL_DYNAMIC;
3089
3090 lines[i].special = 700;
3091 break;
3092 }
3093 case 704: //Slope front sector floor by 3 tagged vertices
3094 case 705: //Slope front sector ceiling by 3 tagged vertices
3095 case 714: //Slope back sector floor by 3 tagged vertices
3096 case 715: //Slope back sector ceiling by 3 tagged vertices
3097 {
3098 if (lines[i].special == 704)
3099 lines[i].args[0] = TMSP_FRONTFLOOR;
3100 else if (lines[i].special == 705)
3101 lines[i].args[0] = TMSP_FRONTCEILING;
3102 else if (lines[i].special == 714)
3103 lines[i].args[0] = TMSP_BACKFLOOR;
3104 else if (lines[i].special == 715)
3105 lines[i].args[0] = TMSP_BACKCEILING;
3106
3107 lines[i].args[1] = tag;
3108
3109 if (lines[i].flags & ML_EFFECT6)
3110 {
3111 UINT8 side = lines[i].special >= 714;
3112
3113 if (side == 1 && lines[i].sidenum[1] == 0xffff)
3114 CONS_Debug(DBG_GAMELOGIC, "P_ConvertBinaryMap: Line special %d (line #%s) missing 2nd side!\n", lines[i].special, sizeu1(i));
3115 else
3116 {
3117 lines[i].args[2] = sides[lines[i].sidenum[side]].textureoffset >> FRACBITS;
3118 lines[i].args[3] = sides[lines[i].sidenum[side]].rowoffset >> FRACBITS;
3119 }
3120 }
3121 else
3122 {
3123 lines[i].args[2] = lines[i].args[1];
3124 lines[i].args[3] = lines[i].args[1];
3125 }
3126
3127 if (lines[i].flags & ML_NETONLY)
3128 lines[i].args[4] |= TMSL_NOPHYSICS;
3129 if (lines[i].flags & ML_NONET)
3130 lines[i].args[4] |= TMSL_DYNAMIC;
3131
3132 lines[i].special = 704;
3133 break;
3134 }
3135 case 720: //Copy front side floor slope
3136 case 721: //Copy front side ceiling slope
3137 case 722: //Copy front side floor and ceiling slope
3138 if (lines[i].special != 721)
3139 lines[i].args[0] = tag;
3140 if (lines[i].special != 720)
3141 lines[i].args[1] = tag;
3142 lines[i].special = 720;
3143 break;
3144 case 900: //Translucent wall (10%)
3145 case 901: //Translucent wall (20%)
3146 case 902: //Translucent wall (30%)
3147 case 903: //Translucent wall (40%)
3148 case 904: //Translucent wall (50%)
3149 case 905: //Translucent wall (60%)
3150 case 906: //Translucent wall (70%)
3151 case 907: //Translucent wall (80%)
3152 case 908: //Translucent wall (90%)
3153 lines[i].alpha = ((909 - lines[i].special) << FRACBITS)/10;
3154 break;
3155 default:
3156 break;
3157 }
3158
3159 //Linedef executor delay
3160 if (lines[i].special >= 400 && lines[i].special < 500)
3161 {
3162 //Dummy value to indicate that this executor is delayed.
3163 //The real value is taken from the back sector at runtime.
3164 if (lines[i].flags & ML_DONTPEGTOP)
3165 lines[i].executordelay = 1;
3166 }
3167 }
3168
3169 for (i = 0; i < nummapthings; i++)
3170 {
3171 switch (mapthings[i].type)
3172 {
3173 case 750:
3174 Tag_FSet(&mapthings[i].tags, mapthings[i].angle);
3175 break;
3176 case 760:
3177 case 761:
3178 Tag_FSet(&mapthings[i].tags, mapthings[i].angle);
3179 break;
3180 case 762:
3181 {
3182 INT32 check = -1;
3183 INT32 firstline = -1;
3184 mtag_t tag = mapthings[i].angle;
3185
3186 TAG_ITER_DECLARECOUNTER(0);
3187
3188 Tag_FSet(&mapthings[i].tags, tag);
3189
3190 TAG_ITER_LINES(0, tag, check)
3191 {
3192 if (lines[check].special == 20)
3193 {
3194 firstline = check;
3195 break;
3196 }
3197 }
3198
3199 if (firstline != -1)
3200 lines[firstline].args[3] |= TMPF_CRUSH;
3201
3202 mapthings[i].type = 761;
3203 break;
3204 }
3205 case 780:
3206 Tag_FSet(&mapthings[i].tags, mapthings[i].extrainfo);
3207 break;
3208 default:
3209 break;
3210 }
3211 }
3212 }
3213
3214 /** Compute MD5 message digest for bytes read from memory source
3215 *
3216 * The resulting message digest number will be written into the 16 bytes
3217 * beginning at RESBLOCK.
3218 *
3219 * \param filename path of file
3220 * \param resblock resulting MD5 checksum
3221 * \return 0 if MD5 checksum was made, and is at resblock, 1 if error was found
3222 */
P_MakeBufferMD5(const char * buffer,size_t len,void * resblock)3223 static INT32 P_MakeBufferMD5(const char *buffer, size_t len, void *resblock)
3224 {
3225 #ifdef NOMD5
3226 (void)buffer;
3227 (void)len;
3228 memset(resblock, 0x00, 16);
3229 return 1;
3230 #else
3231 tic_t t = I_GetTime();
3232 CONS_Debug(DBG_SETUP, "Making MD5\n");
3233 if (md5_buffer(buffer, len, resblock) == NULL)
3234 return 1;
3235 CONS_Debug(DBG_SETUP, "MD5 calc took %f seconds\n", (float)(I_GetTime() - t)/NEWTICRATE);
3236 return 0;
3237 #endif
3238 }
3239
P_MakeMapMD5(virtres_t * virt,void * dest)3240 static void P_MakeMapMD5(virtres_t *virt, void *dest)
3241 {
3242 unsigned char resmd5[16];
3243
3244 if (udmf)
3245 {
3246 virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
3247 P_MakeBufferMD5((char*)textmap->data, textmap->size, resmd5);
3248 }
3249 else
3250 {
3251 unsigned char linemd5[16];
3252 unsigned char sectormd5[16];
3253 unsigned char thingmd5[16];
3254 unsigned char sidedefmd5[16];
3255 UINT8 i;
3256
3257 // Create a hash for the current map
3258 // get the actual lumps!
3259 virtlump_t* virtlines = vres_Find(virt, "LINEDEFS");
3260 virtlump_t* virtsectors = vres_Find(virt, "SECTORS");
3261 virtlump_t* virtmthings = vres_Find(virt, "THINGS");
3262 virtlump_t* virtsides = vres_Find(virt, "SIDEDEFS");
3263
3264 P_MakeBufferMD5((char*)virtlines->data, virtlines->size, linemd5);
3265 P_MakeBufferMD5((char*)virtsectors->data, virtsectors->size, sectormd5);
3266 P_MakeBufferMD5((char*)virtmthings->data, virtmthings->size, thingmd5);
3267 P_MakeBufferMD5((char*)virtsides->data, virtsides->size, sidedefmd5);
3268
3269 for (i = 0; i < 16; i++)
3270 resmd5[i] = (linemd5[i] + sectormd5[i] + thingmd5[i] + sidedefmd5[i]) & 0xFF;
3271 }
3272
3273 M_Memcpy(dest, &resmd5, 16);
3274 }
3275
P_LoadMapFromFile(void)3276 static boolean P_LoadMapFromFile(void)
3277 {
3278 virtres_t *virt = vres_GetMap(lastloadedmaplumpnum);
3279 virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
3280 size_t i;
3281 udmf = textmap != NULL;
3282
3283 if (!P_LoadMapData(virt))
3284 return false;
3285 P_LoadMapBSP(virt);
3286 P_LoadMapLUT(virt);
3287
3288 P_LinkMapData();
3289
3290 Taglist_InitGlobalTables();
3291
3292 if (!udmf)
3293 P_ConvertBinaryMap();
3294
3295 // Copy relevant map data for NetArchive purposes.
3296 spawnsectors = Z_Calloc(numsectors * sizeof(*sectors), PU_LEVEL, NULL);
3297 spawnlines = Z_Calloc(numlines * sizeof(*lines), PU_LEVEL, NULL);
3298 spawnsides = Z_Calloc(numsides * sizeof(*sides), PU_LEVEL, NULL);
3299
3300 memcpy(spawnsectors, sectors, numsectors * sizeof(*sectors));
3301 memcpy(spawnlines, lines, numlines * sizeof(*lines));
3302 memcpy(spawnsides, sides, numsides * sizeof(*sides));
3303
3304 for (i = 0; i < numsectors; i++)
3305 if (sectors[i].tags.count)
3306 spawnsectors[i].tags.tags = memcpy(Z_Malloc(sectors[i].tags.count*sizeof(mtag_t), PU_LEVEL, NULL), sectors[i].tags.tags, sectors[i].tags.count*sizeof(mtag_t));
3307
3308 P_MakeMapMD5(virt, &mapmd5);
3309
3310 vres_Free(virt);
3311 return true;
3312 }
3313
3314 //
3315 // LEVEL INITIALIZATION FUNCTIONS
3316 //
3317
3318 /** Sets up a sky texture to use for the level.
3319 * The sky texture is used instead of F_SKY1.
3320 */
P_SetupLevelSky(INT32 skynum,boolean global)3321 void P_SetupLevelSky(INT32 skynum, boolean global)
3322 {
3323 char skytexname[12];
3324
3325 sprintf(skytexname, "SKY%d", skynum);
3326 skytexture = R_TextureNumForName(skytexname);
3327 levelskynum = skynum;
3328
3329 // Global change
3330 if (global)
3331 globallevelskynum = levelskynum;
3332
3333 // Don't go beyond for dedicated servers
3334 if (dedicated)
3335 return;
3336
3337 // scale up the old skies, if needed
3338 R_SetupSkyDraw();
3339 }
3340
3341 static const char *maplumpname;
3342 lumpnum_t lastloadedmaplumpnum; // for comparative savegame
3343
3344 //
3345 // P_LevelInitStuff
3346 //
3347 // Some player initialization for map start.
3348 //
P_InitLevelSettings(void)3349 static void P_InitLevelSettings(void)
3350 {
3351 INT32 i;
3352 boolean canresetlives = true;
3353
3354 leveltime = 0;
3355
3356 modulothing = 0;
3357
3358 // special stage tokens, emeralds, and ring total
3359 tokenbits = 0;
3360 runemeraldmanager = false;
3361 emeraldspawndelay = 60*TICRATE;
3362 if ((netgame || multiplayer) && !G_IsSpecialStage(gamemap))
3363 nummaprings = -1;
3364 else
3365 nummaprings = mapheaderinfo[gamemap-1]->startrings;
3366
3367 // emerald hunt
3368 hunt1 = hunt2 = hunt3 = NULL;
3369
3370 // map time limit
3371 if (mapheaderinfo[gamemap-1]->countdown)
3372 {
3373 tic_t maxtime = 0;
3374 countdowntimer = mapheaderinfo[gamemap-1]->countdown * TICRATE;
3375 for (i = 0; i < MAXPLAYERS; i++)
3376 {
3377 if (!playeringame[i])
3378 continue;
3379 if (players[i].starposttime > maxtime)
3380 maxtime = players[i].starposttime;
3381 }
3382 countdowntimer -= maxtime;
3383 }
3384 else
3385 countdowntimer = 0;
3386 countdowntimeup = false;
3387
3388 // clear ctf pointers
3389 redflag = blueflag = NULL;
3390 rflagpoint = bflagpoint = NULL;
3391
3392 // circuit, race and competition stuff
3393 circuitmap = false;
3394 numstarposts = 0;
3395 ssspheres = timeinmap = 0;
3396
3397 // special stage
3398 stagefailed = true; // assume failed unless proven otherwise - P_GiveEmerald or emerald touchspecial
3399 // Reset temporary record data
3400 memset(&ntemprecords, 0, sizeof(nightsdata_t));
3401
3402 // earthquake camera
3403 memset(&quake,0,sizeof(struct quake));
3404
3405 if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
3406 {
3407 for (i = 0; i < MAXPLAYERS; i++)
3408 {
3409 if (playeringame[i] && players[i].lives > 0)
3410 {
3411 canresetlives = false;
3412 break;
3413 }
3414 }
3415 }
3416
3417 countdown = countdown2 = exitfadestarted = 0;
3418
3419 for (i = 0; i < MAXPLAYERS; i++)
3420 {
3421 G_PlayerReborn(i, true);
3422
3423 if (canresetlives && (netgame || multiplayer) && playeringame[i] && (G_CompetitionGametype() || players[i].lives <= 0))
3424 {
3425 // In Co-Op, replenish a user's lives if they are depleted.
3426 players[i].lives = cv_startinglives.value;
3427 }
3428
3429 // obliteration station...
3430 players[i].numboxes = players[i].totalring =\
3431 players[i].laps = players[i].marescore = players[i].lastmarescore =\
3432 players[i].mare = players[i].exiting = 0;
3433
3434 players[i].drillmeter = 40*20;
3435
3436 // hit these too
3437 players[i].pflags &= ~(PF_GAMETYPEOVER);
3438 }
3439
3440 if (botingame)
3441 CV_SetValue(&cv_analog[1], true);
3442 }
3443
3444 // Respawns all the mapthings and mobjs in the map from the already loaded map data.
P_RespawnThings(void)3445 void P_RespawnThings(void)
3446 {
3447 // Search through all the thinkers.
3448 thinker_t *think;
3449 INT32 i, viewid = -1, centerid = -1; // for skyboxes
3450
3451 // check if these are any of the normal viewpoint/centerpoint mobjs in the level or not
3452 if (skyboxmo[0] || skyboxmo[1])
3453 for (i = 0; i < 16; i++)
3454 {
3455 if (skyboxmo[0] && skyboxmo[0] == skyboxviewpnts[i])
3456 viewid = i; // save id just in case
3457 if (skyboxmo[1] && skyboxmo[1] == skyboxcenterpnts[i])
3458 centerid = i; // save id just in case
3459 }
3460
3461 for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
3462 {
3463 if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
3464 continue;
3465 P_RemoveMobj((mobj_t *)think);
3466 }
3467
3468 P_InitLevelSettings();
3469
3470 localaiming = 0;
3471 localaiming2 = 0;
3472
3473 P_SpawnMapThings(true);
3474
3475 // restore skybox viewpoint/centerpoint if necessary, set them to defaults if we can't do that
3476 skyboxmo[0] = skyboxviewpnts[(viewid >= 0) ? viewid : 0];
3477 skyboxmo[1] = skyboxcenterpnts[(centerid >= 0) ? centerid : 0];
3478 }
3479
P_RunLevelScript(const char * scriptname)3480 static void P_RunLevelScript(const char *scriptname)
3481 {
3482 if (!(mapheaderinfo[gamemap-1]->levelflags & LF_SCRIPTISFILE))
3483 {
3484 lumpnum_t lumpnum;
3485 char newname[9];
3486
3487 strncpy(newname, scriptname, 8);
3488
3489 newname[8] = '\0';
3490
3491 lumpnum = W_CheckNumForName(newname);
3492
3493 if (lumpnum == LUMPERROR || W_LumpLength(lumpnum) == 0)
3494 {
3495 CONS_Debug(DBG_SETUP, "SOC Error: script lump %s not found/not valid.\n", newname);
3496 return;
3497 }
3498
3499 COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE));
3500 }
3501 else
3502 {
3503 COM_BufAddText(va("exec %s\n", scriptname));
3504 }
3505 COM_BufExecute(); // Run it!
3506 }
3507
P_ForceCharacter(const char * forcecharskin)3508 static void P_ForceCharacter(const char *forcecharskin)
3509 {
3510 if (netgame)
3511 {
3512 char skincmd[33];
3513 if (splitscreen)
3514 {
3515 sprintf(skincmd, "skin2 %s\n", forcecharskin);
3516 CV_Set(&cv_skin2, forcecharskin);
3517 }
3518
3519 sprintf(skincmd, "skin %s\n", forcecharskin);
3520 COM_BufAddText(skincmd);
3521 }
3522 else
3523 {
3524 if (splitscreen)
3525 {
3526 SetPlayerSkin(secondarydisplayplayer, forcecharskin);
3527 if ((unsigned)cv_playercolor2.value != skins[players[secondarydisplayplayer].skin].prefcolor)
3528 {
3529 CV_StealthSetValue(&cv_playercolor2, skins[players[secondarydisplayplayer].skin].prefcolor);
3530 players[secondarydisplayplayer].skincolor = skins[players[secondarydisplayplayer].skin].prefcolor;
3531 }
3532 }
3533
3534 SetPlayerSkin(consoleplayer, forcecharskin);
3535 // normal player colors in single player
3536 if ((unsigned)cv_playercolor.value != skins[players[consoleplayer].skin].prefcolor)
3537 {
3538 CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor);
3539 players[consoleplayer].skincolor = skins[players[consoleplayer].skin].prefcolor;
3540 }
3541 }
3542 }
3543
P_ResetSpawnpoints(void)3544 static void P_ResetSpawnpoints(void)
3545 {
3546 UINT8 i;
3547
3548 numdmstarts = numredctfstarts = numbluectfstarts = 0;
3549
3550 // reset the player starts
3551 for (i = 0; i < MAXPLAYERS; i++)
3552 playerstarts[i] = bluectfstarts[i] = redctfstarts[i] = NULL;
3553
3554 for (i = 0; i < MAX_DM_STARTS; i++)
3555 deathmatchstarts[i] = NULL;
3556
3557 for (i = 0; i < 2; i++)
3558 skyboxmo[i] = NULL;
3559
3560 for (i = 0; i < 16; i++)
3561 skyboxviewpnts[i] = skyboxcenterpnts[i] = NULL;
3562 }
3563
P_LoadRecordGhosts(void)3564 static void P_LoadRecordGhosts(void)
3565 {
3566 const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
3567 char *gpath = malloc(glen);
3568 INT32 i;
3569
3570 if (!gpath)
3571 return;
3572
3573 sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
3574
3575 // Best Score ghost
3576 if (cv_ghost_bestscore.value)
3577 {
3578 for (i = 0; i < numskins; ++i)
3579 {
3580 if (cv_ghost_bestscore.value == 1 && players[consoleplayer].skin != i)
3581 continue;
3582
3583 if (FIL_FileExists(va("%s-%s-score-best.lmp", gpath, skins[i].name)))
3584 G_AddGhost(va("%s-%s-score-best.lmp", gpath, skins[i].name));
3585 }
3586 }
3587
3588 // Best Time ghost
3589 if (cv_ghost_besttime.value)
3590 {
3591 for (i = 0; i < numskins; ++i)
3592 {
3593 if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i)
3594 continue;
3595
3596 if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name)))
3597 G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name));
3598 }
3599 }
3600
3601 // Best Rings ghost
3602 if (cv_ghost_bestrings.value)
3603 {
3604 for (i = 0; i < numskins; ++i)
3605 {
3606 if (cv_ghost_bestrings.value == 1 && players[consoleplayer].skin != i)
3607 continue;
3608
3609 if (FIL_FileExists(va("%s-%s-rings-best.lmp", gpath, skins[i].name)))
3610 G_AddGhost(va("%s-%s-rings-best.lmp", gpath, skins[i].name));
3611 }
3612 }
3613
3614 // Last ghost
3615 if (cv_ghost_last.value)
3616 {
3617 for (i = 0; i < numskins; ++i)
3618 {
3619 if (cv_ghost_last.value == 1 && players[consoleplayer].skin != i)
3620 continue;
3621
3622 if (FIL_FileExists(va("%s-%s-last.lmp", gpath, skins[i].name)))
3623 G_AddGhost(va("%s-%s-last.lmp", gpath, skins[i].name));
3624 }
3625 }
3626
3627 // Guest ghost
3628 if (cv_ghost_guest.value && FIL_FileExists(va("%s-guest.lmp", gpath)))
3629 G_AddGhost(va("%s-guest.lmp", gpath));
3630
3631 free(gpath);
3632 }
3633
P_LoadNightsGhosts(void)3634 static void P_LoadNightsGhosts(void)
3635 {
3636 const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
3637 char *gpath = malloc(glen);
3638 INT32 i;
3639
3640 if (!gpath)
3641 return;
3642
3643 sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
3644
3645 // Best Score ghost
3646 if (cv_ghost_bestscore.value)
3647 {
3648 for (i = 0; i < numskins; ++i)
3649 {
3650 if (cv_ghost_bestscore.value == 1 && players[consoleplayer].skin != i)
3651 continue;
3652
3653 if (FIL_FileExists(va("%s-%s-score-best.lmp", gpath, skins[i].name)))
3654 G_AddGhost(va("%s-%s-score-best.lmp", gpath, skins[i].name));
3655 }
3656 }
3657
3658 // Best Time ghost
3659 if (cv_ghost_besttime.value)
3660 {
3661 for (i = 0; i < numskins; ++i)
3662 {
3663 if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i)
3664 continue;
3665
3666 if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name)))
3667 G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name));
3668 }
3669 }
3670
3671 // Last ghost
3672 if (cv_ghost_last.value)
3673 {
3674 for (i = 0; i < numskins; ++i)
3675 {
3676 if (cv_ghost_last.value == 1 && players[consoleplayer].skin != i)
3677 continue;
3678
3679 if (FIL_FileExists(va("%s-%s-last.lmp", gpath, skins[i].name)))
3680 G_AddGhost(va("%s-%s-last.lmp", gpath, skins[i].name));
3681 }
3682 }
3683
3684 // Guest ghost
3685 if (cv_ghost_guest.value && FIL_FileExists(va("%s-guest.lmp", gpath)))
3686 G_AddGhost(va("%s-guest.lmp", gpath));
3687
3688 free(gpath);
3689 }
3690
P_InitTagGametype(void)3691 static void P_InitTagGametype(void)
3692 {
3693 UINT8 i;
3694 INT32 realnumplayers = 0;
3695 INT32 playersactive[MAXPLAYERS];
3696
3697 //I just realized how problematic this code can be.
3698 //D_NumPlayers() will not always cover the scope of the netgame.
3699 //What if one player is node 0 and the other node 31?
3700 //The solution? Make a temp array of all players that are currently playing and pick from them.
3701 //Future todo? When a player leaves, shift all nodes down so D_NumPlayers() can be used as intended?
3702 //Also, you'd never have to loop through all 32 players slots to find anything ever again.
3703 for (i = 0; i < MAXPLAYERS; i++)
3704 {
3705 if (playeringame[i] && !(players[i].spectator || players[i].quittime))
3706 {
3707 playersactive[realnumplayers] = i; //stores the player's node in the array.
3708 realnumplayers++;
3709 }
3710 }
3711
3712 if (!realnumplayers) //this should also fix the dedicated crash bug. You only pick a player if one exists to be picked.
3713 {
3714 CONS_Printf(M_GetText("No player currently available to become IT. Awaiting available players.\n"));
3715 return;
3716 }
3717
3718 i = P_RandomKey(realnumplayers);
3719 players[playersactive[i]].pflags |= PF_TAGIT; //choose our initial tagger before map starts.
3720
3721 // Taken and modified from G_DoReborn()
3722 // Remove the player so he can respawn elsewhere.
3723 // first disassociate the corpse
3724 if (players[playersactive[i]].mo)
3725 P_RemoveMobj(players[playersactive[i]].mo);
3726
3727 G_SpawnPlayer(playersactive[i]); //respawn the lucky player in his dedicated spawn location.
3728 }
3729
P_SetupCamera(void)3730 static void P_SetupCamera(void)
3731 {
3732 if (players[displayplayer].mo && (server || addedtogame))
3733 {
3734 camera.x = players[displayplayer].mo->x;
3735 camera.y = players[displayplayer].mo->y;
3736 camera.z = players[displayplayer].mo->z;
3737 camera.angle = players[displayplayer].mo->angle;
3738 camera.subsector = R_PointInSubsector(camera.x, camera.y); // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
3739 }
3740 else
3741 {
3742 mapthing_t *thing;
3743
3744 if (gametyperules & GTR_DEATHMATCHSTARTS)
3745 thing = deathmatchstarts[0];
3746 else
3747 thing = playerstarts[0];
3748
3749 if (thing)
3750 {
3751 camera.x = thing->x;
3752 camera.y = thing->y;
3753 camera.z = thing->z;
3754 camera.angle = FixedAngle((fixed_t)thing->angle << FRACBITS);
3755 camera.subsector = R_PointInSubsector(camera.x, camera.y); // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
3756 }
3757 }
3758 }
3759
P_InitCamera(void)3760 static void P_InitCamera(void)
3761 {
3762 if (!dedicated)
3763 {
3764 P_SetupCamera();
3765
3766 // Salt: CV_ClearChangedFlags() messes with your settings :(
3767 /*if (!cv_cam_height.changed)
3768 CV_Set(&cv_cam_height, cv_cam_height.defaultvalue);
3769 if (!cv_cam2_height.changed)
3770 CV_Set(&cv_cam2_height, cv_cam2_height.defaultvalue);
3771
3772 if (!cv_cam_dist.changed)
3773 CV_Set(&cv_cam_dist, cv_cam_dist.defaultvalue);
3774 if (!cv_cam2_dist.changed)
3775 CV_Set(&cv_cam2_dist, cv_cam2_dist.defaultvalue);*/
3776
3777 // Though, I don't think anyone would care about cam_rotate being reset back to the only value that makes sense :P
3778 if (!cv_cam_rotate.changed)
3779 CV_Set(&cv_cam_rotate, cv_cam_rotate.defaultvalue);
3780 if (!cv_cam2_rotate.changed)
3781 CV_Set(&cv_cam2_rotate, cv_cam2_rotate.defaultvalue);
3782
3783 if (!cv_analog[0].changed)
3784 CV_SetValue(&cv_analog[0], 0);
3785 if (!cv_analog[1].changed)
3786 CV_SetValue(&cv_analog[1], 0);
3787
3788 displayplayer = consoleplayer; // Start with your OWN view, please!
3789 }
3790
3791 if (twodlevel)
3792 {
3793 CV_SetValue(&cv_analog[0], false);
3794 CV_SetValue(&cv_analog[1], false);
3795 }
3796 else
3797 {
3798 if (cv_useranalog[0].value)
3799 CV_SetValue(&cv_analog[0], true);
3800
3801 if ((splitscreen && cv_useranalog[1].value) || botingame)
3802 CV_SetValue(&cv_analog[1], true);
3803 }
3804 }
3805
P_RunSpecialStageWipe(void)3806 static void P_RunSpecialStageWipe(void)
3807 {
3808 tic_t starttime = I_GetTime();
3809 tic_t endtime = starttime + (3*TICRATE)/2;
3810 tic_t nowtime;
3811
3812 S_StartSound(NULL, sfx_s3kaf);
3813
3814 // Fade music! Time it to S3KAF: 0.25 seconds is snappy.
3815 if (RESETMUSIC ||
3816 strnicmp(S_MusicName(),
3817 (mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap - 1]->musname : mapmusname, 7))
3818 S_FadeOutStopMusic(MUSICRATE/4); //FixedMul(FixedDiv(F_GetWipeLength(wipedefs[wipe_speclevel_towhite])*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)
3819
3820 F_WipeStartScreen();
3821 wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
3822
3823 #ifdef HWRENDER
3824 // uh..........
3825 if (rendermode == render_opengl)
3826 F_WipeColorFill(0);
3827 #endif
3828
3829 F_WipeEndScreen();
3830 F_RunWipe(wipedefs[wipe_speclevel_towhite], false);
3831
3832 I_OsPolling();
3833 I_FinishUpdate(); // page flip or blit buffer
3834 if (moviemode)
3835 M_SaveFrame();
3836
3837 nowtime = lastwipetic;
3838
3839 // Hold on white for extra effect.
3840 while (nowtime < endtime)
3841 {
3842 // wait loop
3843 while (!((nowtime = I_GetTime()) - lastwipetic))
3844 I_Sleep();
3845 lastwipetic = nowtime;
3846 if (moviemode) // make sure we save frames for the white hold too
3847 M_SaveFrame();
3848 }
3849 }
3850
P_RunLevelWipe(void)3851 static void P_RunLevelWipe(void)
3852 {
3853 F_WipeStartScreen();
3854 wipestyleflags |= WSF_FADEOUT;
3855
3856 #ifdef HWRENDER
3857 // uh..........
3858 if (rendermode == render_opengl)
3859 F_WipeColorFill(31);
3860 #endif
3861
3862 F_WipeEndScreen();
3863 // for titlemap: run a specific wipe if specified
3864 // needed for exiting time attack
3865 if (wipetypepre != INT16_MAX)
3866 F_RunWipe(
3867 (wipetypepre >= 0 && F_WipeExists(wipetypepre)) ? wipetypepre : wipedefs[wipe_level_toblack],
3868 false);
3869 wipetypepre = -1;
3870 }
3871
P_InitPlayers(void)3872 static void P_InitPlayers(void)
3873 {
3874 UINT8 i;
3875
3876 for (i = 0; i < MAXPLAYERS; i++)
3877 {
3878 if (!playeringame[i])
3879 continue;
3880
3881 // Start players with pity shields if possible
3882 players[i].pity = -1;
3883
3884 players[i].mo = NULL;
3885
3886 if (!G_PlatformGametype())
3887 G_DoReborn(i);
3888 else // gametype is GT_COOP or GT_RACE
3889 {
3890 G_SpawnPlayer(i);
3891 if (players[i].starposttime)
3892 P_ClearStarPost(players[i].starpostnum);
3893 }
3894 }
3895 }
3896
P_WriteLetter(void)3897 static void P_WriteLetter(void)
3898 {
3899 char *buf, *b;
3900
3901 if (!unlockables[27].unlocked) // pandora's box
3902 return;
3903
3904 if (modeattacking)
3905 return;
3906
3907 #ifndef DEVELOP
3908 if (modifiedgame)
3909 return;
3910 #endif
3911
3912 if (netgame || multiplayer)
3913 return;
3914
3915 if (gamemap != 0x1d35 - 016464)
3916 return;
3917
3918 P_SpawnMobj(0640370000, 0x11000000, 0x3180000, MT_LETTER)->angle = ANGLE_90;
3919
3920 if (textprompts[199]->page[1].backcolor == 259)
3921 return;
3922
3923 buf = W_CacheLumpName("WATERMAP", PU_STATIC);
3924 b = buf;
3925
3926 while ((*b != 65) && (b - buf < 256))
3927 {
3928 *b = (*b - 65) & 255;
3929 b++;
3930 }
3931 *b = '\0';
3932
3933 Z_Free(textprompts[199]->page[1].text);
3934 textprompts[199]->page[1].text = Z_StrDup(buf);
3935 textprompts[199]->page[1].lines = 4;
3936 textprompts[199]->page[1].backcolor = 259;
3937 Z_Free(buf);
3938 }
3939
P_InitGametype(void)3940 static void P_InitGametype(void)
3941 {
3942 UINT8 i;
3943
3944 P_InitPlayers();
3945
3946 // restore time in netgame (see also g_game.c)
3947 if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
3948 {
3949 // is this a hack? maybe
3950 tic_t maxstarposttime = 0;
3951 for (i = 0; i < MAXPLAYERS; i++)
3952 {
3953 if (playeringame[i] && players[i].starposttime > maxstarposttime)
3954 maxstarposttime = players[i].starposttime;
3955 }
3956 leveltime = maxstarposttime;
3957 }
3958
3959 P_WriteLetter();
3960
3961 if (modeattacking == ATTACKING_RECORD && !demoplayback)
3962 P_LoadRecordGhosts();
3963 else if (modeattacking == ATTACKING_NIGHTS && !demoplayback)
3964 P_LoadNightsGhosts();
3965
3966 if (G_TagGametype())
3967 P_InitTagGametype();
3968 else if (((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE) && server)
3969 CV_StealthSetValue(&cv_numlaps,
3970 (cv_basenumlaps.value)
3971 ? cv_basenumlaps.value
3972 : mapheaderinfo[gamemap - 1]->numlaps);
3973 }
3974
3975 /** Loads a level from a lump or external wad.
3976 *
3977 * \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot.
3978 * \todo Clean up, refactor, split up; get rid of the bloat.
3979 */
P_LoadLevel(boolean fromnetsave,boolean reloadinggamestate)3980 boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
3981 {
3982 // use gamemap to get map number.
3983 // 99% of the things already did, so.
3984 // Map header should always be in place at this point
3985 INT32 i, ranspecialwipe = 0;
3986 sector_t *ss;
3987 levelloading = true;
3988
3989 // This is needed. Don't touch.
3990 maptol = mapheaderinfo[gamemap-1]->typeoflevel;
3991 gametyperules = gametypedefaultrules[gametype];
3992
3993 CON_Drawer(); // let the user know what we are going to do
3994 I_FinishUpdate(); // page flip or blit buffer
3995
3996 // Reset the palette
3997 if (rendermode != render_none)
3998 V_SetPaletteLump("PLAYPAL");
3999
4000 // Initialize sector node list.
4001 P_Initsecnode();
4002
4003 if (netgame || multiplayer)
4004 cv_debug = botskin = 0;
4005
4006 if (metalplayback)
4007 G_StopMetalDemo();
4008
4009 // Clear CECHO messages
4010 HU_ClearCEcho();
4011
4012 if (mapheaderinfo[gamemap-1]->runsoc[0] != '#')
4013 P_RunSOC(mapheaderinfo[gamemap-1]->runsoc);
4014
4015 if (cv_runscripts.value && mapheaderinfo[gamemap-1]->scriptname[0] != '#')
4016 P_RunLevelScript(mapheaderinfo[gamemap-1]->scriptname);
4017
4018 P_InitLevelSettings();
4019
4020 postimgtype = postimgtype2 = postimg_none;
4021
4022 if (mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0')
4023 P_ForceCharacter(mapheaderinfo[gamemap-1]->forcecharacter);
4024
4025 if (!dedicated)
4026 {
4027 // chasecam on in first-person gametypes and 2D
4028 boolean chase = (!(gametyperules & GTR_FIRSTPERSON)) || (maptol & TOL_2D);
4029
4030 // Salt: CV_ClearChangedFlags() messes with your settings :(
4031 /*if (!cv_cam_speed.changed)
4032 CV_Set(&cv_cam_speed, cv_cam_speed.defaultvalue);*/
4033
4034 CV_UpdateCamDist();
4035 CV_UpdateCam2Dist();
4036
4037 if (!cv_chasecam.changed)
4038 CV_SetValue(&cv_chasecam, chase);
4039
4040 // same for second player
4041 if (!cv_chasecam2.changed)
4042 CV_SetValue(&cv_chasecam2, chase);
4043 }
4044
4045 // Initial height of PointOfView
4046 // will be set by player think.
4047 players[consoleplayer].viewz = 1;
4048
4049 // Cancel all d_main.c fadeouts (keep fade in though).
4050 if (reloadinggamestate)
4051 wipegamestate = gamestate; // Don't fade if reloading the gamestate
4052 else
4053 wipegamestate = FORCEWIPEOFF;
4054 wipestyleflags = 0;
4055
4056 // Special stage & record attack retry fade to white
4057 // This is handled BEFORE sounds are stopped.
4058 if (G_GetModeAttackRetryFlag())
4059 {
4060 if (modeattacking && !demoplayback)
4061 {
4062 ranspecialwipe = 2;
4063 wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
4064 }
4065 G_ClearModeAttackRetryFlag();
4066 }
4067 else if (rendermode != render_none && G_IsSpecialStage(gamemap))
4068 {
4069 P_RunSpecialStageWipe();
4070 ranspecialwipe = 1;
4071 }
4072
4073 // Make sure all sounds are stopped before Z_FreeTags.
4074 S_StopSounds();
4075 S_ClearSfx();
4076
4077 // Fade out music here. Deduct 2 tics so the fade volume actually reaches 0.
4078 // But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug.
4079 if (!(reloadinggamestate || titlemapinaction) && (RESETMUSIC ||
4080 strnicmp(S_MusicName(),
4081 (mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap-1]->musname : mapmusname, 7)))
4082 {
4083 S_FadeMusic(0, FixedMul(
4084 FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE));
4085 }
4086
4087 // Let's fade to black here
4088 // But only if we didn't do the special stage wipe
4089 if (rendermode != render_none && !(ranspecialwipe || reloadinggamestate))
4090 P_RunLevelWipe();
4091
4092 if (!(reloadinggamestate || titlemapinaction))
4093 {
4094 if (ranspecialwipe == 2)
4095 {
4096 pausedelay = -3; // preticker plus one
4097 S_StartSound(NULL, sfx_s3k73);
4098 }
4099
4100 // Print "SPEEDING OFF TO [ZONE] [ACT 1]..."
4101 if (rendermode != render_none)
4102 {
4103 // Don't include these in the fade!
4104 char tx[64];
4105 V_DrawSmallString(1, 191, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("Speeding off to..."));
4106 snprintf(tx, 63, "%s%s%s",
4107 mapheaderinfo[gamemap-1]->lvlttl,
4108 (mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " Zone",
4109 (mapheaderinfo[gamemap-1]->actnum > 0) ? va(" %d",mapheaderinfo[gamemap-1]->actnum) : "");
4110 V_DrawSmallString(1, 195, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, tx);
4111 I_UpdateNoVsync();
4112 }
4113
4114 // As oddly named as this is, this handles music only.
4115 // We should be fine starting it here.
4116 // Don't do this during titlemap, because the menu code handles music by itself.
4117 S_Start();
4118 }
4119
4120 levelfadecol = (ranspecialwipe) ? 0 : 31;
4121
4122 // Close text prompt before freeing the old level
4123 F_EndTextPrompt(false, true);
4124
4125 LUA_InvalidateLevel();
4126
4127 for (ss = sectors; sectors+numsectors != ss; ss++)
4128 {
4129 Z_Free(ss->attached);
4130 Z_Free(ss->attachedsolid);
4131 }
4132
4133 // Clear pointers that would be left dangling by the purge
4134 R_FlushTranslationColormapCache();
4135
4136 #ifdef HWRENDER
4137 // Free GPU textures before freeing patches.
4138 if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
4139 HWR_ClearAllTextures();
4140 #endif
4141
4142 Patch_FreeTag(PU_PATCH_LOWPRIORITY);
4143 Patch_FreeTag(PU_PATCH_ROTATED);
4144 Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
4145
4146 P_InitThinkers();
4147 P_InitCachedActions();
4148
4149 if (!fromnetsave && savedata.lives > 0)
4150 {
4151 numgameovers = savedata.numgameovers;
4152 players[consoleplayer].continues = savedata.continues;
4153 players[consoleplayer].lives = savedata.lives;
4154 players[consoleplayer].score = savedata.score;
4155 if ((botingame = ((botskin = savedata.botskin) != 0)))
4156 botcolor = skins[botskin-1].prefcolor;
4157 emeralds = savedata.emeralds;
4158 savedata.lives = 0;
4159 }
4160
4161 // internal game map
4162 maplumpname = G_BuildMapName(gamemap);
4163 lastloadedmaplumpnum = W_CheckNumForName(maplumpname);
4164 if (lastloadedmaplumpnum == LUMPERROR)
4165 I_Error("Map %s not found.\n", maplumpname);
4166
4167 R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);
4168 CON_SetupBackColormap();
4169
4170 // SRB2 determines the sky texture to be used depending on the map header.
4171 P_SetupLevelSky(mapheaderinfo[gamemap-1]->skynum, true);
4172
4173 P_ResetSpawnpoints();
4174
4175 P_ResetWaypoints();
4176
4177 P_MapStart();
4178
4179 if (!P_LoadMapFromFile())
4180 return false;
4181
4182 // init anything that P_SpawnSlopes/P_LoadThings needs to know
4183 P_InitSpecials();
4184
4185 P_SpawnSlopes(fromnetsave);
4186
4187 P_SpawnMapThings(!fromnetsave);
4188 skyboxmo[0] = skyboxviewpnts[0];
4189 skyboxmo[1] = skyboxcenterpnts[0];
4190
4191 for (numcoopstarts = 0; numcoopstarts < MAXPLAYERS; numcoopstarts++)
4192 if (!playerstarts[numcoopstarts])
4193 break;
4194
4195 // set up world state
4196 P_SpawnSpecials(fromnetsave);
4197
4198 if (!fromnetsave) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
4199 P_SpawnPrecipitation();
4200
4201 #ifdef HWRENDER // not win32 only 19990829 by Kin
4202 gl_maploaded = false;
4203
4204 // Lactozilla: Free extrasubsectors regardless of renderer.
4205 HWR_FreeExtraSubsectors();
4206
4207 // Create plane polygons.
4208 if (rendermode == render_opengl)
4209 HWR_LoadLevel();
4210 #endif
4211
4212 // oh god I hope this helps
4213 // (addendum: apparently it does!
4214 // none of this needs to be done because it's not the beginning of the map when
4215 // a netgame save is being loaded, and could actively be harmful by messing with
4216 // the client's view of the data.)
4217 if (!fromnetsave)
4218 P_InitGametype();
4219
4220 if (!reloadinggamestate)
4221 {
4222 P_InitCamera();
4223 localaiming = 0;
4224 localaiming2 = 0;
4225 }
4226
4227 // clear special respawning que
4228 iquehead = iquetail = 0;
4229
4230 P_MapEnd();
4231
4232 // Remove the loading shit from the screen
4233 if (rendermode != render_none && !(titlemapinaction || reloadinggamestate))
4234 F_WipeColorFill(levelfadecol);
4235
4236 if (precache || dedicated)
4237 R_PrecacheLevel();
4238
4239 nextmapoverride = 0;
4240 skipstats = 0;
4241
4242 if (!(netgame || multiplayer || demoplayback) && (!modifiedgame || savemoddata))
4243 mapvisited[gamemap-1] |= MV_VISITED;
4244 else if (netgame || multiplayer)
4245 mapvisited[gamemap-1] |= MV_MP; // you want to record that you've been there this session, but not permanently
4246
4247 levelloading = false;
4248
4249 P_RunCachedActions();
4250
4251 // Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap...
4252 if (!titlemapinaction)
4253 {
4254 if (!lastmaploaded) // Start a new game?
4255 {
4256 // I'd love to do this in the menu code instead of here, but everything's a mess and I can't guarantee saving proper player struct info before the first act's started. You could probably refactor it, but it'd be a lot of effort. Easier to just work off known good code. ~toast 22/06/2020
4257 if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || marathonmode)
4258 && (!modifiedgame || savemoddata) && cursaveslot > 0)
4259 G_SaveGame((UINT32)cursaveslot, gamemap);
4260 // If you're looking for saving sp file progression (distinct from G_SaveGameOver), check G_DoCompleted.
4261 }
4262 lastmaploaded = gamemap; // HAS to be set after saving!!
4263 }
4264
4265 if (!fromnetsave) // uglier hack
4266 { // to make a newly loaded level start on the second frame.
4267 INT32 buf = gametic % BACKUPTICS;
4268 for (i = 0; i < MAXPLAYERS; i++)
4269 {
4270 if (playeringame[i])
4271 G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
4272 }
4273 P_PreTicker(2);
4274 LUAh_MapLoad();
4275 }
4276
4277 // No render mode or reloading gamestate, stop here.
4278 if (rendermode == render_none || reloadinggamestate)
4279 return true;
4280
4281 // Title card!
4282 G_StartTitleCard();
4283
4284 // Can the title card actually run, though?
4285 if (!WipeStageTitle)
4286 return true;
4287 if (ranspecialwipe == 2)
4288 return true;
4289
4290 // If so...
4291 G_PreLevelTitleCard();
4292
4293 return true;
4294 }
4295
4296 //
4297 // P_RunSOC
4298 //
4299 // Runs a SOC file or a lump, depending on if ".SOC" exists in the filename
4300 //
P_RunSOC(const char * socfilename)4301 boolean P_RunSOC(const char *socfilename)
4302 {
4303 lumpnum_t lump;
4304
4305 if (strstr(socfilename, ".soc") != NULL)
4306 return P_AddWadFile(socfilename);
4307
4308 lump = W_CheckNumForName(socfilename);
4309 if (lump == LUMPERROR)
4310 return false;
4311
4312 CONS_Printf(M_GetText("Loading SOC lump: %s\n"), socfilename);
4313 DEH_LoadDehackedLump(lump);
4314
4315 return true;
4316 }
4317
4318 // Auxiliary function for PK3 loading - looks for sound replacements.
4319 // NOTE: it does not really add any new sound entry or anything.
P_LoadSoundsRange(UINT16 wadnum,UINT16 first,UINT16 num)4320 void P_LoadSoundsRange(UINT16 wadnum, UINT16 first, UINT16 num)
4321 {
4322 size_t j;
4323 lumpinfo_t *lumpinfo = wadfiles[wadnum]->lumpinfo + first;
4324 for (; num > 0; num--, lumpinfo++)
4325 {
4326 // Let's check whether it's replacing an existing sound or it's a brand new one.
4327 for (j = 1; j < NUMSFX; j++)
4328 {
4329 if (S_sfx[j].name && !strnicmp(S_sfx[j].name, lumpinfo->name + 2, 6))
4330 {
4331 // the sound will be reloaded when needed,
4332 // since sfx->data will be NULL
4333 CONS_Debug(DBG_SETUP, "Sound %.8s replaced\n", lumpinfo->name);
4334
4335 I_FreeSfx(&S_sfx[j]);
4336 break; // there shouldn't be two sounds with the same name, so stop looking
4337 }
4338 }
4339 }
4340 }
4341
4342 // Auxiliary function for PK3 loading - looks for music and music replacements.
4343 // NOTE: does nothing but print debug messages. The code is handled somewhere else.
P_LoadMusicsRange(UINT16 wadnum,UINT16 first,UINT16 num)4344 void P_LoadMusicsRange(UINT16 wadnum, UINT16 first, UINT16 num)
4345 {
4346 lumpinfo_t *lumpinfo = wadfiles[wadnum]->lumpinfo + first;
4347 char *name;
4348 for (; num > 0; num--, lumpinfo++)
4349 {
4350 name = lumpinfo->name;
4351 if (name[0] == 'O' && name[1] == '_')
4352 {
4353 CONS_Debug(DBG_SETUP, "Music %.8s replaced\n", name);
4354 }
4355 else if (name[0] == 'D' && name[1] == '_')
4356 {
4357 CONS_Debug(DBG_SETUP, "Music %.8s replaced\n", name);
4358 }
4359 }
4360 return;
4361 }
4362
4363 // Auxiliary function - input a folder name and gives us the resource markers positions.
FindFolder(const char * folName,UINT16 * start,UINT16 * end,lumpinfo_t * lumpinfo,UINT16 * pnumlumps,size_t * pi)4364 static lumpinfo_t* FindFolder(const char *folName, UINT16 *start, UINT16 *end, lumpinfo_t *lumpinfo, UINT16 *pnumlumps, size_t *pi)
4365 {
4366 UINT16 numlumps = *pnumlumps;
4367 size_t i = *pi;
4368 if (!stricmp(lumpinfo->fullname, folName))
4369 {
4370 lumpinfo++;
4371 *start = ++i;
4372 for (; i < numlumps; i++, lumpinfo++)
4373 if (strnicmp(lumpinfo->fullname, folName, strlen(folName)))
4374 break;
4375 lumpinfo--;
4376 *end = i-- - *start;
4377 *pi = i;
4378 *pnumlumps = numlumps;
4379 return lumpinfo;
4380 }
4381 return lumpinfo;
4382 }
4383
4384 //
4385 // Add a wadfile to the active wad files,
4386 // replace sounds, musics, patches, textures, sprites and maps
4387 //
P_AddWadFile(const char * wadfilename)4388 boolean P_AddWadFile(const char *wadfilename)
4389 {
4390 size_t i, j, sreplaces = 0, mreplaces = 0, digmreplaces = 0;
4391 UINT16 numlumps, wadnum;
4392 char *name;
4393 lumpinfo_t *lumpinfo;
4394
4395 //boolean texturechange = false; ///\todo Useless; broken when back-frontporting PK3 changes?
4396 boolean mapsadded = false;
4397 boolean replacedcurrentmap = false;
4398
4399 // Vars to help us with the position start and amount of each resource type.
4400 // Useful for PK3s since they use folders.
4401 // WADs use markers for some resources, but others such as sounds are checked lump-by-lump anyway.
4402 // UINT16 luaPos, luaNum = 0;
4403 // UINT16 socPos, socNum = 0;
4404 UINT16 sfxPos = 0, sfxNum = 0;
4405 UINT16 musPos = 0, musNum = 0;
4406 // UINT16 sprPos, sprNum = 0;
4407 UINT16 texPos = 0, texNum = 0;
4408 // UINT16 patPos, patNum = 0;
4409 // UINT16 flaPos, flaNum = 0;
4410 // UINT16 mapPos, mapNum = 0;
4411
4412 // Init file.
4413 if ((numlumps = W_InitFile(wadfilename, false, false)) == INT16_MAX)
4414 {
4415 refreshdirmenu |= REFRESHDIR_NOTLOADED;
4416 return false;
4417 }
4418 else
4419 wadnum = (UINT16)(numwadfiles-1);
4420
4421 switch(wadfiles[wadnum]->type)
4422 {
4423 case RET_PK3:
4424 // Look for the lumps that act as resource delimitation markers.
4425 lumpinfo = wadfiles[wadnum]->lumpinfo;
4426 for (i = 0; i < numlumps; i++, lumpinfo++)
4427 {
4428 // lumpinfo = FindFolder("Lua/", &luaPos, &luaNum, lumpinfo, &numlumps, &i);
4429 // lumpinfo = FindFolder("SOC/", &socPos, &socNum, lumpinfo, &numlumps, &i);
4430 lumpinfo = FindFolder("Sounds/", &sfxPos, &sfxNum, lumpinfo, &numlumps, &i);
4431 lumpinfo = FindFolder("Music/", &musPos, &musNum, lumpinfo, &numlumps, &i);
4432 // lumpinfo = FindFolder("Sprites/", &sprPos, &sprNum, lumpinfo, &numlumps, &i);
4433 lumpinfo = FindFolder("Textures/", &texPos, &texNum, lumpinfo, &numlumps, &i);
4434 // lumpinfo = FindFolder("Patches/", &patPos, &patNum, lumpinfo, &numlumps, &i);
4435 // lumpinfo = FindFolder("Flats/", &flaPos, &flaNum, lumpinfo, &numlumps, &i);
4436 // lumpinfo = FindFolder("Maps/", &mapPos, &mapNum, lumpinfo, &numlumps, &i);
4437 }
4438
4439 // Update the detected resources.
4440 // Note: ALWAYS load Lua scripts first, SOCs right after, and the remaining resources afterwards.
4441 // if (luaNum) // Lua scripts.
4442 // P_LoadLuaScrRange(wadnum, luaPos, luaNum);
4443 // if (socNum) // SOCs.
4444 // P_LoadDehackRange(wadnum, socPos, socNum);
4445 if (sfxNum) // Sounds. TODO: Function currently only updates already existing sounds, the rest is handled somewhere else.
4446 P_LoadSoundsRange(wadnum, sfxPos, sfxNum);
4447 if (musNum) // Music. TODO: Useless function right now.
4448 P_LoadMusicsRange(wadnum, musPos, musNum);
4449 // if (sprNum) // Sprites.
4450 // R_LoadSpritsRange(wadnum, sprPos, sprNum);
4451 // if (texNum) // Textures. TODO: R_LoadTextures() does the folder positioning once again. New function maybe?
4452 // R_LoadTextures();
4453 // if (mapNum) // Maps. TODO: Actually implement the map WAD loading code, lulz.
4454 // P_LoadWadMapRange(wadnum, mapPos, mapNum);
4455 break;
4456 default:
4457 lumpinfo = wadfiles[wadnum]->lumpinfo;
4458 for (i = 0; i < numlumps; i++, lumpinfo++)
4459 {
4460 name = lumpinfo->name;
4461 if (name[0] == 'D')
4462 {
4463 if (name[1] == 'S')
4464 {
4465 for (j = 1; j < NUMSFX; j++)
4466 {
4467 if (S_sfx[j].name && !strnicmp(S_sfx[j].name, name + 2, 6))
4468 {
4469 // the sound will be reloaded when needed,
4470 // since sfx->data will be NULL
4471 CONS_Debug(DBG_SETUP, "Sound %.8s replaced\n", name);
4472
4473 I_FreeSfx(&S_sfx[j]);
4474
4475 sreplaces++;
4476 break; // there shouldn't be two sounds with the same name, so stop looking
4477 }
4478 }
4479 }
4480 else if (name[1] == '_')
4481 {
4482 CONS_Debug(DBG_SETUP, "Music %.8s replaced\n", name);
4483 mreplaces++;
4484 }
4485 }
4486 else if (name[0] == 'O' && name[1] == '_')
4487 {
4488 CONS_Debug(DBG_SETUP, "Music %.8s replaced\n", name);
4489 digmreplaces++;
4490 }
4491 }
4492 break;
4493 }
4494 if (!devparm && sreplaces)
4495 CONS_Printf(M_GetText("%s sounds replaced\n"), sizeu1(sreplaces));
4496 if (!devparm && mreplaces)
4497 CONS_Printf(M_GetText("%s midi musics replaced\n"), sizeu1(mreplaces));
4498 if (!devparm && digmreplaces)
4499 CONS_Printf(M_GetText("%s digital musics replaced\n"), sizeu1(digmreplaces));
4500
4501 #ifdef HWRENDER
4502 // Free GPU textures before freeing patches.
4503 if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
4504 HWR_ClearAllTextures();
4505 #endif
4506
4507 //
4508 // search for sprite replacements
4509 //
4510 Patch_FreeTag(PU_SPRITE);
4511 Patch_FreeTag(PU_PATCH_ROTATED);
4512 R_AddSpriteDefs(wadnum);
4513
4514 // Reload it all anyway, just in case they
4515 // added some textures but didn't insert a
4516 // TEXTURES/etc. list.
4517 R_LoadTextures(); // numtexture changes
4518
4519 // Reload ANIMDEFS
4520 P_InitPicAnims();
4521
4522 // Flush and reload HUD graphics
4523 ST_UnloadGraphics();
4524 HU_LoadGraphics();
4525 ST_LoadGraphics();
4526
4527 //
4528 // look for skins
4529 //
4530 R_AddSkins(wadnum); // faB: wadfile index in wadfiles[]
4531 R_PatchSkins(wadnum); // toast: PATCH PATCH
4532 ST_ReloadSkinFaceGraphics();
4533
4534 //
4535 // edit music defs
4536 //
4537 S_LoadMusicDefs(wadnum);
4538
4539 //
4540 // search for maps
4541 //
4542 lumpinfo = wadfiles[wadnum]->lumpinfo;
4543 for (i = 0; i < numlumps; i++, lumpinfo++)
4544 {
4545 name = lumpinfo->name;
4546 if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers
4547 {
4548 INT16 num;
4549 if (name[5]!='\0')
4550 continue;
4551 num = (INT16)M_MapNumber(name[3], name[4]);
4552
4553 //If you replaced the map you're on, end the level when done.
4554 if (num == gamemap)
4555 replacedcurrentmap = true;
4556
4557 CONS_Printf("%s\n", name);
4558 mapsadded = true;
4559 }
4560 }
4561 if (!mapsadded)
4562 CONS_Printf(M_GetText("No maps added\n"));
4563
4564 R_LoadSpriteInfoLumps(wadnum, numlumps);
4565
4566 #ifdef HWRENDER
4567 HWR_ReloadModels();
4568 #endif
4569
4570 // reload status bar (warning should have valid player!)
4571 if (gamestate == GS_LEVEL)
4572 ST_Start();
4573
4574 // Prevent savefile cheating
4575 if (cursaveslot > 0)
4576 cursaveslot = 0;
4577
4578 if (replacedcurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer))
4579 {
4580 CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap);
4581 if (server)
4582 SendNetXCmd(XD_EXITLEVEL, NULL, 0);
4583 }
4584
4585 return true;
4586 }
4587