1 // Emacs style mode select	 -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 //		Do all the WAD I/O, get map description,
21 //		set up initial state and misc. LUTs.
22 //
23 //-----------------------------------------------------------------------------
24 
25 
26 #include <math.h>
27 #ifdef _MSC_VER
28 #include <malloc.h>		// for alloca()
29 #endif
30 
31 #include "templates.h"
32 #include "m_argv.h"
33 #include "m_swap.h"
34 #include "m_bbox.h"
35 #include "g_game.h"
36 #include "i_system.h"
37 #include "x86.h"
38 #include "w_wad.h"
39 #include "doomdef.h"
40 #include "p_local.h"
41 #include "p_effect.h"
42 #include "p_terrain.h"
43 #include "nodebuild.h"
44 #include "s_sound.h"
45 #include "doomstat.h"
46 #include "p_lnspec.h"
47 #include "v_palette.h"
48 #include "c_console.h"
49 #include "c_cvars.h"
50 #include "p_acs.h"
51 #include "announcer.h"
52 #include "wi_stuff.h"
53 #include "stats.h"
54 #include "doomerrors.h"
55 #include "gi.h"
56 #include "p_conversation.h"
57 #include "a_keys.h"
58 #include "s_sndseq.h"
59 #include "sbar.h"
60 #include "p_setup.h"
61 #include "r_data/r_translate.h"
62 #include "r_data/r_interpolate.h"
63 #include "r_sky.h"
64 #include "cmdlib.h"
65 #include "g_level.h"
66 #include "md5.h"
67 #include "compatibility.h"
68 #include "po_man.h"
69 #include "r_renderer.h"
70 #include "r_data/colormaps.h"
71 
72 #include "fragglescript/t_fs.h"
73 
74 #define MISSING_TEXTURE_WARN_LIMIT		20
75 
76 void P_SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable);
77 void P_SetSlopes ();
78 void P_CopySlopes();
79 void BloodCrypt (void *data, int key, int len);
80 void P_ClearUDMFKeys();
81 
82 extern AActor *P_SpawnMapThing (FMapThing *mthing, int position);
83 extern bool P_LoadBuildMap (BYTE *mapdata, size_t len, FMapThing **things, int *numthings);
84 
85 extern void P_TranslateTeleportThings (void);
86 
87 void P_ParseTextMap(MapData *map, FMissingTextureTracker &);
88 
89 extern int numinterpolations;
90 extern unsigned int R_OldBlend;
91 
92 EXTERN_CVAR(Bool, am_textured)
93 
94 CVAR (Bool, genblockmap, false, CVAR_SERVERINFO|CVAR_GLOBALCONFIG);
95 CVAR (Bool, gennodes, false, CVAR_SERVERINFO|CVAR_GLOBALCONFIG);
96 CVAR (Bool, genglnodes, false, CVAR_SERVERINFO);
97 CVAR (Bool, showloadtimes, false, 0);
98 
99 static void P_Shutdown ();
100 
101 bool P_IsBuildMap(MapData *map);
102 
103 
104 //
105 // MAP related Lookup tables.
106 // Store VERTEXES, LINEDEFS, SIDEDEFS, etc.
107 //
108 int 			numvertexes;
109 vertex_t*		vertexes;
110 int 			numvertexdatas;
111 vertexdata_t*		vertexdatas;
112 
113 int 			numsegs;
114 seg_t*			segs;
115 glsegextra_t*	glsegextras;
116 
117 int 			numsectors;
118 sector_t*		sectors;
119 
120 int 			numsubsectors;
121 subsector_t*	subsectors;
122 
123 int 			numnodes;
124 node_t* 		nodes;
125 
126 int 			numlines;
127 line_t* 		lines;
128 
129 int 			numsides;
130 side_t* 		sides;
131 
132 int				numzones;
133 zone_t*			zones;
134 
135 node_t * 		gamenodes;
136 int 			numgamenodes;
137 
138 subsector_t * 	gamesubsectors;
139 int 			numgamesubsectors;
140 
141 bool			hasglnodes;
142 
143 TArray<FMapThing> MapThingsConverted;
144 TMap<unsigned,unsigned>  MapThingsUserDataIndex;	// from mapthing idx -> user data idx
145 TArray<FMapThingUserData> MapThingsUserData;
146 
147 int sidecount;
148 sidei_t *sidetemp;
149 
150 TArray<int>		linemap;
151 
152 // BLOCKMAP
153 // Created from axis aligned bounding box
154 // of the map, a rectangular array of
155 // blocks of size 256x256.
156 // Used to speed up collision detection
157 // by spatial subdivision in 2D.
158 //
159 // Blockmap size.
160 int 			bmapwidth;
161 int 			bmapheight; 	// size in mapblocks
162 
163 int				*blockmap;		// int for larger maps ([RH] Made int because BOOM does)
164 int				*blockmaplump;	// offsets in blockmap are from here
165 
166 fixed_t 		bmaporgx;		// origin of block map
167 fixed_t 		bmaporgy;
168 int				bmapnegx;		// min negs of block map before wrapping
169 int				bmapnegy;
170 
171 FBlockNode**	blocklinks;		// for thing chains
172 
173 
174 // REJECT
175 // For fast sight rejection.
176 // Speeds up enemy AI by skipping detailed
177 //	LineOf Sight calculation.
178 // Without special effect, this could be
179 //	used as a PVS lookup as well.
180 //
181 BYTE*			rejectmatrix;
182 
183 bool		ForceNodeBuild;
184 
185 // Maintain single and multi player starting spots.
186 TArray<FPlayerStart> deathmatchstarts (16);
187 FPlayerStart		playerstarts[MAXPLAYERS];
188 TArray<FPlayerStart> AllPlayerStarts;
189 
190 static void P_AllocateSideDefs (int count);
191 
192 
193 //===========================================================================
194 //
195 // GetMapIndex
196 //
197 // Gets the type of map lump or -1 if invalid or -2 if required and not found.
198 //
199 //===========================================================================
200 
201 struct checkstruct
202 {
203 	const char lumpname[9];
204 	bool  required;
205 };
206 
GetMapIndex(const char * mapname,int lastindex,const char * lumpname,bool needrequired)207 static int GetMapIndex(const char *mapname, int lastindex, const char *lumpname, bool needrequired)
208 {
209 	static const checkstruct check[] =
210 	{
211 		{"",		 true},
212 		{"THINGS",	 true},
213 		{"LINEDEFS", true},
214 		{"SIDEDEFS", true},
215 		{"VERTEXES", true},
216 		{"SEGS",	 false},
217 		{"SSECTORS", false},
218 		{"NODES",	 false},
219 		{"SECTORS",	 true},
220 		{"REJECT",	 false},
221 		{"BLOCKMAP", false},
222 		{"BEHAVIOR", false},
223 		//{"SCRIPTS",	 false},
224 	};
225 
226 	if (lumpname==NULL) lumpname="";
227 
228 	for(size_t i=lastindex+1;i<countof(check);i++)
229 	{
230 		if (!strnicmp(lumpname, check[i].lumpname, 8))
231 			return (int)i;
232 
233 		if (check[i].required)
234 		{
235 			if (needrequired)
236 			{
237 				I_Error("'%s' not found in %s\n", check[i].lumpname, mapname);
238 			}
239 			return -2;
240 		}
241 	}
242 
243 	return -1;	// End of map reached
244 }
245 
246 //===========================================================================
247 //
248 // Opens a map for reading
249 //
250 //===========================================================================
251 
P_OpenMapData(const char * mapname,bool justcheck)252 MapData *P_OpenMapData(const char * mapname, bool justcheck)
253 {
254 	MapData * map = new MapData;
255 	FileReader * wadReader = NULL;
256 	bool externalfile = !strnicmp(mapname, "file:", 5);
257 
258 	if (externalfile)
259 	{
260 		mapname += 5;
261 		if (!FileExists(mapname))
262 		{
263 			delete map;
264 			return NULL;
265 		}
266 		map->resource = FResourceFile::OpenResourceFile(mapname, NULL, true);
267 		wadReader = map->resource->GetReader();
268 	}
269 	else
270 	{
271 		FString fmt;
272 		int lump_wad;
273 		int lump_map;
274 		int lump_name = -1;
275 
276 		// Check for both *.wad and *.map in order to load Build maps
277 		// as well. The higher one will take precedence.
278 		// Names with more than 8 characters will only be checked as .wad and .map.
279 		if (strlen(mapname) <= 8) lump_name = Wads.CheckNumForName(mapname);
280 		fmt.Format("maps/%s.wad", mapname);
281 		lump_wad = Wads.CheckNumForFullName(fmt);
282 		fmt.Format("maps/%s.map", mapname);
283 		lump_map = Wads.CheckNumForFullName(fmt);
284 
285 		if (lump_name > lump_wad && lump_name > lump_map && lump_name != -1)
286 		{
287 			int lumpfile = Wads.GetLumpFile(lump_name);
288 			int nextfile = Wads.GetLumpFile(lump_name+1);
289 
290 			map->lumpnum = lump_name;
291 
292 			if (lumpfile != nextfile)
293 			{
294 				// The following lump is from a different file so whatever this is,
295 				// it is not a multi-lump Doom level so let's assume it is a Build map.
296 				map->MapLumps[0].Reader = map->file = Wads.ReopenLumpNum(lump_name);
297 				if (!P_IsBuildMap(map))
298 				{
299 					delete map;
300 					return NULL;
301 				}
302 				return map;
303 			}
304 
305 			// This case can only happen if the lump is inside a real WAD file.
306 			// As such any special handling for other types of lumps is skipped.
307 			map->MapLumps[0].Reader = map->file = Wads.ReopenLumpNum(lump_name);
308 			strncpy(map->MapLumps[0].Name, Wads.GetLumpFullName(lump_name), 8);
309 			map->Encrypted = Wads.IsEncryptedFile(lump_name);
310 			map->InWad = true;
311 
312 			if (map->Encrypted)
313 			{ // If it's encrypted, then it's a Blood file, presumably a map.
314 				if (!P_IsBuildMap(map))
315 				{
316 					delete map;
317 					return NULL;
318 				}
319 				return map;
320 			}
321 
322 			int index = 0;
323 
324 			if (stricmp(Wads.GetLumpFullName(lump_name + 1), "TEXTMAP") != 0)
325 			{
326 				for(int i = 1;; i++)
327 				{
328 					// Since levels must be stored in WADs they can't really have full
329 					// names and for any valid level lump this always returns the short name.
330 					const char * lumpname = Wads.GetLumpFullName(lump_name + i);
331 					try
332 					{
333 						index = GetMapIndex(mapname, index, lumpname, !justcheck);
334 					}
335 					catch(...)
336 					{
337 						delete map;
338 						throw;
339 					}
340 					if (index == -2)
341 					{
342 						delete map;
343 						return NULL;
344 					}
345 					if (index == ML_BEHAVIOR) map->HasBehavior = true;
346 
347 					// The next lump is not part of this map anymore
348 					if (index < 0) break;
349 
350 					map->MapLumps[index].Reader = Wads.ReopenLumpNum(lump_name + i);
351 					strncpy(map->MapLumps[index].Name, lumpname, 8);
352 				}
353 			}
354 			else
355 			{
356 				map->isText = true;
357 				map->MapLumps[1].Reader = Wads.ReopenLumpNum(lump_name + 1);
358 				for(int i = 2;; i++)
359 				{
360 					const char * lumpname = Wads.GetLumpFullName(lump_name + i);
361 
362 					if (lumpname == NULL)
363 					{
364 						I_Error("Invalid map definition for %s", mapname);
365 					}
366 					else if (!stricmp(lumpname, "ZNODES"))
367 					{
368 						index = ML_GLZNODES;
369 					}
370 					else if (!stricmp(lumpname, "BLOCKMAP"))
371 					{
372 						// there is no real point in creating a blockmap but let's use it anyway
373 						index = ML_BLOCKMAP;
374 					}
375 					else if (!stricmp(lumpname, "REJECT"))
376 					{
377 						index = ML_REJECT;
378 					}
379 					else if (!stricmp(lumpname, "DIALOGUE"))
380 					{
381 						index = ML_CONVERSATION;
382 					}
383 					else if (!stricmp(lumpname, "BEHAVIOR"))
384 					{
385 						index = ML_BEHAVIOR;
386 						map->HasBehavior = true;
387 					}
388 					else if (!stricmp(lumpname, "ENDMAP"))
389 					{
390 						break;
391 					}
392 					else continue;
393 					map->MapLumps[index].Reader = Wads.ReopenLumpNum(lump_name + i);
394 					strncpy(map->MapLumps[index].Name, lumpname, 8);
395 				}
396 			}
397 			return map;
398 		}
399 		else
400 		{
401 			if (lump_map > lump_wad)
402 			{
403 				lump_wad = lump_map;
404 			}
405 			if (lump_wad == -1)
406 			{
407 				delete map;
408 				return NULL;
409 			}
410 			map->lumpnum = lump_wad;
411 			map->resource = FResourceFile::OpenResourceFile(Wads.GetLumpFullName(lump_wad), Wads.ReopenLumpNum(lump_wad), true);
412 			wadReader = map->resource->GetReader();
413 		}
414 	}
415 	DWORD id;
416 
417 	// Although we're using the resource system, we still want to be sure we're
418 	// reading from a wad file.
419 	wadReader->Seek(0, SEEK_SET);
420 	wadReader->Read(&id, sizeof(id));
421 
422 	if (id == IWAD_ID || id == PWAD_ID)
423 	{
424 		char maplabel[9]="";
425 		int index=0;
426 
427 		map->MapLumps[0].Reader = map->resource->GetLump(0)->NewReader();
428 		strncpy(map->MapLumps[0].Name, map->resource->GetLump(0)->Name, 8);
429 
430 		for(DWORD i = 1; i < map->resource->LumpCount(); i++)
431 		{
432 			const char* lumpname = map->resource->GetLump(i)->Name;
433 
434 			if (i == 1 && !strnicmp(lumpname, "TEXTMAP", 8))
435 			{
436 				map->isText = true;
437 				map->MapLumps[ML_TEXTMAP].Reader = map->resource->GetLump(i)->NewReader();
438 				strncpy(map->MapLumps[ML_TEXTMAP].Name, lumpname, 8);
439 				for(int i = 2;; i++)
440 				{
441 					lumpname = map->resource->GetLump(i)->Name;
442 					if (!strnicmp(lumpname, "ZNODES",8))
443 					{
444 						index = ML_GLZNODES;
445 					}
446 					else if (!strnicmp(lumpname, "BLOCKMAP",8))
447 					{
448 						// there is no real point in creating a blockmap but let's use it anyway
449 						index = ML_BLOCKMAP;
450 					}
451 					else if (!strnicmp(lumpname, "REJECT",8))
452 					{
453 						index = ML_REJECT;
454 					}
455 					else if (!strnicmp(lumpname, "DIALOGUE",8))
456 					{
457 						index = ML_CONVERSATION;
458 					}
459 					else if (!strnicmp(lumpname, "BEHAVIOR",8))
460 					{
461 						index = ML_BEHAVIOR;
462 						map->HasBehavior = true;
463 					}
464 					else if (!strnicmp(lumpname, "ENDMAP",8))
465 					{
466 						return map;
467 					}
468 					else continue;
469 					map->MapLumps[index].Reader = map->resource->GetLump(i)->NewReader();
470 					strncpy(map->MapLumps[index].Name, lumpname, 8);
471 				}
472 			}
473 
474 			if (i>0)
475 			{
476 				try
477 				{
478 					index = GetMapIndex(maplabel, index, lumpname, !justcheck);
479 				}
480 				catch(...)
481 				{
482 					delete map;
483 					throw;
484 				}
485 				if (index == -2)
486 				{
487 					delete map;
488 					return NULL;
489 				}
490 				if (index == ML_BEHAVIOR) map->HasBehavior = true;
491 
492 				// The next lump is not part of this map anymore
493 				if (index < 0) break;
494 			}
495 			else
496 			{
497 				strncpy(maplabel, lumpname, 8);
498 				maplabel[8]=0;
499 			}
500 
501 			map->MapLumps[index].Reader = map->resource->GetLump(i)->NewReader();
502 			strncpy(map->MapLumps[index].Name, lumpname, 8);
503 		}
504 	}
505 	else
506 	{
507 		// This is a Build map and not subject to WAD consistency checks.
508 		//map->MapLumps[0].Size = wadReader->GetLength();
509 		if (!P_IsBuildMap(map))
510 		{
511 			delete map;
512 			return NULL;
513 		}
514 	}
515 	return map;
516 }
517 
P_CheckMapData(const char * mapname)518 bool P_CheckMapData(const char *mapname)
519 {
520 	MapData *mapd = P_OpenMapData(mapname, true);
521 	if (mapd == NULL) return false;
522 	delete mapd;
523 	return true;
524 }
525 
526 //===========================================================================
527 //
528 // MapData :: GetChecksum
529 //
530 // Hashes a map based on its header, THINGS, LINEDEFS, SIDEDEFS, SECTORS,
531 // and BEHAVIOR lumps. Node-builder generated lumps are not included.
532 //
533 //===========================================================================
534 
GetChecksum(BYTE cksum[16])535 void MapData::GetChecksum(BYTE cksum[16])
536 {
537 	MD5Context md5;
538 
539 	if (isText)
540 	{
541 		Seek(ML_TEXTMAP);
542 		if (file != NULL) md5.Update(file, Size(ML_TEXTMAP));
543 	}
544 	else
545 	{
546 		if (Size(ML_LABEL) != 0)
547 		{
548 			Seek(ML_LABEL);
549 			if (file != NULL) md5.Update(file, Size(ML_LABEL));
550 		}
551 		Seek(ML_THINGS);
552 		if (file != NULL) md5.Update(file, Size(ML_THINGS));
553 		Seek(ML_LINEDEFS);
554 		if (file != NULL) md5.Update(file, Size(ML_LINEDEFS));
555 		Seek(ML_SIDEDEFS);
556 		if (file != NULL) md5.Update(file, Size(ML_SIDEDEFS));
557 		Seek(ML_SECTORS);
558 		if (file != NULL) md5.Update(file, Size(ML_SECTORS));
559 	}
560 	if (HasBehavior)
561 	{
562 		Seek(ML_BEHAVIOR);
563 		if (file != NULL) md5.Update(file, Size(ML_BEHAVIOR));
564 	}
565 	md5.Final(cksum);
566 }
567 
568 
569 //===========================================================================
570 //
571 // Sets a sidedef's texture and prints a message if it's not present.
572 //
573 //===========================================================================
574 
SetTexture(side_t * side,int position,const char * name,FMissingTextureTracker & track)575 static void SetTexture (side_t *side, int position, const char *name, FMissingTextureTracker &track)
576 {
577 	static const char *positionnames[] = { "top", "middle", "bottom" };
578 	static const char *sidenames[] = { "first", "second" };
579 
580 	FTextureID texture = TexMan.CheckForTexture (name, FTexture::TEX_Wall,
581 			FTextureManager::TEXMAN_Overridable|FTextureManager::TEXMAN_TryAny);
582 
583 	if (!texture.Exists())
584 	{
585 		if (++track[name].Count <= MISSING_TEXTURE_WARN_LIMIT)
586 		{
587 			// Print an error that lists all references to this sidedef.
588 			// We must scan the linedefs manually for all references to this sidedef.
589 			for(int i = 0; i < numlines; i++)
590 			{
591 				for(int j = 0; j < 2; j++)
592 				{
593 					if (lines[i].sidedef[j] == side)
594 					{
595 						Printf(TEXTCOLOR_RED"Unknown %s texture '"
596 							TEXTCOLOR_ORANGE "%s" TEXTCOLOR_RED
597 							"' on %s side of linedef %d\n",
598 							positionnames[position], name, sidenames[j], i);
599 					}
600 				}
601 			}
602 		}
603 		texture = TexMan.GetDefaultTexture();
604 	}
605 	side->SetTexture(position, texture);
606 }
607 
608 //===========================================================================
609 //
610 // Sets a sidedef's texture and prints a message if it's not present.
611 // (Passing index separately is for UDMF which does not have sectors allocated yet)
612 //
613 //===========================================================================
614 
SetTexture(sector_t * sector,int index,int position,const char * name,FMissingTextureTracker & track,bool truncate)615 void SetTexture (sector_t *sector, int index, int position, const char *name, FMissingTextureTracker &track, bool truncate)
616 {
617 	static const char *positionnames[] = { "floor", "ceiling" };
618 	char name8[9];
619 	if (truncate)
620 	{
621 		strncpy(name8, name, 8);
622 		name8[8] = 0;
623 		name = name8;
624 	}
625 
626 	FTextureID texture = TexMan.CheckForTexture (name, FTexture::TEX_Flat,
627 			FTextureManager::TEXMAN_Overridable|FTextureManager::TEXMAN_TryAny);
628 
629 	if (!texture.Exists())
630 	{
631 		if (++track[name].Count <= MISSING_TEXTURE_WARN_LIMIT)
632 		{
633 			Printf(TEXTCOLOR_RED"Unknown %s texture '"
634 				TEXTCOLOR_ORANGE "%s" TEXTCOLOR_RED
635 				"' in sector %d\n",
636 				positionnames[position], name, index);
637 		}
638 		texture = TexMan.GetDefaultTexture();
639 	}
640 	sector->SetTexture(position, texture);
641 }
642 
643 //===========================================================================
644 //
645 // SummarizeMissingTextures
646 //
647 // Lists textures that were missing more than MISSING_TEXTURE_WARN_LIMIT
648 // times.
649 //
650 //===========================================================================
651 
SummarizeMissingTextures(const FMissingTextureTracker & missing)652 static void SummarizeMissingTextures(const FMissingTextureTracker &missing)
653 {
654 	FMissingTextureTracker::ConstIterator it(missing);
655 	FMissingTextureTracker::ConstPair *pair;
656 
657 	while (it.NextPair(pair))
658 	{
659 		if (pair->Value.Count > MISSING_TEXTURE_WARN_LIMIT)
660 		{
661 			Printf(TEXTCOLOR_RED "Missing texture '"
662 				TEXTCOLOR_ORANGE "%s" TEXTCOLOR_RED
663 				"' is used %d more times\n",
664 				pair->Key.GetChars(), pair->Value.Count - MISSING_TEXTURE_WARN_LIMIT);
665 		}
666 	}
667 }
668 
669 //===========================================================================
670 //
671 // [RH] Figure out blends for deep water sectors
672 //
673 //===========================================================================
674 
SetTexture(side_t * side,int position,DWORD * blend,const char * name)675 static void SetTexture (side_t *side, int position, DWORD *blend, const char *name)
676 {
677 	FTextureID texture;
678 	if ((*blend = R_ColormapNumForName (name)) == 0)
679 	{
680 		texture = TexMan.CheckForTexture (name, FTexture::TEX_Wall,
681 			FTextureManager::TEXMAN_Overridable|FTextureManager::TEXMAN_TryAny);
682 		if (!texture.Exists())
683 		{
684 			char name2[9];
685 			char *stop;
686 			strncpy (name2, name, 8);
687 			name2[8] = 0;
688 			*blend = strtoul (name2, &stop, 16);
689 			texture = FNullTextureID();
690 		}
691 		else
692 		{
693 			*blend = 0;
694 		}
695 	}
696 	else
697 	{
698 		texture = FNullTextureID();
699 	}
700 	side->SetTexture(position, texture);
701 }
702 
SetTextureNoErr(side_t * side,int position,DWORD * color,const char * name,bool * validcolor,bool isFog)703 static void SetTextureNoErr (side_t *side, int position, DWORD *color, const char *name, bool *validcolor, bool isFog)
704 {
705 	FTextureID texture;
706 	*validcolor = false;
707 	texture = TexMan.CheckForTexture (name, FTexture::TEX_Wall,
708 		FTextureManager::TEXMAN_Overridable|FTextureManager::TEXMAN_TryAny);
709 	if (!texture.Exists())
710 	{
711 		char name2[9];
712 		char *stop;
713 		strncpy (name2, name+1, 7);
714 		name2[7] = 0;
715 		if (*name != '#')
716 		{
717 			*color = strtoul (name, &stop, 16);
718 			texture = FNullTextureID();
719 			*validcolor = (*stop == 0) && (stop >= name + 2) && (stop <= name + 6);
720 			return;
721 		}
722 		else	// Support for Legacy's color format!
723 		{
724 			int l=(int)strlen(name);
725 			texture = FNullTextureID();
726 			*validcolor = false;
727 			if (l>=7)
728 			{
729 				for(stop=name2;stop<name2+6;stop++) if (!isxdigit(*stop)) *stop='0';
730 
731 				int factor = l==7? 0 : clamp<int> ((name2[6]&223)-'A', 0, 25);
732 
733 				name2[6]=0; int blue=strtol(name2+4,NULL,16);
734 				name2[4]=0; int green=strtol(name2+2,NULL,16);
735 				name2[2]=0; int red=strtol(name2,NULL,16);
736 
737 				if (!isFog)
738 				{
739 					if (factor==0)
740 					{
741 						*validcolor=false;
742 						return;
743 					}
744 					factor = factor * 255 / 25;
745 				}
746 				else
747 				{
748 					factor=0;
749 				}
750 
751 				*color=MAKEARGB(factor, red, green, blue);
752 				texture = FNullTextureID();
753 				*validcolor = true;
754 				return;
755 			}
756 		}
757 		texture = FNullTextureID();
758 	}
759 	side->SetTexture(position, texture);
760 }
761 
762 //===========================================================================
763 //
764 // Sound enviroment handling
765 //
766 //===========================================================================
767 
P_FloodZone(sector_t * sec,int zonenum)768 void P_FloodZone (sector_t *sec, int zonenum)
769 {
770 	int i;
771 
772 	if (sec->ZoneNumber == zonenum)
773 		return;
774 
775 	sec->ZoneNumber = zonenum;
776 
777 	for (i = 0; i < sec->linecount; ++i)
778 	{
779 		line_t *check = sec->lines[i];
780 		sector_t *other;
781 
782 		if (check->sidedef[1] == NULL || (check->flags & ML_ZONEBOUNDARY))
783 			continue;
784 
785 		if (check->frontsector == sec)
786 		{
787 			assert(check->backsector != NULL);
788 			other = check->backsector;
789 		}
790 		else
791 		{
792 			assert(check->frontsector != NULL);
793 			other = check->frontsector;
794 		}
795 
796 		if (other->ZoneNumber != zonenum)
797 			P_FloodZone (other, zonenum);
798 	}
799 }
800 
P_FloodZones()801 void P_FloodZones ()
802 {
803 	int z = 0, i;
804 	ReverbContainer *reverb;
805 
806 	for (i = 0; i < numsectors; ++i)
807 	{
808 		if (sectors[i].ZoneNumber == 0xFFFF)
809 		{
810 			P_FloodZone (&sectors[i], z++);
811 		}
812 	}
813 	numzones = z;
814 	zones = new zone_t[z];
815 	reverb = S_FindEnvironment(level.DefaultEnvironment);
816 	if (reverb == NULL)
817 	{
818 		Printf("Sound environment %d, %d not found\n", level.DefaultEnvironment >> 8, level.DefaultEnvironment & 255);
819 		reverb = DefaultEnvironments[0];
820 	}
821 	for (i = 0; i < z; ++i)
822 	{
823 		zones[i].Environment = reverb;
824 	}
825 }
826 
827 //===========================================================================
828 //
829 // P_LoadVertexes
830 //
831 //===========================================================================
832 
P_LoadVertexes(MapData * map)833 void P_LoadVertexes (MapData * map)
834 {
835 	int i;
836 
837 	// Determine number of vertices:
838 	//	total lump length / vertex record length.
839 	numvertexes = map->Size(ML_VERTEXES) / sizeof(mapvertex_t);
840 	numvertexdatas = 0;
841 
842 	if (numvertexes == 0)
843 	{
844 		I_Error ("Map has no vertices.\n");
845 	}
846 
847 	// Allocate memory for buffer.
848 	vertexes = new vertex_t[numvertexes];
849 	vertexdatas = NULL;
850 
851 	map->Seek(ML_VERTEXES);
852 
853 	// Copy and convert vertex coordinates, internal representation as fixed.
854 	for (i = 0; i < numvertexes; i++)
855 	{
856 		SWORD x, y;
857 
858 		(*map->file) >> x >> y;
859 		vertexes[i].x = x << FRACBITS;
860 		vertexes[i].y = y << FRACBITS;
861 	}
862 }
863 
864 //===========================================================================
865 //
866 // P_LoadZSegs
867 //
868 //===========================================================================
869 
P_LoadZSegs(FileReaderBase & data)870 void P_LoadZSegs (FileReaderBase &data)
871 {
872 	for (int i = 0; i < numsegs; ++i)
873 	{
874 		line_t *ldef;
875 		DWORD v1, v2;
876 		WORD line;
877 		BYTE side;
878 
879 		data >> v1 >> v2 >> line >> side;
880 
881 		segs[i].v1 = &vertexes[v1];
882 		segs[i].v2 = &vertexes[v2];
883 		segs[i].linedef = ldef = &lines[line];
884 		segs[i].sidedef = ldef->sidedef[side];
885 		segs[i].frontsector = ldef->sidedef[side]->sector;
886 		if (ldef->flags & ML_TWOSIDED && ldef->sidedef[side^1] != NULL)
887 		{
888 			segs[i].backsector = ldef->sidedef[side^1]->sector;
889 		}
890 		else
891 		{
892 			segs[i].backsector = 0;
893 			ldef->flags &= ~ML_TWOSIDED;
894 		}
895 	}
896 }
897 
898 //===========================================================================
899 //
900 // P_LoadGLZSegs
901 //
902 // This is the GL nodes version of the above function.
903 //
904 //===========================================================================
905 
P_LoadGLZSegs(FileReaderBase & data,int type)906 void P_LoadGLZSegs (FileReaderBase &data, int type)
907 {
908 	for (int i = 0; i < numsubsectors; ++i)
909 	{
910 		for (size_t j = 0; j < subsectors[i].numlines; ++j)
911 		{
912 			seg_t *seg;
913 			DWORD v1, partner;
914 			DWORD line;
915 			WORD lineword;
916 			BYTE side;
917 
918 			data >> v1 >> partner;
919 			if (type >= 2)
920 			{
921 				data >> line;
922 			}
923 			else
924 			{
925 				data >> lineword;
926 				line = lineword == 0xFFFF ? 0xFFFFFFFF : lineword;
927 			}
928 			data >> side;
929 
930 			seg = subsectors[i].firstline + j;
931 			seg->v1 = &vertexes[v1];
932 			if (j == 0)
933 			{
934 				seg[subsectors[i].numlines - 1].v2 = seg->v1;
935 			}
936 			else
937 			{
938 				seg[-1].v2 = seg->v1;
939 			}
940 			glsegextras[seg - segs].PartnerSeg = partner;
941 			if (line != 0xFFFFFFFF)
942 			{
943 				line_t *ldef;
944 
945 				seg->linedef = ldef = &lines[line];
946 				seg->sidedef = ldef->sidedef[side];
947 				seg->frontsector = ldef->sidedef[side]->sector;
948 				if (ldef->flags & ML_TWOSIDED && ldef->sidedef[side^1] != NULL)
949 				{
950 					seg->backsector = ldef->sidedef[side^1]->sector;
951 				}
952 				else
953 				{
954 					seg->backsector = 0;
955 					ldef->flags &= ~ML_TWOSIDED;
956 				}
957 			}
958 			else
959 			{
960 				seg->linedef = NULL;
961 				seg->sidedef = NULL;
962 				seg->frontsector = seg->backsector = subsectors[i].firstline->frontsector;
963 			}
964 		}
965 	}
966 }
967 
968 //===========================================================================
969 //
970 // P_LoadZNodes
971 //
972 //===========================================================================
973 
LoadZNodes(FileReaderBase & data,int glnodes)974 void LoadZNodes(FileReaderBase &data, int glnodes)
975 {
976 	// Read extra vertices added during node building
977 	DWORD orgVerts, newVerts;
978 	vertex_t *newvertarray;
979 	unsigned int i;
980 
981 	data >> orgVerts >> newVerts;
982 	if (orgVerts > (DWORD)numvertexes)
983 	{ // These nodes are based on a map with more vertex data than we have.
984 	  // We can't use them.
985 		throw CRecoverableError("Incorrect number of vertexes in nodes.\n");
986 	}
987 	if (orgVerts + newVerts == (DWORD)numvertexes)
988 	{
989 		newvertarray = vertexes;
990 	}
991 	else
992 	{
993 		newvertarray = new vertex_t[orgVerts + newVerts];
994 		memcpy (newvertarray, vertexes, orgVerts * sizeof(vertex_t));
995 	}
996 	for (i = 0; i < newVerts; ++i)
997 	{
998 		data >> newvertarray[i + orgVerts].x >> newvertarray[i + orgVerts].y;
999 	}
1000 	if (vertexes != newvertarray)
1001 	{
1002 		for (i = 0; i < (DWORD)numlines; ++i)
1003 		{
1004 			lines[i].v1 = lines[i].v1 - vertexes + newvertarray;
1005 			lines[i].v2 = lines[i].v2 - vertexes + newvertarray;
1006 		}
1007 		delete[] vertexes;
1008 		vertexes = newvertarray;
1009 		numvertexes = orgVerts + newVerts;
1010 	}
1011 
1012 	// Read the subsectors
1013 	DWORD numSubs, currSeg;
1014 
1015 	data >> numSubs;
1016 	numsubsectors = numSubs;
1017 	subsectors = new subsector_t[numSubs];
1018 	memset (subsectors, 0, numsubsectors*sizeof(subsector_t));
1019 
1020 	for (i = currSeg = 0; i < numSubs; ++i)
1021 	{
1022 		DWORD numsegs;
1023 
1024 		data >> numsegs;
1025 		subsectors[i].firstline = (seg_t *)(size_t)currSeg;		// Oh damn. I should have stored the seg count sooner.
1026 		subsectors[i].numlines = numsegs;
1027 		currSeg += numsegs;
1028 	}
1029 
1030 	// Read the segs
1031 	DWORD numSegs;
1032 
1033 	data >> numSegs;
1034 
1035 	// The number of segs stored should match the number of
1036 	// segs used by subsectors.
1037 	if (numSegs != currSeg)
1038 	{
1039 		throw CRecoverableError("Incorrect number of segs in nodes.\n");
1040 	}
1041 
1042 	numsegs = numSegs;
1043 	segs = new seg_t[numsegs];
1044 	memset (segs, 0, numsegs*sizeof(seg_t));
1045 	glsegextras = NULL;
1046 
1047 	for (i = 0; i < numSubs; ++i)
1048 	{
1049 		subsectors[i].firstline = &segs[(size_t)subsectors[i].firstline];
1050 	}
1051 
1052 	if (glnodes == 0)
1053 	{
1054 		P_LoadZSegs (data);
1055 	}
1056 	else
1057 	{
1058 		glsegextras = new glsegextra_t[numsegs];
1059 		P_LoadGLZSegs (data, glnodes);
1060 	}
1061 
1062 	// Read nodes
1063 	DWORD numNodes;
1064 
1065 	data >> numNodes;
1066 	numnodes = numNodes;
1067 	nodes = new node_t[numNodes];
1068 	memset (nodes, 0, sizeof(node_t)*numNodes);
1069 
1070 	for (i = 0; i < numNodes; ++i)
1071 	{
1072 		if (glnodes < 3)
1073 		{
1074 			SWORD x, y, dx, dy;
1075 
1076 			data >> x >> y >> dx >> dy;
1077 			nodes[i].x = x << FRACBITS;
1078 			nodes[i].y = y << FRACBITS;
1079 			nodes[i].dx = dx << FRACBITS;
1080 			nodes[i].dy = dy << FRACBITS;
1081 		}
1082 		else
1083 		{
1084 			data >> nodes[i].x >> nodes[i].y >> nodes[i].dx >> nodes[i].dy;
1085 		}
1086 		for (int j = 0; j < 2; ++j)
1087 		{
1088 			for (int k = 0; k < 4; ++k)
1089 			{
1090 				SWORD coord;
1091 				data >> coord;
1092 				nodes[i].bbox[j][k] = coord << FRACBITS;
1093 			}
1094 		}
1095 		for (int m = 0; m < 2; ++m)
1096 		{
1097 			DWORD child;
1098 			data >> child;
1099 			if (child & 0x80000000)
1100 			{
1101 				nodes[i].children[m] = (BYTE *)&subsectors[child & 0x7FFFFFFF] + 1;
1102 			}
1103 			else
1104 			{
1105 				nodes[i].children[m] = &nodes[child];
1106 			}
1107 		}
1108 	}
1109 }
1110 
1111 
P_LoadZNodes(FileReader & dalump,DWORD id)1112 void P_LoadZNodes (FileReader &dalump, DWORD id)
1113 {
1114 	int type;
1115 	bool compressed;
1116 
1117 	switch (id)
1118 	{
1119 	case MAKE_ID('Z','N','O','D'):
1120 		type = 0;
1121 		compressed = true;
1122 		break;
1123 
1124 	case MAKE_ID('Z','G','L','N'):
1125 		type = 1;
1126 		compressed = true;
1127 		break;
1128 
1129 	case MAKE_ID('Z','G','L','2'):
1130 		type = 2;
1131 		compressed = true;
1132 		break;
1133 
1134 	case MAKE_ID('Z','G','L','3'):
1135 		type = 3;
1136 		compressed = true;
1137 		break;
1138 
1139 	case MAKE_ID('X','N','O','D'):
1140 		type = 0;
1141 		compressed = false;
1142 		break;
1143 
1144 	case MAKE_ID('X','G','L','N'):
1145 		type = 1;
1146 		compressed = false;
1147 		break;
1148 
1149 	case MAKE_ID('X','G','L','2'):
1150 		type = 2;
1151 		compressed = false;
1152 		break;
1153 
1154 	case MAKE_ID('X','G','L','3'):
1155 		type = 3;
1156 		compressed = false;
1157 		break;
1158 
1159 	default:
1160 		return;
1161 	}
1162 
1163 	if (compressed)
1164 	{
1165 		FileReaderZ data (dalump);
1166 		LoadZNodes(data, type);
1167 	}
1168 	else
1169 	{
1170 		LoadZNodes(dalump, type);
1171 	}
1172 }
1173 
1174 
1175 
1176 //===========================================================================
1177 //
1178 // P_CheckV4Nodes
1179 // http://www.sbsoftware.com/files/DeePBSPV4specs.txt
1180 //
1181 //===========================================================================
1182 
P_CheckV4Nodes(MapData * map)1183 static bool P_CheckV4Nodes(MapData *map)
1184 {
1185 	char header[8];
1186 
1187 	map->Read(ML_NODES, header, 8);
1188 	return !memcmp(header, "xNd4\0\0\0\0", 8);
1189 }
1190 
1191 
1192 //===========================================================================
1193 //
1194 // P_LoadSegs
1195 //
1196 // killough 5/3/98: reformatted, cleaned up
1197 //
1198 //===========================================================================
1199 
1200 struct badseg
1201 {
badsegbadseg1202 	badseg(int t, int s, int d) : badtype(t), badsegnum(s), baddata(d) {}
1203 	int badtype;
1204 	int badsegnum;
1205 	int baddata;
1206 };
1207 
1208 template<class segtype>
P_LoadSegs(MapData * map)1209 void P_LoadSegs (MapData * map)
1210 {
1211 	int  i;
1212 	BYTE *data;
1213 	BYTE *vertchanged = new BYTE[numvertexes];	// phares 10/4/98
1214 	DWORD segangle;
1215 	line_t* line;		// phares 10/4/98
1216 	int ptp_angle;		// phares 10/4/98
1217 	int delta_angle;	// phares 10/4/98
1218 	int dis;			// phares 10/4/98
1219 	int dx,dy;			// phares 10/4/98
1220 	int vnum1,vnum2;	// phares 10/4/98
1221 	int lumplen = map->Size(ML_SEGS);
1222 
1223 	memset (vertchanged,0,numvertexes); // phares 10/4/98
1224 
1225 	numsegs = lumplen / sizeof(segtype);
1226 
1227 	if (numsegs == 0)
1228 	{
1229 		Printf ("This map has no segs.\n");
1230 		delete[] subsectors;
1231 		delete[] nodes;
1232 		delete[] vertchanged;
1233 		ForceNodeBuild = true;
1234 		return;
1235 	}
1236 
1237 	segs = new seg_t[numsegs];
1238 	memset (segs, 0, numsegs*sizeof(seg_t));
1239 
1240 	data = new BYTE[lumplen];
1241 	map->Read(ML_SEGS, data);
1242 
1243 	for (i = 0; i < numsubsectors; ++i)
1244 	{
1245 		subsectors[i].firstline = &segs[(size_t)subsectors[i].firstline];
1246 	}
1247 
1248 	// phares: 10/4/98: Vertchanged is an array that represents the vertices.
1249 	// Mark those used by linedefs. A marked vertex is one that is not a
1250 	// candidate for movement further down.
1251 
1252 	line = lines;
1253 	for (i = 0; i < numlines ; i++, line++)
1254 	{
1255 		vertchanged[line->v1 - vertexes] = vertchanged[line->v2 - vertexes] = 1;
1256 	}
1257 
1258 	try
1259 	{
1260 		for (i = 0; i < numsegs; i++)
1261 		{
1262 			seg_t *li = segs + i;
1263 			segtype *ml = ((segtype *) data) + i;
1264 
1265 			int side, linedef;
1266 			line_t *ldef;
1267 
1268 			vnum1 = ml->V1();
1269 			vnum2 = ml->V2();
1270 
1271 			if (vnum1 >= numvertexes || vnum2 >= numvertexes)
1272 			{
1273 				throw badseg(0, i, MAX(vnum1, vnum2));
1274 			}
1275 
1276 			li->v1 = &vertexes[vnum1];
1277 			li->v2 = &vertexes[vnum2];
1278 
1279 			segangle = (WORD)LittleShort(ml->angle);
1280 
1281 			// phares 10/4/98: In the case of a lineseg that was created by splitting
1282 			// another line, it appears that the line angle is inherited from the
1283 			// father line. Due to roundoff, the new vertex may have been placed 'off
1284 			// the line'. When you get close to such a line, and it is very short,
1285 			// it's possible that the roundoff error causes 'firelines', the thin
1286 			// lines that can draw from screen top to screen bottom occasionally. This
1287 			// is due to all the angle calculations that are done based on the line
1288 			// angle, the angles from the viewer to the vertices, and the viewer's
1289 			// angle in the world. In the case of firelines, the rounded-off position
1290 			// of one of the vertices determines one of these angles, and introduces
1291 			// an error in the scaling factor for mapping textures and determining
1292 			// where on the screen the ceiling and floor spans should be shown. For a
1293 			// fireline, the engine thinks the ceiling bottom and floor top are at the
1294 			// midpoint of the screen. So you get ceilings drawn all the way down to the
1295 			// screen midpoint, and floors drawn all the way up. Thus 'firelines'. The
1296 			// name comes from the original sighting, which involved a fire texture.
1297 			//
1298 			// To correct this, reset the vertex that was added so that it sits ON the
1299 			// split line.
1300 			//
1301 			// To know which of the two vertices was added, its number is greater than
1302 			// that of the last of the author-created vertices. If both vertices of the
1303 			// line were added by splitting, pick the higher-numbered one. Once you've
1304 			// changed a vertex, don't change it again if it shows up in another seg.
1305 			//
1306 			// To determine if there's an error in the first place, find the
1307 			// angle of the line between the two seg vertices. If it's one degree or more
1308 			// off, then move one vertex. This may seem insignificant, but one degree
1309 			// errors _can_ cause firelines.
1310 
1311 			ptp_angle = R_PointToAngle2 (li->v1->x, li->v1->y, li->v2->x, li->v2->y);
1312 			dis = 0;
1313 			delta_angle = (absangle(ptp_angle-(segangle<<16))>>ANGLETOFINESHIFT)*360/FINEANGLES;
1314 
1315 			if (delta_angle != 0)
1316 			{
1317 				segangle >>= (ANGLETOFINESHIFT-16);
1318 				dx = (li->v1->x - li->v2->x)>>FRACBITS;
1319 				dy = (li->v1->y - li->v2->y)>>FRACBITS;
1320 				dis = ((int) sqrt((double)(dx*dx + dy*dy)))<<FRACBITS;
1321 				dx = finecosine[segangle];
1322 				dy = finesine[segangle];
1323 				if ((vnum2 > vnum1) && (vertchanged[vnum2] == 0))
1324 				{
1325 					li->v2->x = li->v1->x + FixedMul(dis,dx);
1326 					li->v2->y = li->v1->y + FixedMul(dis,dy);
1327 					vertchanged[vnum2] = 1; // this was changed
1328 				}
1329 				else if (vertchanged[vnum1] == 0)
1330 				{
1331 					li->v1->x = li->v2->x - FixedMul(dis,dx);
1332 					li->v1->y = li->v2->y - FixedMul(dis,dy);
1333 					vertchanged[vnum1] = 1; // this was changed
1334 				}
1335 			}
1336 
1337 			linedef = LittleShort(ml->linedef);
1338 			if ((unsigned)linedef >= (unsigned)numlines)
1339 			{
1340 				throw badseg(1, i, linedef);
1341 			}
1342 			ldef = &lines[linedef];
1343 			li->linedef = ldef;
1344 			side = LittleShort(ml->side);
1345 			if ((unsigned)(ldef->sidedef[side] - sides) >= (unsigned)numsides)
1346 			{
1347 				throw badseg(2, i, int(ldef->sidedef[side] - sides));
1348 			}
1349 			li->sidedef = ldef->sidedef[side];
1350 			li->frontsector = ldef->sidedef[side]->sector;
1351 
1352 			// killough 5/3/98: ignore 2s flag if second sidedef missing:
1353 			if (ldef->flags & ML_TWOSIDED && ldef->sidedef[side^1] != NULL)
1354 			{
1355 				li->backsector = ldef->sidedef[side^1]->sector;
1356 			}
1357 			else
1358 			{
1359 				li->backsector = 0;
1360 				ldef->flags &= ~ML_TWOSIDED;
1361 			}
1362 		}
1363 	}
1364 	catch (const badseg &bad) // the preferred way is to catch by (const) reference.
1365 	{
1366 		switch (bad.badtype)
1367 		{
1368 		case 0:
1369 			Printf ("Seg %d references a nonexistant vertex %d (max %d).\n", bad.badsegnum, bad.baddata, numvertexes);
1370 			break;
1371 
1372 		case 1:
1373 			Printf ("Seg %d references a nonexistant linedef %d (max %d).\n", bad.badsegnum, bad.baddata, numlines);
1374 			break;
1375 
1376 		case 2:
1377 			Printf ("The linedef for seg %d references a nonexistant sidedef %d (max %d).\n", bad.badsegnum, bad.baddata, numsides);
1378 			break;
1379 		}
1380 		Printf ("The BSP will be rebuilt.\n");
1381 		delete[] segs;
1382 		delete[] subsectors;
1383 		delete[] nodes;
1384 		ForceNodeBuild = true;
1385 	}
1386 
1387 	delete[] vertchanged; // phares 10/4/98
1388 	delete[] data;
1389 }
1390 
1391 
1392 //===========================================================================
1393 //
1394 // P_LoadSubsectors
1395 //
1396 //===========================================================================
1397 
1398 template<class subsectortype, class segtype>
P_LoadSubsectors(MapData * map)1399 void P_LoadSubsectors (MapData * map)
1400 {
1401 	int i;
1402 	DWORD maxseg = map->Size(ML_SEGS) / sizeof(segtype);
1403 
1404 	numsubsectors = map->Size(ML_SSECTORS) / sizeof(subsectortype);
1405 
1406 	if (numsubsectors == 0 || maxseg == 0 )
1407 	{
1408 		Printf ("This map has an incomplete BSP tree.\n");
1409 		delete[] nodes;
1410 		ForceNodeBuild = true;
1411 		return;
1412 	}
1413 
1414 	subsectors = new subsector_t[numsubsectors];
1415 	map->Seek(ML_SSECTORS);
1416 
1417 	memset (subsectors, 0, numsubsectors*sizeof(subsector_t));
1418 
1419 	for (i = 0; i < numsubsectors; i++)
1420 	{
1421 		subsectortype subd;
1422 
1423 		(*map->file) >> subd.numsegs >> subd.firstseg;
1424 
1425 		if (subd.numsegs == 0)
1426 		{
1427 			Printf ("Subsector %i is empty.\n", i);
1428 			delete[] subsectors;
1429 			delete[] nodes;
1430 			ForceNodeBuild = true;
1431 			return;
1432 		}
1433 
1434 		subsectors[i].numlines = subd.numsegs;
1435 		subsectors[i].firstline = (seg_t *)(size_t)subd.firstseg;
1436 
1437 		if ((size_t)subsectors[i].firstline >= maxseg)
1438 		{
1439 			Printf ("Subsector %d contains invalid segs %u-%u\n"
1440 				"The BSP will be rebuilt.\n", i, (unsigned)((size_t)subsectors[i].firstline),
1441 				(unsigned)((size_t)subsectors[i].firstline) + subsectors[i].numlines - 1);
1442 			ForceNodeBuild = true;
1443 			delete[] nodes;
1444 			delete[] subsectors;
1445 			break;
1446 		}
1447 		else if ((size_t)subsectors[i].firstline + subsectors[i].numlines > maxseg)
1448 		{
1449 			Printf ("Subsector %d contains invalid segs %u-%u\n"
1450 				"The BSP will be rebuilt.\n", i, maxseg,
1451 				(unsigned)((size_t)subsectors[i].firstline) + subsectors[i].numlines - 1);
1452 			ForceNodeBuild = true;
1453 			delete[] nodes;
1454 			delete[] subsectors;
1455 			break;
1456 		}
1457 	}
1458 }
1459 
1460 
1461 //===========================================================================
1462 //
1463 // P_LoadSectors
1464 //
1465 //===========================================================================
1466 
P_LoadSectors(MapData * map,FMissingTextureTracker & missingtex)1467 void P_LoadSectors (MapData *map, FMissingTextureTracker &missingtex)
1468 {
1469 	int 				i;
1470 	char				*msp;
1471 	mapsector_t			*ms;
1472 	sector_t*			ss;
1473 	int					defSeqType;
1474 	FDynamicColormap	*fogMap, *normMap;
1475 	int					lumplen = map->Size(ML_SECTORS);
1476 
1477 	numsectors = lumplen / sizeof(mapsector_t);
1478 	sectors = new sector_t[numsectors];
1479 	memset (sectors, 0, numsectors*sizeof(sector_t));
1480 
1481 	if (level.flags & LEVEL_SNDSEQTOTALCTRL)
1482 		defSeqType = 0;
1483 	else
1484 		defSeqType = -1;
1485 
1486 	fogMap = normMap = NULL;
1487 
1488 	msp = new char[lumplen];
1489 	map->Read(ML_SECTORS, msp);
1490 	ms = (mapsector_t*)msp;
1491 	ss = sectors;
1492 
1493 	// Extended properties
1494 	sectors[0].e = new extsector_t[numsectors];
1495 
1496 	for (i = 0; i < numsectors; i++, ss++, ms++)
1497 	{
1498 		ss->e = &sectors[0].e[i];
1499 		if (!map->HasBehavior) ss->Flags |= SECF_FLOORDROP;
1500 		ss->SetPlaneTexZ(sector_t::floor, LittleShort(ms->floorheight)<<FRACBITS);
1501 		ss->floorplane.d = -ss->GetPlaneTexZ(sector_t::floor);
1502 		ss->floorplane.c = FRACUNIT;
1503 		ss->floorplane.ic = FRACUNIT;
1504 		ss->SetPlaneTexZ(sector_t::ceiling, LittleShort(ms->ceilingheight)<<FRACBITS);
1505 		ss->ceilingplane.d = ss->GetPlaneTexZ(sector_t::ceiling);
1506 		ss->ceilingplane.c = -FRACUNIT;
1507 		ss->ceilingplane.ic = -FRACUNIT;
1508 		SetTexture(ss, i, sector_t::floor, ms->floorpic, missingtex, true);
1509 		SetTexture(ss, i, sector_t::ceiling, ms->ceilingpic, missingtex, true);
1510 		ss->lightlevel = LittleShort(ms->lightlevel);
1511 		if (map->HasBehavior)
1512 			ss->special = LittleShort(ms->special);
1513 		else	// [RH] Translate to new sector special
1514 			ss->special = P_TranslateSectorSpecial (LittleShort(ms->special));
1515 		tagManager.AddSectorTag(i, LittleShort(ms->tag));
1516 		ss->thinglist = NULL;
1517 		ss->touching_thinglist = NULL;		// phares 3/14/98
1518 		ss->seqType = defSeqType;
1519 		ss->SeqName = NAME_None;
1520 		ss->nextsec = -1;	//jff 2/26/98 add fields to support locking out
1521 		ss->prevsec = -1;	// stair retriggering until build completes
1522 
1523 		ss->SetAlpha(sector_t::floor, FRACUNIT);
1524 		ss->SetAlpha(sector_t::ceiling, FRACUNIT);
1525 		ss->SetXScale(sector_t::floor, FRACUNIT);	// [RH] floor and ceiling scaling
1526 		ss->SetYScale(sector_t::floor, FRACUNIT);
1527 		ss->SetXScale(sector_t::ceiling, FRACUNIT);
1528 		ss->SetYScale(sector_t::ceiling, FRACUNIT);
1529 
1530 		ss->heightsec = NULL;	// sector used to get floor and ceiling height
1531 		// killough 3/7/98: end changes
1532 
1533 		ss->gravity = 1.f;	// [RH] Default sector gravity of 1.0
1534 		ss->ZoneNumber = 0xFFFF;
1535 		ss->terrainnum[sector_t::ceiling] = ss->terrainnum[sector_t::floor] = -1;
1536 
1537 		// [RH] Sectors default to white light with the default fade.
1538 		//		If they are outside (have a sky ceiling), they use the outside fog.
1539 		if (level.outsidefog != 0xff000000 && (ss->GetTexture(sector_t::ceiling) == skyflatnum || (ss->special&0xff) == Sector_Outside))
1540 		{
1541 			if (fogMap == NULL)
1542 				fogMap = GetSpecialLights (PalEntry (255,255,255), level.outsidefog, 0);
1543 			ss->ColorMap = fogMap;
1544 		}
1545 		else
1546 		{
1547 			if (normMap == NULL)
1548 				normMap = GetSpecialLights (PalEntry (255,255,255), level.fadeto, NormalLight.Desaturate);
1549 			ss->ColorMap = normMap;
1550 		}
1551 
1552 		// killough 8/28/98: initialize all sectors to normal friction
1553 		ss->friction = ORIG_FRICTION;
1554 		ss->movefactor = ORIG_FRICTION_FACTOR;
1555 		ss->sectornum = i;
1556 	}
1557 	delete[] msp;
1558 }
1559 
1560 
1561 //===========================================================================
1562 //
1563 // P_LoadNodes
1564 //
1565 //===========================================================================
1566 
1567 template<class nodetype, class subsectortype>
P_LoadNodes(MapData * map)1568 void P_LoadNodes (MapData * map)
1569 {
1570 	FMemLump	data;
1571 	int 		i;
1572 	int 		j;
1573 	int 		k;
1574 	char		*mnp;
1575 	nodetype	*mn;
1576 	node_t* 	no;
1577 	WORD*		used;
1578 	int			lumplen = map->Size(ML_NODES);
1579 	int			maxss = map->Size(ML_SSECTORS) / sizeof(subsectortype);
1580 
1581 	numnodes = (lumplen - nodetype::NF_LUMPOFFSET) / sizeof(nodetype);
1582 
1583 	if ((numnodes == 0 && maxss != 1) || maxss == 0)
1584 	{
1585 		ForceNodeBuild = true;
1586 		return;
1587 	}
1588 
1589 	nodes = new node_t[numnodes];
1590 	used = (WORD *)alloca (sizeof(WORD)*numnodes);
1591 	memset (used, 0, sizeof(WORD)*numnodes);
1592 
1593 	mnp = new char[lumplen];
1594 	mn = (nodetype*)(mnp + nodetype::NF_LUMPOFFSET);
1595 	map->Read(ML_NODES, mnp);
1596 	no = nodes;
1597 
1598 	for (i = 0; i < numnodes; i++, no++, mn++)
1599 	{
1600 		no->x = LittleShort(mn->x)<<FRACBITS;
1601 		no->y = LittleShort(mn->y)<<FRACBITS;
1602 		no->dx = LittleShort(mn->dx)<<FRACBITS;
1603 		no->dy = LittleShort(mn->dy)<<FRACBITS;
1604 		for (j = 0; j < 2; j++)
1605 		{
1606 			int child = mn->Child(j);
1607 			if (child & nodetype::NF_SUBSECTOR)
1608 			{
1609 				child &= ~nodetype::NF_SUBSECTOR;
1610 				if (child >= maxss)
1611 				{
1612 					Printf ("BSP node %d references invalid subsector %d.\n"
1613 						"The BSP will be rebuilt.\n", i, child);
1614 					ForceNodeBuild = true;
1615 					delete[] nodes;
1616 					delete[] mnp;
1617 					return;
1618 				}
1619 				no->children[j] = (BYTE *)&subsectors[child] + 1;
1620 			}
1621 			else if (child >= numnodes)
1622 			{
1623 				Printf ("BSP node %d references invalid node %td.\n"
1624 					"The BSP will be rebuilt.\n", i, (node_t *)no->children[j] - nodes);
1625 				ForceNodeBuild = true;
1626 				delete[] nodes;
1627 				delete[] mnp;
1628 				return;
1629 			}
1630 			else if (used[child])
1631 			{
1632 				Printf ("BSP node %d references node %d,\n"
1633 					"which is already used by node %d.\n"
1634 					"The BSP will be rebuilt.\n", i, child, used[child]-1);
1635 				ForceNodeBuild = true;
1636 				delete[] nodes;
1637 				delete[] mnp;
1638 				return;
1639 			}
1640 			else
1641 			{
1642 				no->children[j] = &nodes[child];
1643 				used[child] = j + 1;
1644 			}
1645 			for (k = 0; k < 4; k++)
1646 			{
1647 				no->bbox[j][k] = LittleShort(mn->bbox[j][k])<<FRACBITS;
1648 			}
1649 		}
1650 	}
1651 	delete[] mnp;
1652 }
1653 
1654 //===========================================================================
1655 //
1656 // SpawnMapThing
1657 //
1658 //===========================================================================
1659 CVAR(Bool, dumpspawnedthings, false, 0)
1660 
SpawnMapThing(int index,FMapThing * mt,int position)1661 AActor *SpawnMapThing(int index, FMapThing *mt, int position)
1662 {
1663 	AActor *spawned = P_SpawnMapThing(mt, position);
1664 	if (dumpspawnedthings)
1665 	{
1666 		Printf("%5d: (%5d, %5d, %5d), doomednum = %5d, flags = %04x, type = %s\n",
1667 			index, mt->x>>FRACBITS, mt->y>>FRACBITS, mt->z>>FRACBITS, mt->EdNum, mt->flags,
1668 			spawned? spawned->GetClass()->TypeName.GetChars() : "(none)");
1669 	}
1670 	T_AddSpawnedThing(spawned);
1671 	return spawned;
1672 }
1673 
1674 //===========================================================================
1675 //
1676 // SetMapThingUserData
1677 //
1678 //===========================================================================
1679 
SetMapThingUserData(AActor * actor,unsigned udi)1680 static void SetMapThingUserData(AActor *actor, unsigned udi)
1681 {
1682 	if (actor == NULL)
1683 	{
1684 		return;
1685 	}
1686 	while (MapThingsUserData[udi].Property != NAME_None)
1687 	{
1688 		FName varname = MapThingsUserData[udi].Property;
1689 		int value = MapThingsUserData[udi].Value;
1690 		PSymbol *sym = actor->GetClass()->Symbols.FindSymbol(varname, true);
1691 		PSymbolVariable *var;
1692 
1693 		udi++;
1694 
1695 		if (sym == NULL || sym->SymbolType != SYM_Variable ||
1696 			!(var = static_cast<PSymbolVariable *>(sym))->bUserVar ||
1697 			var->ValueType.Type != VAL_Int)
1698 		{
1699 			DPrintf("%s is not a user variable in class %s\n", varname.GetChars(),
1700 				actor->GetClass()->TypeName.GetChars());
1701 		}
1702 		else
1703 		{ // Set the value of the specified user variable.
1704 			*(int *)(reinterpret_cast<BYTE *>(actor) + var->offset) = value;
1705 		}
1706 	}
1707 }
1708 
1709 //===========================================================================
1710 //
1711 // P_LoadThings
1712 //
1713 //===========================================================================
1714 
MakeSkill(int flags)1715 WORD MakeSkill(int flags)
1716 {
1717 	WORD res = 0;
1718 	if (flags & 1) res |= 1+2;
1719 	if (flags & 2) res |= 4;
1720 	if (flags & 4) res |= 8+16;
1721 	return res;
1722 }
1723 
P_LoadThings(MapData * map)1724 void P_LoadThings (MapData * map)
1725 {
1726 	int	lumplen = map->Size(ML_THINGS);
1727 	int numthings = lumplen / sizeof(mapthing_t);
1728 
1729 	char *mtp;
1730 	mapthing_t *mt;
1731 
1732 	mtp = new char[lumplen];
1733 	map->Read(ML_THINGS, mtp);
1734 	mt = (mapthing_t*)mtp;
1735 
1736 	MapThingsConverted.Resize(numthings);
1737 	FMapThing *mti = &MapThingsConverted[0];
1738 
1739 	// [RH] ZDoom now uses Hexen-style maps as its native format.
1740 	//		Since this is the only place where Doom-style Things are ever
1741 	//		referenced, we translate them into a Hexen-style thing.
1742 	for (int i=0 ; i < numthings; i++, mt++)
1743 	{
1744 		// [RH] At this point, monsters unique to Doom II were weeded out
1745 		//		if the IWAD wasn't for Doom II. P_SpawnMapThing() can now
1746 		//		handle these and more cases better, so we just pass it
1747 		//		everything and let it decide what to do with them.
1748 
1749 		// [RH] Need to translate the spawn flags to Hexen format.
1750 		short flags = LittleShort(mt->options);
1751 
1752 		memset (&mti[i], 0, sizeof(mti[i]));
1753 
1754 		mti[i].gravity = FRACUNIT;
1755 		mti[i].Conversation = 0;
1756 		mti[i].SkillFilter = MakeSkill(flags);
1757 		mti[i].ClassFilter = 0xffff;	// Doom map format doesn't have class flags so spawn for all player classes
1758 		mti[i].RenderStyle = STYLE_Count;
1759 		mti[i].alpha = -1;
1760 		mti[i].health = 1;
1761 		mti[i].FloatbobPhase = -1;
1762 		flags &= ~MTF_SKILLMASK;
1763 		mti[i].flags = (short)((flags & 0xf) | 0x7e0);
1764 		if (gameinfo.gametype == GAME_Strife)
1765 		{
1766 			mti[i].flags &= ~MTF_AMBUSH;
1767 			if (flags & STF_SHADOW)			mti[i].flags |= MTF_SHADOW;
1768 			if (flags & STF_ALTSHADOW)		mti[i].flags |= MTF_ALTSHADOW;
1769 			if (flags & STF_STANDSTILL)		mti[i].flags |= MTF_STANDSTILL;
1770 			if (flags & STF_AMBUSH)			mti[i].flags |= MTF_AMBUSH;
1771 			if (flags & STF_FRIENDLY)		mti[i].flags |= MTF_FRIENDLY;
1772 		}
1773 		else
1774 		{
1775 			if (flags & BTF_BADEDITORCHECK)
1776 			{
1777 				flags &= 0x1F;
1778 			}
1779 			if (flags & BTF_NOTDEATHMATCH)	mti[i].flags &= ~MTF_DEATHMATCH;
1780 			if (flags & BTF_NOTCOOPERATIVE)	mti[i].flags &= ~MTF_COOPERATIVE;
1781 			if (flags & BTF_FRIENDLY)		mti[i].flags |= MTF_FRIENDLY;
1782 		}
1783 		if (flags & BTF_NOTSINGLE)			mti[i].flags &= ~MTF_SINGLE;
1784 
1785 		mti[i].x = LittleShort(mt->x) << FRACBITS;
1786 		mti[i].y = LittleShort(mt->y) << FRACBITS;
1787 		mti[i].angle = LittleShort(mt->angle);
1788 		mti[i].EdNum = LittleShort(mt->type);
1789 		mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
1790 	}
1791 	delete [] mtp;
1792 }
1793 
1794 //===========================================================================
1795 //
1796 // [RH]
1797 // P_LoadThings2
1798 //
1799 // Same as P_LoadThings() except it assumes Things are
1800 // saved Hexen-style. Position also controls which single-
1801 // player start spots are spawned by filtering out those
1802 // whose first parameter don't match position.
1803 //
1804 //===========================================================================
1805 
P_LoadThings2(MapData * map)1806 void P_LoadThings2 (MapData * map)
1807 {
1808 	int	lumplen = map->Size(ML_THINGS);
1809 	int numthings = lumplen / sizeof(mapthinghexen_t);
1810 
1811 	char *mtp;
1812 
1813 	MapThingsConverted.Resize(numthings);
1814 	FMapThing *mti = &MapThingsConverted[0];
1815 
1816 	mtp = new char[lumplen];
1817 	map->Read(ML_THINGS, mtp);
1818 	mapthinghexen_t *mth = (mapthinghexen_t*)mtp;
1819 
1820 	for(int i = 0; i< numthings; i++)
1821 	{
1822 		memset (&mti[i], 0, sizeof(mti[i]));
1823 
1824 		mti[i].thingid = LittleShort(mth[i].thingid);
1825 		mti[i].x = LittleShort(mth[i].x)<<FRACBITS;
1826 		mti[i].y = LittleShort(mth[i].y)<<FRACBITS;
1827 		mti[i].z = LittleShort(mth[i].z)<<FRACBITS;
1828 		mti[i].angle = LittleShort(mth[i].angle);
1829 		mti[i].EdNum = LittleShort(mth[i].type);
1830 		mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
1831 		mti[i].flags = LittleShort(mth[i].flags);
1832 		mti[i].special = mth[i].special;
1833 		for(int j=0;j<5;j++) mti[i].args[j] = mth[i].args[j];
1834 		mti[i].SkillFilter = MakeSkill(mti[i].flags);
1835 		mti[i].ClassFilter = (mti[i].flags & MTF_CLASS_MASK) >> MTF_CLASS_SHIFT;
1836 		mti[i].flags &= ~(MTF_SKILLMASK|MTF_CLASS_MASK);
1837 		if (level.flags2 & LEVEL2_HEXENHACK)
1838 		{
1839 			mti[i].flags &= 0x7ff;	// mask out Strife flags if playing an original Hexen map.
1840 		}
1841 
1842 		mti[i].gravity = FRACUNIT;
1843 		mti[i].RenderStyle = STYLE_Count;
1844 		mti[i].alpha = -1;
1845 		mti[i].health = 1;
1846 		mti[i].FloatbobPhase = -1;
1847 	}
1848 	delete[] mtp;
1849 }
1850 
1851 
P_SpawnThings(int position)1852 void P_SpawnThings (int position)
1853 {
1854 	int numthings = MapThingsConverted.Size();
1855 
1856 	for (int i=0; i < numthings; i++)
1857 	{
1858 		AActor *actor = SpawnMapThing (i, &MapThingsConverted[i], position);
1859 		unsigned *udi = MapThingsUserDataIndex.CheckKey((unsigned)i);
1860 		if (udi != NULL)
1861 		{
1862 			SetMapThingUserData(actor, *udi);
1863 		}
1864 	}
1865 	for(int i=0; i<MAXPLAYERS; i++)
1866 	{
1867 		if (playeringame[i] && players[i].mo != NULL)
1868 			P_PlayerStartStomp(players[i].mo);
1869 	}
1870 }
1871 
1872 
1873 //===========================================================================
1874 //
1875 // P_LoadLineDefs
1876 //
1877 // killough 4/4/98: split into two functions, to allow sidedef overloading
1878 //
1879 // [RH] Actually split into four functions to allow for Hexen and Doom
1880 //		linedefs.
1881 //
1882 //===========================================================================
1883 
P_AdjustLine(line_t * ld)1884 void P_AdjustLine (line_t *ld)
1885 {
1886 	vertex_t *v1, *v2;
1887 
1888 	v1 = ld->v1;
1889 	v2 = ld->v2;
1890 
1891 	ld->dx = v2->x - v1->x;
1892 	ld->dy = v2->y - v1->y;
1893 
1894 	if (v1->x < v2->x)
1895 	{
1896 		ld->bbox[BOXLEFT] = v1->x;
1897 		ld->bbox[BOXRIGHT] = v2->x;
1898 	}
1899 	else
1900 	{
1901 		ld->bbox[BOXLEFT] = v2->x;
1902 		ld->bbox[BOXRIGHT] = v1->x;
1903 	}
1904 
1905 	if (v1->y < v2->y)
1906 	{
1907 		ld->bbox[BOXBOTTOM] = v1->y;
1908 		ld->bbox[BOXTOP] = v2->y;
1909 	}
1910 	else
1911 	{
1912 		ld->bbox[BOXBOTTOM] = v2->y;
1913 		ld->bbox[BOXTOP] = v1->y;
1914 	}
1915 }
1916 
P_SetLineID(int i,line_t * ld)1917 void P_SetLineID (int i, line_t *ld)
1918 {
1919 	// [RH] Set line id (as appropriate) here
1920 	// for Doom format maps this must be done in P_TranslateLineDef because
1921 	// the tag doesn't always go into the first arg.
1922 	if (level.maptype == MAPTYPE_HEXEN)
1923 	{
1924 		int setid = -1;
1925 		switch (ld->special)
1926 		{
1927 		case Line_SetIdentification:
1928 			if (!(level.flags2 & LEVEL2_HEXENHACK))
1929 			{
1930 				setid = ld->args[0] + 256 * ld->args[4];
1931 				ld->flags |= ld->args[1]<<16;
1932 			}
1933 			else
1934 			{
1935 				setid = ld->args[0];
1936 			}
1937 			ld->special = 0;
1938 			break;
1939 
1940 		case TranslucentLine:
1941 			setid = ld->args[0];
1942 			ld->flags |= ld->args[3]<<16;
1943 			break;
1944 
1945 		case Teleport_Line:
1946 		case Scroll_Texture_Model:
1947 			setid = ld->args[0];
1948 			break;
1949 
1950 		case Polyobj_StartLine:
1951 			setid = ld->args[3];
1952 			break;
1953 
1954 		case Polyobj_ExplicitLine:
1955 			setid = ld->args[4];
1956 			break;
1957 
1958 		case Plane_Align:
1959 			setid = ld->args[2];
1960 			break;
1961 
1962 		case Static_Init:
1963 			if (ld->args[1] == Init_SectorLink) setid = ld->args[0];
1964 			break;
1965 		}
1966 		if (setid != -1)
1967 		{
1968 			tagManager.AddLineID(i, setid);
1969 		}
1970 	}
1971 }
1972 
P_SaveLineSpecial(line_t * ld)1973 void P_SaveLineSpecial (line_t *ld)
1974 {
1975 	if (ld->sidedef[0] == NULL)
1976 		return;
1977 
1978 	DWORD sidenum = DWORD(ld->sidedef[0]-sides);
1979 	// killough 4/4/98: support special sidedef interpretation below
1980 	// [RH] Save Static_Init only if it's interested in the textures
1981 	if	(ld->special != Static_Init || ld->args[1] == Init_Color)
1982 	{
1983 		sidetemp[sidenum].a.special = ld->special;
1984 		sidetemp[sidenum].a.tag = ld->args[0];
1985 	}
1986 	else
1987 	{
1988 		sidetemp[sidenum].a.special = 0;
1989 	}
1990 }
1991 
P_FinishLoadingLineDef(line_t * ld,int alpha)1992 void P_FinishLoadingLineDef(line_t *ld, int alpha)
1993 {
1994 	bool additive = false;
1995 
1996 	ld->frontsector = ld->sidedef[0] != NULL ? ld->sidedef[0]->sector : NULL;
1997 	ld->backsector  = ld->sidedef[1] != NULL ? ld->sidedef[1]->sector : NULL;
1998 	double dx = FIXED2DBL(ld->v2->x - ld->v1->x);
1999 	double dy = FIXED2DBL(ld->v2->y - ld->v1->y);
2000 	int linenum = int(ld-lines);
2001 
2002 	if (ld->frontsector == NULL)
2003 	{
2004 		Printf ("Line %d has no front sector\n", linemap[linenum]);
2005 	}
2006 
2007 	// [RH] Set some new sidedef properties
2008 	int len = (int)(sqrt (dx*dx + dy*dy) + 0.5f);
2009 
2010 	if (ld->sidedef[0] != NULL)
2011 	{
2012 		ld->sidedef[0]->linedef = ld;
2013 		ld->sidedef[0]->TexelLength = len;
2014 
2015 	}
2016 	if (ld->sidedef[1] != NULL)
2017 	{
2018 		ld->sidedef[1]->linedef = ld;
2019 		ld->sidedef[1]->TexelLength = len;
2020 	}
2021 
2022 	switch (ld->special)
2023 	{						// killough 4/11/98: handle special types
2024 		int j;
2025 
2026 	case TranslucentLine:			// killough 4/11/98: translucent 2s textures
2027 		// [RH] Second arg controls how opaque it is.
2028 		if (alpha == SHRT_MIN)
2029 		{
2030 			alpha = ld->args[1];
2031 			additive = !!ld->args[2];
2032 		}
2033 		else if (alpha < 0)
2034 		{
2035 			alpha = -alpha;
2036 			additive = true;
2037 		}
2038 
2039 		alpha = Scale(alpha, FRACUNIT, 255);
2040 		if (!ld->args[0])
2041 		{
2042 			ld->Alpha = alpha;
2043 			if (additive)
2044 			{
2045 				ld->flags |= ML_ADDTRANS;
2046 			}
2047 		}
2048 		else
2049 		{
2050 			for (j = 0; j < numlines; j++)
2051 			{
2052 				if (tagManager.LineHasID(j, ld->args[0]))
2053 				{
2054 					lines[j].Alpha = alpha;
2055 					if (additive)
2056 					{
2057 						lines[j].flags |= ML_ADDTRANS;
2058 					}
2059 				}
2060 			}
2061 		}
2062 		ld->special = 0;
2063 		break;
2064 	}
2065 }
2066 // killough 4/4/98: delay using sidedefs until they are loaded
P_FinishLoadingLineDefs()2067 void P_FinishLoadingLineDefs ()
2068 {
2069 	for (int i = 0; i < numlines; i++)
2070 	{
2071 		P_FinishLoadingLineDef(&lines[i], sidetemp[lines[i].sidedef[0]-sides].a.alpha);
2072 	}
2073 }
2074 
P_SetSideNum(side_t ** sidenum_p,WORD sidenum)2075 static void P_SetSideNum (side_t **sidenum_p, WORD sidenum)
2076 {
2077 	if (sidenum == NO_INDEX)
2078 	{
2079 		*sidenum_p = NULL;
2080 	}
2081 	else if (sidecount < numsides)
2082 	{
2083 		sidetemp[sidecount].a.map = sidenum;
2084 		*sidenum_p = &sides[sidecount++];
2085 	}
2086 	else
2087 	{
2088 		I_Error ("%d sidedefs is not enough\n", sidecount);
2089 	}
2090 }
2091 
P_LoadLineDefs(MapData * map)2092 void P_LoadLineDefs (MapData * map)
2093 {
2094 	int i, skipped;
2095 	line_t *ld;
2096 	int lumplen = map->Size(ML_LINEDEFS);
2097 	char * mldf;
2098 	maplinedef_t *mld;
2099 
2100 	numlines = lumplen / sizeof(maplinedef_t);
2101 	lines = new line_t[numlines];
2102 	linemap.Resize(numlines);
2103 	memset (lines, 0, numlines*sizeof(line_t));
2104 
2105 	mldf = new char[lumplen];
2106 	map->Read(ML_LINEDEFS, mldf);
2107 
2108 	// [RH] Count the number of sidedef references. This is the number of
2109 	// sidedefs we need. The actual number in the SIDEDEFS lump might be less.
2110 	// Lines with 0 length are also removed.
2111 
2112 	for (skipped = sidecount = i = 0; i < numlines; )
2113 	{
2114 		mld = ((maplinedef_t*)mldf) + i;
2115 		int v1 = LittleShort(mld->v1);
2116 		int v2 = LittleShort(mld->v2);
2117 
2118 		if (v1 >= numvertexes || v2 >= numvertexes)
2119 		{
2120 			delete [] mldf;
2121 			I_Error ("Line %d has invalid vertices: %d and/or %d.\nThe map only contains %d vertices.", i+skipped, v1, v2, numvertexes);
2122 		}
2123 		else if (v1 == v2 ||
2124 			(vertexes[LittleShort(mld->v1)].x == vertexes[LittleShort(mld->v2)].x &&
2125 			 vertexes[LittleShort(mld->v1)].y == vertexes[LittleShort(mld->v2)].y))
2126 		{
2127 			Printf ("Removing 0-length line %d\n", i+skipped);
2128 			memmove (mld, mld+1, sizeof(*mld)*(numlines-i-1));
2129 			ForceNodeBuild = true;
2130 			skipped++;
2131 			numlines--;
2132 		}
2133 		else
2134 		{
2135 			// patch missing first sides instead of crashing out.
2136 			// Visual glitches are better than not being able to play.
2137 			if (LittleShort(mld->sidenum[0]) == NO_INDEX)
2138 			{
2139 				Printf("Line %d has no first side.\n", i);
2140 				mld->sidenum[0] = 0;
2141 			}
2142 			sidecount++;
2143 			if (LittleShort(mld->sidenum[1]) != NO_INDEX)
2144 				sidecount++;
2145 			linemap[i] = i+skipped;
2146 			i++;
2147 		}
2148 	}
2149 
2150 	P_AllocateSideDefs (sidecount);
2151 
2152 	mld = (maplinedef_t *)mldf;
2153 	ld = lines;
2154 	for (i = 0; i < numlines; i++, mld++, ld++)
2155 	{
2156 		ld->Alpha = FRACUNIT;	// [RH] Opaque by default
2157 
2158 		// [RH] Translate old linedef special and flags to be
2159 		//		compatible with the new format.
2160 		P_TranslateLineDef (ld, mld, i);
2161 
2162 		ld->v1 = &vertexes[LittleShort(mld->v1)];
2163 		ld->v2 = &vertexes[LittleShort(mld->v2)];
2164 
2165 		P_SetSideNum (&ld->sidedef[0], LittleShort(mld->sidenum[0]));
2166 		P_SetSideNum (&ld->sidedef[1], LittleShort(mld->sidenum[1]));
2167 
2168 		P_AdjustLine (ld);
2169 		P_SaveLineSpecial (ld);
2170 		if (level.flags2 & LEVEL2_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX;
2171 		if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX;
2172 		if (level.flags2 & LEVEL2_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE;
2173 	}
2174 	delete[] mldf;
2175 }
2176 
2177 // [RH] Same as P_LoadLineDefs() except it uses Hexen-style LineDefs.
P_LoadLineDefs2(MapData * map)2178 void P_LoadLineDefs2 (MapData * map)
2179 {
2180 	int i, skipped;
2181 	line_t *ld;
2182 	int lumplen = map->Size(ML_LINEDEFS);
2183 	char * mldf;
2184 	maplinedef2_t *mld;
2185 
2186 	numlines = lumplen / sizeof(maplinedef2_t);
2187 	lines = new line_t[numlines];
2188 	linemap.Resize(numlines);
2189 	memset (lines, 0, numlines*sizeof(line_t));
2190 
2191 	mldf = new char[lumplen];
2192 	map->Read(ML_LINEDEFS, mldf);
2193 
2194 	// [RH] Remove any lines that have 0 length and count sidedefs used
2195 	for (skipped = sidecount = i = 0; i < numlines; )
2196 	{
2197 		mld = ((maplinedef2_t*)mldf) + i;
2198 
2199 		if (mld->v1 == mld->v2 ||
2200 			(vertexes[LittleShort(mld->v1)].x == vertexes[LittleShort(mld->v2)].x &&
2201 			 vertexes[LittleShort(mld->v1)].y == vertexes[LittleShort(mld->v2)].y))
2202 		{
2203 			Printf ("Removing 0-length line %d\n", i+skipped);
2204 			memmove (mld, mld+1, sizeof(*mld)*(numlines-i-1));
2205 			skipped++;
2206 			numlines--;
2207 		}
2208 		else
2209 		{
2210 			// patch missing first sides instead of crashing out.
2211 			// Visual glitches are better than not being able to play.
2212 			if (LittleShort(mld->sidenum[0]) == NO_INDEX)
2213 			{
2214 				Printf("Line %d has no first side.\n", i);
2215 				mld->sidenum[0] = 0;
2216 			}
2217 			sidecount++;
2218 			if (LittleShort(mld->sidenum[1]) != NO_INDEX)
2219 				sidecount++;
2220 			linemap[i] = i+skipped;
2221 			i++;
2222 		}
2223 	}
2224 	if (skipped > 0)
2225 	{
2226 		ForceNodeBuild = true;
2227 	}
2228 
2229 	P_AllocateSideDefs (sidecount);
2230 
2231 	mld = (maplinedef2_t *)mldf;
2232 	ld = lines;
2233 	for (i = 0; i < numlines; i++, mld++, ld++)
2234 	{
2235 		int j;
2236 
2237 		for (j = 0; j < 5; j++)
2238 			ld->args[j] = mld->args[j];
2239 
2240 		ld->flags = LittleShort(mld->flags);
2241 		ld->special = mld->special;
2242 
2243 		ld->v1 = &vertexes[LittleShort(mld->v1)];
2244 		ld->v2 = &vertexes[LittleShort(mld->v2)];
2245 		ld->Alpha = FRACUNIT;	// [RH] Opaque by default
2246 
2247 		P_SetSideNum (&ld->sidedef[0], LittleShort(mld->sidenum[0]));
2248 		P_SetSideNum (&ld->sidedef[1], LittleShort(mld->sidenum[1]));
2249 
2250 		P_AdjustLine (ld);
2251 		P_SetLineID(i, ld);
2252 		P_SaveLineSpecial (ld);
2253 		if (level.flags2 & LEVEL2_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX;
2254 		if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX;
2255 		if (level.flags2 & LEVEL2_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE;
2256 
2257 		// convert the activation type
2258 		ld->activation = 1 << GET_SPAC(ld->flags);
2259 		if (ld->activation == SPAC_AnyCross)
2260 		{ // this is really PTouch
2261 			ld->activation = SPAC_Impact | SPAC_PCross;
2262 		}
2263 		else if (ld->activation == SPAC_Impact)
2264 		{ // In non-UMDF maps, Impact implies PCross
2265 			ld->activation = SPAC_Impact | SPAC_PCross;
2266 		}
2267 		ld->flags &= ~ML_SPAC_MASK;
2268 	}
2269 	delete[] mldf;
2270 }
2271 
2272 
2273 //
2274 // P_LoadSideDefs
2275 //
2276 // killough 4/4/98: split into two functions
P_LoadSideDefs(MapData * map)2277 void P_LoadSideDefs (MapData * map)
2278 {
2279 	numsides = map->Size(ML_SIDEDEFS) / sizeof(mapsidedef_t);
2280 }
2281 
P_AllocateSideDefs(int count)2282 static void P_AllocateSideDefs (int count)
2283 {
2284 	int i;
2285 
2286 	sides = new side_t[count];
2287 	memset (sides, 0, count*sizeof(side_t));
2288 
2289 	sidetemp = new sidei_t[MAX(count,numvertexes)];
2290 	for (i = 0; i < count; i++)
2291 	{
2292 		sidetemp[i].a.special = sidetemp[i].a.tag = 0;
2293 		sidetemp[i].a.alpha = SHRT_MIN;
2294 		sidetemp[i].a.map = NO_SIDE;
2295 	}
2296 	if (count < numsides)
2297 	{
2298 		Printf ("Map has %d unused sidedefs\n", numsides - count);
2299 	}
2300 	numsides = count;
2301 	sidecount = 0;
2302 }
2303 
2304 
2305 // [RH] Group sidedefs into loops so that we can easily determine
2306 // what walls any particular wall neighbors.
2307 
P_LoopSidedefs(bool firstloop)2308 static void P_LoopSidedefs (bool firstloop)
2309 {
2310 	int i;
2311 
2312 	if (sidetemp != NULL)
2313 	{
2314 		delete[] sidetemp;
2315 	}
2316 	sidetemp = new sidei_t[MAX(numvertexes, numsides)];
2317 
2318 	for (i = 0; i < numvertexes; ++i)
2319 	{
2320 		sidetemp[i].b.first = NO_SIDE;
2321 		sidetemp[i].b.next = NO_SIDE;
2322 	}
2323 	for (; i < numsides; ++i)
2324 	{
2325 		sidetemp[i].b.next = NO_SIDE;
2326 	}
2327 
2328 	for (i = 0; i < numsides; ++i)
2329 	{
2330 		// For each vertex, build a list of sidedefs that use that vertex
2331 		// as their left edge.
2332 		line_t *line = sides[i].linedef;
2333 		int lineside = (line->sidedef[0] != &sides[i]);
2334 		int vert = int((lineside ? line->v2 : line->v1) - vertexes);
2335 
2336 		sidetemp[i].b.lineside = lineside;
2337 		sidetemp[i].b.next = sidetemp[vert].b.first;
2338 		sidetemp[vert].b.first = i;
2339 
2340 		// Set each side so that it is the only member of its loop
2341 		sides[i].LeftSide = NO_SIDE;
2342 		sides[i].RightSide = NO_SIDE;
2343 	}
2344 
2345 	// For each side, find the side that is to its right and set the
2346 	// loop pointers accordingly. If two sides share a left vertex, the
2347 	// one that forms the smallest angle is assumed to be the right one.
2348 	for (i = 0; i < numsides; ++i)
2349 	{
2350 		DWORD right;
2351 		line_t *line = sides[i].linedef;
2352 
2353 		// If the side's line only exists in a single sector,
2354 		// then consider that line to be a self-contained loop
2355 		// instead of as part of another loop
2356 		if (line->frontsector == line->backsector)
2357 		{
2358 			const side_t* const rightside = line->sidedef[!sidetemp[i].b.lineside];
2359 
2360 			if (NULL == rightside)
2361 			{
2362 				// There is no right side!
2363 				if (firstloop) Printf ("Line %d's right edge is unconnected\n", linemap[unsigned(line-lines)]);
2364 				continue;
2365 			}
2366 
2367 			right = DWORD(rightside - sides);
2368 		}
2369 		else
2370 		{
2371 			if (sidetemp[i].b.lineside)
2372 			{
2373 				right = int(line->v1 - vertexes);
2374 			}
2375 			else
2376 			{
2377 				right = int(line->v2 - vertexes);
2378 			}
2379 
2380 			right = sidetemp[right].b.first;
2381 
2382 			if (right == NO_SIDE)
2383 			{
2384 				// There is no right side!
2385 				if (firstloop) Printf ("Line %d's right edge is unconnected\n", linemap[unsigned(line-lines)]);
2386 				continue;
2387 			}
2388 
2389 			if (sidetemp[right].b.next != NO_SIDE)
2390 			{
2391 				int bestright = right;	// Shut up, GCC
2392 				angle_t bestang = ANGLE_MAX;
2393 				line_t *leftline, *rightline;
2394 				angle_t ang1, ang2, ang;
2395 
2396 				leftline = sides[i].linedef;
2397 				ang1 = R_PointToAngle2 (0, 0, leftline->dx, leftline->dy);
2398 				if (!sidetemp[i].b.lineside)
2399 				{
2400 					ang1 += ANGLE_180;
2401 				}
2402 
2403 				while (right != NO_SIDE)
2404 				{
2405 					if (sides[right].LeftSide == NO_SIDE)
2406 					{
2407 						rightline = sides[right].linedef;
2408 						if (rightline->frontsector != rightline->backsector)
2409 						{
2410 							ang2 = R_PointToAngle2 (0, 0, rightline->dx, rightline->dy);
2411 							if (sidetemp[right].b.lineside)
2412 							{
2413 								ang2 += ANGLE_180;
2414 							}
2415 
2416 							ang = ang2 - ang1;
2417 
2418 							if (ang != 0 && ang <= bestang)
2419 							{
2420 								bestright = right;
2421 								bestang = ang;
2422 							}
2423 						}
2424 					}
2425 					right = sidetemp[right].b.next;
2426 				}
2427 				right = bestright;
2428 			}
2429 		}
2430 		assert((unsigned)i<(unsigned)numsides);
2431 		assert(right<(unsigned)numsides);
2432 		sides[i].RightSide = right;
2433 		sides[right].LeftSide = i;
2434 	}
2435 
2436 	// We keep the sidedef init info around until after polyobjects are initialized,
2437 	// so don't delete just yet.
2438 }
2439 
P_DetermineTranslucency(int lumpnum)2440 int P_DetermineTranslucency (int lumpnum)
2441 {
2442 	FWadLump tranmap = Wads.OpenLumpNum (lumpnum);
2443 	BYTE index;
2444 	PalEntry newcolor;
2445 	PalEntry newcolor2;
2446 
2447 	tranmap.Seek (GPalette.BlackIndex * 256 + GPalette.WhiteIndex, SEEK_SET);
2448 	tranmap.Read (&index, 1);
2449 
2450 	newcolor = GPalette.BaseColors[GPalette.Remap[index]];
2451 
2452 	tranmap.Seek (GPalette.WhiteIndex * 256 + GPalette.BlackIndex, SEEK_SET);
2453 	tranmap.Read (&index, 1);
2454 	newcolor2 = GPalette.BaseColors[GPalette.Remap[index]];
2455 	if (newcolor2.r == 255)	// if black on white results in white it's either
2456 							// fully transparent or additive
2457 	{
2458 		if (developer)
2459 		{
2460 			char lumpname[9];
2461 			lumpname[8] = 0;
2462 			Wads.GetLumpName (lumpname, lumpnum);
2463 			Printf ("%s appears to be additive translucency %d (%d%%)\n", lumpname, newcolor.r,
2464 				newcolor.r*100/255);
2465 		}
2466 		return -newcolor.r;
2467 	}
2468 
2469 	if (developer)
2470 	{
2471 		char lumpname[9];
2472 		lumpname[8] = 0;
2473 		Wads.GetLumpName (lumpname, lumpnum);
2474 		Printf ("%s appears to be translucency %d (%d%%)\n", lumpname, newcolor.r,
2475 			newcolor.r*100/255);
2476 	}
2477 	return newcolor.r;
2478 }
2479 
P_ProcessSideTextures(bool checktranmap,side_t * sd,sector_t * sec,intmapsidedef_t * msd,int special,int tag,short * alpha,FMissingTextureTracker & missingtex)2480 void P_ProcessSideTextures(bool checktranmap, side_t *sd, sector_t *sec, intmapsidedef_t *msd, int special, int tag, short *alpha, FMissingTextureTracker &missingtex)
2481 {
2482 	switch (special)
2483 	{
2484 	case Transfer_Heights:	// variable colormap via 242 linedef
2485 		  // [RH] The colormap num we get here isn't really a colormap,
2486 		  //	  but a packed ARGB word for blending, so we also allow
2487 		  //	  the blend to be specified directly by the texture names
2488 		  //	  instead of figuring something out from the colormap.
2489 		if (sec != NULL)
2490 		{
2491 			SetTexture (sd, side_t::bottom, &sec->bottommap, msd->bottomtexture);
2492 			SetTexture (sd, side_t::mid, &sec->midmap, msd->midtexture);
2493 			SetTexture (sd, side_t::top, &sec->topmap, msd->toptexture);
2494 		}
2495 		break;
2496 
2497 	case Static_Init:
2498 		// [RH] Set sector color and fog
2499 		// upper "texture" is light color
2500 		// lower "texture" is fog color
2501 		{
2502 			DWORD color = MAKERGB(255,255,255), fog = 0;
2503 			bool colorgood, foggood;
2504 
2505 			SetTextureNoErr (sd, side_t::bottom, &fog, msd->bottomtexture, &foggood, true);
2506 			SetTextureNoErr (sd, side_t::top, &color, msd->toptexture, &colorgood, false);
2507 			SetTexture(sd, side_t::mid, msd->midtexture, missingtex);
2508 
2509 			if (colorgood | foggood)
2510 			{
2511 				int s;
2512 				FDynamicColormap *colormap = NULL;
2513 
2514 				for (s = 0; s < numsectors; s++)
2515 				{
2516 					if (tagManager.SectorHasTag(s, tag))
2517 					{
2518 						if (!colorgood) color = sectors[s].ColorMap->Color;
2519 						if (!foggood) fog = sectors[s].ColorMap->Fade;
2520 						if (colormap == NULL ||
2521 							colormap->Color != color ||
2522 							colormap->Fade != fog)
2523 						{
2524 							colormap = GetSpecialLights (color, fog, 0);
2525 						}
2526 						sectors[s].ColorMap = colormap;
2527 					}
2528 				}
2529 			}
2530 		}
2531 		break;
2532 
2533 	case Sector_Set3DFloor:
2534 		if (msd->toptexture[0]=='#')
2535 		{
2536 			sd->SetTexture(side_t::top, FNullTextureID() +(-strtol(&msd->toptexture[1], NULL, 10)));	// store the alpha as a negative texture index
2537 														// This will be sorted out by the 3D-floor code later.
2538 		}
2539 		else
2540 		{
2541 			SetTexture(sd, side_t::top, msd->toptexture, missingtex);
2542 		}
2543 
2544 		SetTexture(sd, side_t::mid, msd->midtexture, missingtex);
2545 		SetTexture(sd, side_t::bottom, msd->bottomtexture, missingtex);
2546 		break;
2547 
2548 	case TranslucentLine:	// killough 4/11/98: apply translucency to 2s normal texture
2549 		if (checktranmap)
2550 		{
2551 			int lumpnum;
2552 
2553 			if (strnicmp ("TRANMAP", msd->midtexture, 8) == 0)
2554 			{
2555 				// The translator set the alpha argument already; no reason to do it again.
2556 				sd->SetTexture(side_t::mid, FNullTextureID());
2557 			}
2558 			else if ((lumpnum = Wads.CheckNumForName (msd->midtexture)) > 0 &&
2559 				Wads.LumpLength (lumpnum) == 65536)
2560 			{
2561 				*alpha = (short)P_DetermineTranslucency (lumpnum);
2562 				sd->SetTexture(side_t::mid, FNullTextureID());
2563 			}
2564 			else
2565 			{
2566 				SetTexture(sd, side_t::mid, msd->midtexture, missingtex);
2567 			}
2568 
2569 			SetTexture(sd, side_t::top, msd->toptexture, missingtex);
2570 			SetTexture(sd, side_t::bottom, msd->bottomtexture, missingtex);
2571 			break;
2572 		}
2573 		// Fallthrough for Hexen maps is intentional
2574 
2575 	default:			// normal cases
2576 
2577 		SetTexture(sd, side_t::mid, msd->midtexture, missingtex);
2578 		SetTexture(sd, side_t::top, msd->toptexture, missingtex);
2579 		SetTexture(sd, side_t::bottom, msd->bottomtexture, missingtex);
2580 		break;
2581 	}
2582 }
2583 
2584 // killough 4/4/98: delay using texture names until
2585 // after linedefs are loaded, to allow overloading.
2586 // killough 5/3/98: reformatted, cleaned up
2587 
P_LoadSideDefs2(MapData * map,FMissingTextureTracker & missingtex)2588 void P_LoadSideDefs2 (MapData *map, FMissingTextureTracker &missingtex)
2589 {
2590 	int  i;
2591 	char * msdf = new char[map->Size(ML_SIDEDEFS)];
2592 	map->Read(ML_SIDEDEFS, msdf);
2593 
2594 	for (i = 0; i < numsides; i++)
2595 	{
2596 		mapsidedef_t *msd = ((mapsidedef_t*)msdf) + sidetemp[i].a.map;
2597 		side_t *sd = sides + i;
2598 		sector_t *sec;
2599 
2600 		// [RH] The Doom renderer ignored the patch y locations when
2601 		// drawing mid textures. ZDoom does not, so fix the laser beams in Strife.
2602 		if (gameinfo.gametype == GAME_Strife &&
2603 			strncmp (msd->midtexture, "LASERB01", 8) == 0)
2604 		{
2605 			msd->rowoffset += 102;
2606 		}
2607 
2608 		sd->SetTextureXOffset(LittleShort(msd->textureoffset)<<FRACBITS);
2609 		sd->SetTextureYOffset(LittleShort(msd->rowoffset)<<FRACBITS);
2610 		sd->SetTextureXScale(FRACUNIT);
2611 		sd->SetTextureYScale(FRACUNIT);
2612 		sd->linedef = NULL;
2613 		sd->Flags = 0;
2614 		sd->Index = i;
2615 
2616 		// killough 4/4/98: allow sidedef texture names to be overloaded
2617 		// killough 4/11/98: refined to allow colormaps to work as wall
2618 		// textures if invalid as colormaps but valid as textures.
2619 
2620 		if ((unsigned)LittleShort(msd->sector)>=(unsigned)numsectors)
2621 		{
2622 			Printf (PRINT_HIGH, "Sidedef %d has a bad sector\n", i);
2623 			sd->sector = sec = NULL;
2624 		}
2625 		else
2626 		{
2627 			sd->sector = sec = &sectors[LittleShort(msd->sector)];
2628 		}
2629 
2630 		intmapsidedef_t imsd;
2631 		imsd.toptexture.CopyCStrPart(msd->toptexture, 8);
2632 		imsd.midtexture.CopyCStrPart(msd->midtexture, 8);
2633 		imsd.bottomtexture.CopyCStrPart(msd->bottomtexture, 8);
2634 
2635 		P_ProcessSideTextures(!map->HasBehavior, sd, sec, &imsd,
2636 							  sidetemp[i].a.special, sidetemp[i].a.tag, &sidetemp[i].a.alpha, missingtex);
2637 	}
2638 	delete[] msdf;
2639 }
2640 
2641 
2642 //
2643 // [RH] My own blockmap builder, not Killough's or TeamTNT's.
2644 //
2645 // Killough's turned out not to be correct enough, and I had
2646 // written this for ZDBSP before I discovered that, so
2647 // replacing the one he wrote for MBF seemed like the easiest
2648 // thing to do. (Doom E3M6, near vertex 0--the one furthest east
2649 // on the map--had problems.)
2650 //
2651 // Using a hash table to get the minimum possible blockmap size
2652 // seems like overkill, but I wanted to change the code as little
2653 // as possible from its ZDBSP incarnation.
2654 //
2655 
BlockHash(TArray<int> * block)2656 static unsigned int BlockHash (TArray<int> *block)
2657 {
2658 	int hash = 0;
2659 	int *ar = &(*block)[0];
2660 	for (size_t i = 0; i < block->Size(); ++i)
2661 	{
2662 		hash = hash * 12235 + ar[i];
2663 	}
2664 	return hash & 0x7fffffff;
2665 }
2666 
BlockCompare(TArray<int> * block1,TArray<int> * block2)2667 static bool BlockCompare (TArray<int> *block1, TArray<int> *block2)
2668 {
2669 	size_t size = block1->Size();
2670 
2671 	if (size != block2->Size())
2672 	{
2673 		return false;
2674 	}
2675 	if (size == 0)
2676 	{
2677 		return true;
2678 	}
2679 	int *ar1 = &(*block1)[0];
2680 	int *ar2 = &(*block2)[0];
2681 	for (size_t i = 0; i < size; ++i)
2682 	{
2683 		if (ar1[i] != ar2[i])
2684 		{
2685 			return false;
2686 		}
2687 	}
2688 	return true;
2689 }
2690 
CreatePackedBlockmap(TArray<int> & BlockMap,TArray<int> * blocks,int bmapwidth,int bmapheight)2691 static void CreatePackedBlockmap (TArray<int> &BlockMap, TArray<int> *blocks, int bmapwidth, int bmapheight)
2692 {
2693 	int buckets[4096];
2694 	int *hashes, hashblock;
2695 	TArray<int> *block;
2696 	int zero = 0;
2697 	int terminator = -1;
2698 	int *array;
2699 	int i, hash;
2700 	int hashed = 0, nothashed = 0;
2701 
2702 	hashes = new int[bmapwidth * bmapheight];
2703 
2704 	memset (hashes, 0xff, sizeof(int)*bmapwidth*bmapheight);
2705 	memset (buckets, 0xff, sizeof(buckets));
2706 
2707 	for (i = 0; i < bmapwidth * bmapheight; ++i)
2708 	{
2709 		block = &blocks[i];
2710 		hash = BlockHash (block) % 4096;
2711 		hashblock = buckets[hash];
2712 		while (hashblock != -1)
2713 		{
2714 			if (BlockCompare (block, &blocks[hashblock]))
2715 			{
2716 				break;
2717 			}
2718 			hashblock = hashes[hashblock];
2719 		}
2720 		if (hashblock != -1)
2721 		{
2722 			BlockMap[4+i] = BlockMap[4+hashblock];
2723 			hashed++;
2724 		}
2725 		else
2726 		{
2727 			hashes[i] = buckets[hash];
2728 			buckets[hash] = i;
2729 			BlockMap[4+i] = BlockMap.Size ();
2730 			BlockMap.Push (zero);
2731 			array = &(*block)[0];
2732 			for (size_t j = 0; j < block->Size(); ++j)
2733 			{
2734 				BlockMap.Push (array[j]);
2735 			}
2736 			BlockMap.Push (terminator);
2737 			nothashed++;
2738 		}
2739 	}
2740 
2741 	delete[] hashes;
2742 
2743 //	printf ("%d blocks written, %d blocks saved\n", nothashed, hashed);
2744 }
2745 
2746 #define BLOCKBITS 7
2747 #define BLOCKSIZE 128
2748 
P_CreateBlockMap()2749 static void P_CreateBlockMap ()
2750 {
2751 	TArray<int> *BlockLists, *block, *endblock;
2752 	int adder;
2753 	int bmapwidth, bmapheight;
2754 	int minx, maxx, miny, maxy;
2755 	int i;
2756 	int line;
2757 
2758 	if (numvertexes <= 0)
2759 		return;
2760 
2761 	// Find map extents for the blockmap
2762 	minx = maxx = vertexes[0].x;
2763 	miny = maxy = vertexes[0].y;
2764 
2765 	for (i = 1; i < numvertexes; ++i)
2766 	{
2767 			 if (vertexes[i].x < minx) minx = vertexes[i].x;
2768 		else if (vertexes[i].x > maxx) maxx = vertexes[i].x;
2769 			 if (vertexes[i].y < miny) miny = vertexes[i].y;
2770 		else if (vertexes[i].y > maxy) maxy = vertexes[i].y;
2771 	}
2772 
2773 	maxx >>= FRACBITS;
2774 	minx >>= FRACBITS;
2775 	maxy >>= FRACBITS;
2776 	miny >>= FRACBITS;
2777 
2778 	bmapwidth =	 ((maxx - minx) >> BLOCKBITS) + 1;
2779 	bmapheight = ((maxy - miny) >> BLOCKBITS) + 1;
2780 
2781 	TArray<int> BlockMap (bmapwidth * bmapheight * 3 + 4);
2782 
2783 	adder = minx;			BlockMap.Push (adder);
2784 	adder = miny;			BlockMap.Push (adder);
2785 	adder = bmapwidth;		BlockMap.Push (adder);
2786 	adder = bmapheight;		BlockMap.Push (adder);
2787 
2788 	BlockLists = new TArray<int>[bmapwidth * bmapheight];
2789 
2790 	for (line = 0; line < numlines; ++line)
2791 	{
2792 		int x1 = lines[line].v1->x >> FRACBITS;
2793 		int y1 = lines[line].v1->y >> FRACBITS;
2794 		int x2 = lines[line].v2->x >> FRACBITS;
2795 		int y2 = lines[line].v2->y >> FRACBITS;
2796 		int dx = x2 - x1;
2797 		int dy = y2 - y1;
2798 		int bx = (x1 - minx) >> BLOCKBITS;
2799 		int by = (y1 - miny) >> BLOCKBITS;
2800 		int bx2 = (x2 - minx) >> BLOCKBITS;
2801 		int by2 = (y2 - miny) >> BLOCKBITS;
2802 
2803 		block = &BlockLists[bx + by * bmapwidth];
2804 		endblock = &BlockLists[bx2 + by2 * bmapwidth];
2805 
2806 		if (block == endblock)	// Single block
2807 		{
2808 			block->Push (line);
2809 		}
2810 		else if (by == by2)		// Horizontal line
2811 		{
2812 			if (bx > bx2)
2813 			{
2814 				swapvalues (block, endblock);
2815 			}
2816 			do
2817 			{
2818 				block->Push (line);
2819 				block += 1;
2820 			} while (block <= endblock);
2821 		}
2822 		else if (bx == bx2)	// Vertical line
2823 		{
2824 			if (by > by2)
2825 			{
2826 				swapvalues (block, endblock);
2827 			}
2828 			do
2829 			{
2830 				block->Push (line);
2831 				block += bmapwidth;
2832 			} while (block <= endblock);
2833 		}
2834 		else				// Diagonal line
2835 		{
2836 			int xchange = (dx < 0) ? -1 : 1;
2837 			int ychange = (dy < 0) ? -1 : 1;
2838 			int ymove = ychange * bmapwidth;
2839 			int adx = abs (dx);
2840 			int ady = abs (dy);
2841 
2842 			if (adx == ady)		// 45 degrees
2843 			{
2844 				int xb = (x1 - minx) & (BLOCKSIZE-1);
2845 				int yb = (y1 - miny) & (BLOCKSIZE-1);
2846 				if (dx < 0)
2847 				{
2848 					xb = BLOCKSIZE-xb;
2849 				}
2850 				if (dy < 0)
2851 				{
2852 					yb = BLOCKSIZE-yb;
2853 				}
2854 				if (xb < yb)
2855 					adx--;
2856 			}
2857 			if (adx >= ady)		// X-major
2858 			{
2859 				int yadd = dy < 0 ? -1 : BLOCKSIZE;
2860 				do
2861 				{
2862 					int stop = (Scale ((by << BLOCKBITS) + yadd - (y1 - miny), dx, dy) + (x1 - minx)) >> BLOCKBITS;
2863 					while (bx != stop)
2864 					{
2865 						block->Push (line);
2866 						block += xchange;
2867 						bx += xchange;
2868 					}
2869 					block->Push (line);
2870 					block += ymove;
2871 					by += ychange;
2872 				} while (by != by2);
2873 				while (block != endblock)
2874 				{
2875 					block->Push (line);
2876 					block += xchange;
2877 				}
2878 				block->Push (line);
2879 			}
2880 			else					// Y-major
2881 			{
2882 				int xadd = dx < 0 ? -1 : BLOCKSIZE;
2883 				do
2884 				{
2885 					int stop = (Scale ((bx << BLOCKBITS) + xadd - (x1 - minx), dy, dx) + (y1 - miny)) >> BLOCKBITS;
2886 					while (by != stop)
2887 					{
2888 						block->Push (line);
2889 						block += ymove;
2890 						by += ychange;
2891 					}
2892 					block->Push (line);
2893 					block += xchange;
2894 					bx += xchange;
2895 				} while (bx != bx2);
2896 				while (block != endblock)
2897 				{
2898 					block->Push (line);
2899 					block += ymove;
2900 				}
2901 				block->Push (line);
2902 			}
2903 		}
2904 	}
2905 
2906 	BlockMap.Reserve (bmapwidth * bmapheight);
2907 	CreatePackedBlockmap (BlockMap, BlockLists, bmapwidth, bmapheight);
2908 	delete[] BlockLists;
2909 
2910 	blockmaplump = new int[BlockMap.Size()];
2911 	for (unsigned int ii = 0; ii < BlockMap.Size(); ++ii)
2912 	{
2913 		blockmaplump[ii] = BlockMap[ii];
2914 	}
2915 }
2916 
2917 
2918 
2919 //
2920 // P_VerifyBlockMap
2921 //
2922 // haleyjd 03/04/10: do verification on validity of blockmap.
2923 //
P_VerifyBlockMap(int count)2924 static bool P_VerifyBlockMap(int count)
2925 {
2926 	int x, y;
2927 	int *maxoffs = blockmaplump + count;
2928 
2929 	int bmapwidth = blockmaplump[2];
2930 	int bmapheight = blockmaplump[3];
2931 
2932 	for(y = 0; y < bmapheight; y++)
2933 	{
2934 		for(x = 0; x < bmapwidth; x++)
2935 		{
2936 			int offset;
2937 			int *list, *tmplist;
2938 			int *blockoffset;
2939 
2940 			offset = y * bmapwidth + x;
2941 			blockoffset = blockmaplump + offset + 4;
2942 
2943 
2944 			// check that block offset is in bounds
2945 			if(blockoffset >= maxoffs)
2946 			{
2947 				Printf(PRINT_HIGH, "P_VerifyBlockMap: block offset overflow\n");
2948 				return false;
2949 			}
2950 
2951 			offset = *blockoffset;
2952 
2953 			// check that list offset is in bounds
2954 			if(offset < 4 || offset >= count)
2955 			{
2956 				Printf(PRINT_HIGH, "P_VerifyBlockMap: list offset overflow\n");
2957 				return false;
2958 			}
2959 
2960 			list   = blockmaplump + offset;
2961 
2962 			// scan forward for a -1 terminator before maxoffs
2963 			for(tmplist = list; ; tmplist++)
2964 			{
2965 				// we have overflowed the lump?
2966 				if(tmplist >= maxoffs)
2967 				{
2968 					Printf(PRINT_HIGH, "P_VerifyBlockMap: open blocklist\n");
2969 					return false;
2970 				}
2971 				if(*tmplist == -1) // found -1
2972 					break;
2973 			}
2974 
2975 			// scan the list for out-of-range linedef indicies in list
2976 			for(tmplist = list; *tmplist != -1; tmplist++)
2977 			{
2978 				if(*tmplist < 0 || *tmplist >= numlines)
2979 				{
2980 					Printf(PRINT_HIGH, "P_VerifyBlockMap: index >= numlines\n");
2981 					return false;
2982 				}
2983 			}
2984 		}
2985 	}
2986 
2987 	return true;
2988 }
2989 
2990 //
2991 // P_LoadBlockMap
2992 //
2993 // killough 3/1/98: substantially modified to work
2994 // towards removing blockmap limit (a wad limitation)
2995 //
2996 // killough 3/30/98: Rewritten to remove blockmap limit
2997 //
2998 
P_LoadBlockMap(MapData * map)2999 void P_LoadBlockMap (MapData * map)
3000 {
3001 	int count = map->Size(ML_BLOCKMAP);
3002 
3003 	if (ForceNodeBuild || genblockmap ||
3004 		count/2 >= 0x10000 || count == 0 ||
3005 		Args->CheckParm("-blockmap")
3006 		)
3007 	{
3008 		DPrintf ("Generating BLOCKMAP\n");
3009 		P_CreateBlockMap ();
3010 	}
3011 	else
3012 	{
3013 		BYTE *data = new BYTE[count];
3014 		map->Read(ML_BLOCKMAP, data);
3015 		const short *wadblockmaplump = (short *)data;
3016 		int i;
3017 
3018 		count/=2;
3019 		blockmaplump = new int[count];
3020 
3021 		// killough 3/1/98: Expand wad blockmap into larger internal one,
3022 		// by treating all offsets except -1 as unsigned and zero-extending
3023 		// them. This potentially doubles the size of blockmaps allowed,
3024 		// because Doom originally considered the offsets as always signed.
3025 
3026 		blockmaplump[0] = LittleShort(wadblockmaplump[0]);
3027 		blockmaplump[1] = LittleShort(wadblockmaplump[1]);
3028 		blockmaplump[2] = (DWORD)(LittleShort(wadblockmaplump[2])) & 0xffff;
3029 		blockmaplump[3] = (DWORD)(LittleShort(wadblockmaplump[3])) & 0xffff;
3030 
3031 		for (i = 4; i < count; i++)
3032 		{
3033 			short t = LittleShort(wadblockmaplump[i]);          // killough 3/1/98
3034 			blockmaplump[i] = t == -1 ? (DWORD)0xffffffff : (DWORD) t & 0xffff;
3035 		}
3036 		delete[] data;
3037 
3038 		if (!P_VerifyBlockMap(count))
3039 		{
3040 			DPrintf ("Generating BLOCKMAP\n");
3041 			P_CreateBlockMap();
3042 		}
3043 
3044 	}
3045 
3046 	bmaporgx = blockmaplump[0] << FRACBITS;
3047 	bmaporgy = blockmaplump[1] << FRACBITS;
3048 	bmapwidth = blockmaplump[2];
3049 	bmapheight = blockmaplump[3];
3050 	// MAES: set blockmapxneg and blockmapyneg
3051 	// E.g. for a full 512x512 map, they should be both
3052 	// -1. For a 257*257, they should be both -255 etc.
3053 	bmapnegx = bmapwidth > 255 ? bmapwidth - 512 : -257;
3054 	bmapnegy = bmapheight > 255 ? bmapheight - 512 : -257;
3055 
3056 	// clear out mobj chains
3057 	count = bmapwidth*bmapheight;
3058 	blocklinks = new FBlockNode *[count];
3059 	memset (blocklinks, 0, count*sizeof(*blocklinks));
3060 	blockmap = blockmaplump+4;
3061 }
3062 
3063 //
3064 // P_GroupLines
3065 // Builds sector line lists and subsector sector numbers.
3066 // Finds block bounding boxes for sectors.
3067 // [RH] Handles extra lights
3068 //
3069 line_t**				linebuffer;
3070 
P_GroupLines(bool buildmap)3071 static void P_GroupLines (bool buildmap)
3072 {
3073 	cycle_t times[16];
3074 	int*				linesDoneInEachSector;
3075 	int 				i;
3076 	int 				j;
3077 	int 				total;
3078 	line_t* 			li;
3079 	sector_t*			sector;
3080 	FBoundingBox		bbox;
3081 	bool				flaggedNoFronts = false;
3082 	unsigned int		jj;
3083 
3084 	for (i = 0; i < (int)countof(times); ++i)
3085 	{
3086 		times[i].Reset();
3087 	}
3088 
3089 	// look up sector number for each subsector
3090 	times[0].Clock();
3091 	for (i = 0; i < numsubsectors; i++)
3092 	{
3093 		subsectors[i].sector = subsectors[i].firstline->sidedef->sector;
3094 	}
3095 	if (glsegextras != NULL)
3096 	{
3097 		for (i = 0; i < numsubsectors; i++)
3098 		{
3099 			for (jj = 0; jj < subsectors[i].numlines; ++jj)
3100 			{
3101 				glsegextras[subsectors[i].firstline - segs + jj].Subsector = &subsectors[i];
3102 			}
3103 		}
3104 	}
3105 	times[0].Unclock();
3106 
3107 	// count number of lines in each sector
3108 	times[1].Clock();
3109 	total = 0;
3110 	for (i = 0, li = lines; i < numlines; i++, li++)
3111 	{
3112 		if (li->frontsector == NULL)
3113 		{
3114 			if (!flaggedNoFronts)
3115 			{
3116 				flaggedNoFronts = true;
3117 				Printf ("The following lines do not have a front sidedef:\n");
3118 			}
3119 			Printf (" %d\n", i);
3120 		}
3121 		else
3122 		{
3123 			li->frontsector->linecount++;
3124 			total++;
3125 		}
3126 
3127 		if (li->backsector && li->backsector != li->frontsector)
3128 		{
3129 			li->backsector->linecount++;
3130 			total++;
3131 		}
3132 	}
3133 	if (flaggedNoFronts)
3134 	{
3135 		I_Error ("You need to fix these lines to play this map.\n");
3136 	}
3137 	times[1].Unclock();
3138 
3139 	// build line tables for each sector
3140 	times[3].Clock();
3141 	linebuffer = new line_t *[total];
3142 	line_t **lineb_p = linebuffer;
3143 	linesDoneInEachSector = new int[numsectors];
3144 	memset (linesDoneInEachSector, 0, sizeof(int)*numsectors);
3145 
3146 	for (sector = sectors, i = 0; i < numsectors; i++, sector++)
3147 	{
3148 		if (sector->linecount == 0)
3149 		{
3150 			Printf ("Sector %i (tag %i) has no lines\n", i, tagManager.GetFirstSectorTag(sector));
3151 			// 0 the sector's tag so that no specials can use it
3152 			tagManager.RemoveSectorTags(i);
3153 		}
3154 		else
3155 		{
3156 			sector->lines = lineb_p;
3157 			lineb_p += sector->linecount;
3158 		}
3159 	}
3160 
3161 	for (i = numlines, li = lines; i > 0; --i, ++li)
3162 	{
3163 		if (li->frontsector != NULL)
3164 		{
3165 			li->frontsector->lines[linesDoneInEachSector[li->frontsector - sectors]++] = li;
3166 		}
3167 		if (li->backsector != NULL && li->backsector != li->frontsector)
3168 		{
3169 			li->backsector->lines[linesDoneInEachSector[li->backsector - sectors]++] = li;
3170 		}
3171 	}
3172 
3173 	for (i = 0, sector = sectors; i < numsectors; ++i, ++sector)
3174 	{
3175 		if (linesDoneInEachSector[i] != sector->linecount)
3176 		{
3177 			I_Error ("P_GroupLines: miscounted");
3178 		}
3179 		if (sector->linecount != 0)
3180 		{
3181 			bbox.ClearBox ();
3182 			for (j = 0; j < sector->linecount; ++j)
3183 			{
3184 				li = sector->lines[j];
3185 				bbox.AddToBox (li->v1->x, li->v1->y);
3186 				bbox.AddToBox (li->v2->x, li->v2->y);
3187 			}
3188 		}
3189 
3190 		// set the soundorg to the middle of the bounding box
3191 		sector->soundorg[0] = bbox.Right()/2 + bbox.Left()/2;
3192 		sector->soundorg[1] = bbox.Top()/2 + bbox.Bottom()/2;
3193 
3194 		// For triangular sectors the above does not calculate good points unless the longest of the triangle's lines is perfectly horizontal and vertical
3195 		if (sector->linecount == 3)
3196 		{
3197 			vertex_t *Triangle[2];
3198 			Triangle[0] = sector->lines[0]->v1;
3199 			Triangle[1] = sector->lines[0]->v2;
3200 			if (sector->linecount > 1)
3201 			{
3202 				fixed_t dx = Triangle[1]->x - Triangle[0]->x;
3203 				fixed_t dy = Triangle[1]->y - Triangle[0]->y;
3204 				// Find another point in the sector that does not lie
3205 				// on the same line as the first two points.
3206 				for (j = 0; j < 2; ++j)
3207 				{
3208 					vertex_t *v;
3209 
3210 					v = (j == 1) ? sector->lines[1]->v1 : sector->lines[1]->v2;
3211 					if (DMulScale32 (v->y - Triangle[0]->y, dx,
3212 									Triangle[0]->x - v->x, dy) != 0)
3213 					{
3214 						sector->soundorg[0] = Triangle[0]->x / 3 + Triangle[1]->x / 3 + v->x / 3;
3215 						sector->soundorg[1] = Triangle[0]->y / 3 + Triangle[1]->y / 3 + v->y / 3;
3216 						break;
3217 					}
3218 				}
3219 			}
3220 		}
3221 
3222 	}
3223 	delete[] linesDoneInEachSector;
3224 	times[3].Unclock();
3225 
3226 	// [RH] Moved this here
3227 	times[4].Clock();
3228 	// killough 1/30/98: Create xref tables for tags
3229 	tagManager.HashTags();
3230 	times[4].Unclock();
3231 
3232 	times[5].Clock();
3233 	if (!buildmap)
3234 	{
3235 		P_SetSlopes ();
3236 	}
3237 	times[5].Unclock();
3238 
3239 	if (showloadtimes)
3240 	{
3241 		Printf ("---Group Lines Times---\n");
3242 		for (i = 0; i < 7; ++i)
3243 		{
3244 			Printf (" time %d:%9.4f ms\n", i, times[i].TimeMS());
3245 		}
3246 	}
3247 }
3248 
3249 //
3250 // P_LoadReject
3251 //
P_LoadReject(MapData * map,bool junk)3252 void P_LoadReject (MapData * map, bool junk)
3253 {
3254 	const int neededsize = (numsectors * numsectors + 7) >> 3;
3255 	int rejectsize;
3256 
3257 	if (strnicmp (map->MapLumps[ML_REJECT].Name, "REJECT", 8) != 0)
3258 	{
3259 		rejectsize = 0;
3260 	}
3261 	else
3262 	{
3263 		rejectsize = junk ? 0 : map->Size(ML_REJECT);
3264 	}
3265 
3266 	if (rejectsize < neededsize)
3267 	{
3268 		if (rejectsize > 0)
3269 		{
3270 			Printf ("REJECT is %d byte%s too small.\n", neededsize - rejectsize,
3271 				neededsize-rejectsize==1?"":"s");
3272 		}
3273 		rejectmatrix = NULL;
3274 	}
3275 	else
3276 	{
3277 		// Check if the reject has some actual content. If not, free it.
3278 		rejectsize = MIN (rejectsize, neededsize);
3279 		rejectmatrix = new BYTE[rejectsize];
3280 
3281 		map->Seek(ML_REJECT);
3282 		map->file->Read (rejectmatrix, rejectsize);
3283 
3284 		int qwords = rejectsize / 8;
3285 		int i;
3286 
3287 		if (qwords > 0)
3288 		{
3289 			const QWORD *qreject = (const QWORD *)rejectmatrix;
3290 
3291 			i = 0;
3292 			do
3293 			{
3294 				if (qreject[i] != 0)
3295 					return;
3296 			} while (++i < qwords);
3297 		}
3298 		rejectsize &= 7;
3299 		qwords *= 8;
3300 		for (i = 0; i < rejectsize; ++i)
3301 		{
3302 			if (rejectmatrix[qwords + i] != 0)
3303 				return;
3304 		}
3305 
3306 		// Reject has no data, so pretend it isn't there.
3307 		delete[] rejectmatrix;
3308 		rejectmatrix = NULL;
3309 	}
3310 }
3311 
3312 //
3313 // [RH] P_LoadBehavior
3314 //
P_LoadBehavior(MapData * map)3315 void P_LoadBehavior (MapData * map)
3316 {
3317 	map->Seek(ML_BEHAVIOR);
3318 	FBehavior::StaticLoadModule (-1, map->file, map->Size(ML_BEHAVIOR));
3319 	if (!FBehavior::StaticCheckAllGood ())
3320 	{
3321 		Printf ("ACS scripts unloaded.\n");
3322 		FBehavior::StaticUnloadModules ();
3323 	}
3324 }
3325 
P_GetPolySpots(MapData * map,TArray<FNodeBuilder::FPolyStart> & spots,TArray<FNodeBuilder::FPolyStart> & anchors)3326 void P_GetPolySpots (MapData * map, TArray<FNodeBuilder::FPolyStart> &spots, TArray<FNodeBuilder::FPolyStart> &anchors)
3327 {
3328 	if (map->HasBehavior)
3329 	{
3330 		for (unsigned int i = 0; i < MapThingsConverted.Size(); ++i)
3331 		{
3332 			FDoomEdEntry *mentry = MapThingsConverted[i].info;
3333 			if (mentry != NULL && mentry->Type == NULL && mentry->Special >= SMT_PolyAnchor && mentry->Special <= SMT_PolySpawnHurt)
3334 			{
3335 				FNodeBuilder::FPolyStart newvert;
3336 				newvert.x = MapThingsConverted[i].x;
3337 				newvert.y = MapThingsConverted[i].y;
3338 				newvert.polynum = MapThingsConverted[i].angle;
3339 				if (mentry->Special == SMT_PolyAnchor)
3340 				{
3341 					anchors.Push (newvert);
3342 				}
3343 				else
3344 				{
3345 					spots.Push (newvert);
3346 				}
3347 			}
3348 		}
3349 	}
3350 }
3351 
3352 extern polyblock_t **PolyBlockMap;
3353 
P_FreeLevelData()3354 void P_FreeLevelData ()
3355 {
3356 	interpolator.ClearInterpolations();	// [RH] Nothing to interpolate on a fresh level.
3357 	Renderer->CleanLevelData();
3358 	FPolyObj::ClearAllSubsectorLinks(); // can't be done as part of the polyobj deletion process.
3359 	SN_StopAllSequences ();
3360 	DThinker::DestroyAllThinkers ();
3361 	tagManager.Clear();
3362 	level.total_monsters = level.total_items = level.total_secrets =
3363 		level.killed_monsters = level.found_items = level.found_secrets =
3364 		wminfo.maxfrags = 0;
3365 
3366 	FBehavior::StaticUnloadModules ();
3367 	if (vertexes != NULL)
3368 	{
3369 		delete[] vertexes;
3370 		vertexes = NULL;
3371 	}
3372 	numvertexes = 0;
3373 	if (segs != NULL)
3374 	{
3375 		delete[] segs;
3376 		segs = NULL;
3377 	}
3378 	numsegs = 0;
3379 	if (glsegextras != NULL)
3380 	{
3381 		delete[] glsegextras;
3382 		glsegextras = NULL;
3383 	}
3384 	if (sectors != NULL)
3385 	{
3386 		delete[] sectors[0].e;
3387 		delete[] sectors;
3388 		sectors = NULL;
3389 	}
3390 	numsectors = 0;
3391 	if (gamenodes != NULL && gamenodes != nodes)
3392 	{
3393 		delete[] gamenodes;
3394 	}
3395 	if (gamesubsectors != NULL && gamesubsectors != subsectors)
3396 	{
3397 		delete[] gamesubsectors;
3398 	}
3399 	if (subsectors != NULL)
3400 	{
3401 		for (int i = 0; i < numsubsectors; ++i)
3402 		{
3403 			if (subsectors[i].BSP != NULL)
3404 			{
3405 				delete subsectors[i].BSP;
3406 			}
3407 		}
3408 		delete[] subsectors;
3409 	}
3410 	if (nodes != NULL)
3411 	{
3412 		delete[] nodes;
3413 	}
3414 	subsectors = gamesubsectors = NULL;
3415 	numsubsectors = numgamesubsectors = 0;
3416 	nodes = gamenodes = NULL;
3417 	numnodes = numgamenodes = 0;
3418 	if (lines != NULL)
3419 	{
3420 		delete[] lines;
3421 		lines = NULL;
3422 	}
3423 	numlines = 0;
3424 	if (sides != NULL)
3425 	{
3426 		delete[] sides;
3427 		sides = NULL;
3428 	}
3429 	numsides = 0;
3430 
3431 	if (blockmaplump != NULL)
3432 	{
3433 		delete[] blockmaplump;
3434 		blockmaplump = NULL;
3435 	}
3436 	if (blocklinks != NULL)
3437 	{
3438 		delete[] blocklinks;
3439 		blocklinks = NULL;
3440 	}
3441 	if (PolyBlockMap != NULL)
3442 	{
3443 		for (int i = bmapwidth*bmapheight-1; i >= 0; --i)
3444 		{
3445 			polyblock_t *link = PolyBlockMap[i];
3446 			while (link != NULL)
3447 			{
3448 				polyblock_t *next = link->next;
3449 				delete link;
3450 				link = next;
3451 			}
3452 		}
3453 		delete[] PolyBlockMap;
3454 		PolyBlockMap = NULL;
3455 	}
3456 	if (rejectmatrix != NULL)
3457 	{
3458 		delete[] rejectmatrix;
3459 		rejectmatrix = NULL;
3460 	}
3461 	if (linebuffer != NULL)
3462 	{
3463 		delete[] linebuffer;
3464 		linebuffer = NULL;
3465 	}
3466 	if (polyobjs != NULL)
3467 	{
3468 		delete[] polyobjs;
3469 		polyobjs = NULL;
3470 	}
3471 	po_NumPolyobjs = 0;
3472 	if (zones != NULL)
3473 	{
3474 		delete[] zones;
3475 		zones = NULL;
3476 	}
3477 	numzones = 0;
3478 	P_FreeStrifeConversations ();
3479 	if (level.Scrolls != NULL)
3480 	{
3481 		delete[] level.Scrolls;
3482 		level.Scrolls = NULL;
3483 	}
3484 	P_ClearUDMFKeys();
3485 }
3486 
3487 extern msecnode_t *headsecnode;
3488 
P_FreeExtraLevelData()3489 void P_FreeExtraLevelData()
3490 {
3491 	// Free all blocknodes and msecnodes.
3492 	// *NEVER* call this function without calling
3493 	// P_FreeLevelData() first, or they might not all be freed.
3494 	{
3495 		FBlockNode *node = FBlockNode::FreeBlocks;
3496 		while (node != NULL)
3497 		{
3498 			FBlockNode *next = node->NextBlock;
3499 			delete node;
3500 			node = next;
3501 		}
3502 		FBlockNode::FreeBlocks = NULL;
3503 	}
3504 	{
3505 		msecnode_t *node = headsecnode;
3506 
3507 		while (node != NULL)
3508 		{
3509 			msecnode_t *next = node->m_snext;
3510 			M_Free (node);
3511 			node = next;
3512 		}
3513 		headsecnode = NULL;
3514 	}
3515 }
3516 
3517 //
3518 // P_SetupLevel
3519 //
3520 
3521 // [RH] position indicates the start spot to spawn at
P_SetupLevel(const char * lumpname,int position)3522 void P_SetupLevel (const char *lumpname, int position)
3523 {
3524 	cycle_t times[20];
3525 	FMapThing *buildthings;
3526 	int numbuildthings;
3527 	int i;
3528 	bool buildmap;
3529 	const int *oldvertextable = NULL;
3530 
3531 	// This is motivated as follows:
3532 
3533 	bool RequireGLNodes = Renderer->RequireGLNodes() || am_textured;
3534 
3535 	for (i = 0; i < (int)countof(times); ++i)
3536 	{
3537 		times[i].Reset();
3538 	}
3539 
3540 	level.maptype = MAPTYPE_UNKNOWN;
3541 	wminfo.partime = 180;
3542 
3543 	MapThingsConverted.Clear();
3544 	MapThingsUserDataIndex.Clear();
3545 	MapThingsUserData.Clear();
3546 	linemap.Clear();
3547 	FCanvasTextureInfo::EmptyList ();
3548 	R_FreePastViewers ();
3549 	P_ClearUDMFKeys();
3550 
3551 	if (!savegamerestore)
3552 	{
3553 		for (i = 0; i < MAXPLAYERS; ++i)
3554 		{
3555 			players[i].killcount = players[i].secretcount
3556 				= players[i].itemcount = 0;
3557 		}
3558 	}
3559 	for (i = 0; i < MAXPLAYERS; ++i)
3560 	{
3561 		players[i].mo = NULL;
3562 	}
3563 	// [RH] Clear any scripted translation colors the previous level may have set.
3564 	for (i = 0; i < int(translationtables[TRANSLATION_LevelScripted].Size()); ++i)
3565 	{
3566 		FRemapTable *table = translationtables[TRANSLATION_LevelScripted][i];
3567 		if (table != NULL)
3568 		{
3569 			delete table;
3570 			translationtables[TRANSLATION_LevelScripted][i] = NULL;
3571 		}
3572 	}
3573 	translationtables[TRANSLATION_LevelScripted].Clear();
3574 
3575 	// Initial height of PointOfView will be set by player think.
3576 	players[consoleplayer].viewz = 1;
3577 
3578 	// Make sure all sounds are stopped before Z_FreeTags.
3579 	S_Start ();
3580 	// [RH] Clear all ThingID hash chains.
3581 	AActor::ClearTIDHashes ();
3582 
3583 	// [RH] clear out the mid-screen message
3584 	C_MidPrint (NULL, NULL);
3585 
3586 	// Free all level data from the previous map
3587 	P_FreeLevelData ();
3588 
3589 	MapData *map = P_OpenMapData(lumpname, true);
3590 	if (map == NULL)
3591 	{
3592 		I_Error("Unable to open map '%s'\n", lumpname);
3593 	}
3594 
3595 	// find map num
3596 	level.lumpnum = map->lumpnum;
3597 	hasglnodes = false;
3598 
3599 	// [RH] Support loading Build maps (because I felt like it. :-)
3600 	buildmap = false;
3601 	if (map->Size(0) > 0)
3602 	{
3603 		BYTE *mapdata = new BYTE[map->Size(0)];
3604 		map->Seek(0);
3605 		map->file->Read(mapdata, map->Size(0));
3606 		times[0].Clock();
3607 		buildmap = P_LoadBuildMap (mapdata, map->Size(0), &buildthings, &numbuildthings);
3608 		times[0].Unclock();
3609 		delete[] mapdata;
3610 	}
3611 
3612 	if (!buildmap)
3613 	{
3614 		// note: most of this ordering is important
3615 		ForceNodeBuild = gennodes;
3616 
3617 		// [RH] Load in the BEHAVIOR lump
3618 		FBehavior::StaticUnloadModules ();
3619 		if (map->HasBehavior)
3620 		{
3621 			P_LoadBehavior (map);
3622 			level.maptype = MAPTYPE_HEXEN;
3623 		}
3624 		else
3625 		{
3626 			// We need translators only for Doom format maps.
3627 			const char *translator;
3628 
3629 			if (!level.info->Translator.IsEmpty())
3630 			{
3631 				// The map defines its own translator.
3632 				translator = level.info->Translator.GetChars();
3633 			}
3634 			else
3635 			{
3636 				// Has the user overridden the game's default translator with a commandline parameter?
3637 				translator = Args->CheckValue("-xlat");
3638 				if (translator == NULL)
3639 				{
3640 					// Use the game's default.
3641 					translator = gameinfo.translator.GetChars();
3642 				}
3643 			}
3644 			P_LoadTranslator(translator);
3645 			level.maptype = MAPTYPE_DOOM;
3646 		}
3647 		if (map->isText)
3648 		{
3649 			level.maptype = MAPTYPE_UDMF;
3650 		}
3651 		CheckCompatibility(map);
3652 		if (ib_compatflags & BCOMPATF_REBUILDNODES)
3653 		{
3654 			ForceNodeBuild = true;
3655 		}
3656 		T_LoadScripts(map);
3657 
3658 		if (!map->HasBehavior || map->isText)
3659 		{
3660 			// Doom format and UDMF text maps get strict monster activation unless the mapinfo
3661 			// specifies differently.
3662 			if (!(level.flags2 & LEVEL2_LAXACTIVATIONMAPINFO))
3663 			{
3664 				level.flags2 &= ~LEVEL2_LAXMONSTERACTIVATION;
3665 			}
3666 		}
3667 
3668 		if (!map->HasBehavior && !map->isText)
3669 		{
3670 			// set compatibility flags
3671 			if (gameinfo.gametype == GAME_Strife)
3672 			{
3673 				level.flags2 |= LEVEL2_RAILINGHACK;
3674 			}
3675 			level.flags2 |= LEVEL2_DUMMYSWITCHES;
3676 		}
3677 
3678 		FBehavior::StaticLoadDefaultModules ();
3679 
3680 		P_LoadStrifeConversations (map, lumpname);
3681 
3682 		FMissingTextureTracker missingtex;
3683 
3684 		if (!map->isText)
3685 		{
3686 			times[0].Clock();
3687 			P_LoadVertexes (map);
3688 			times[0].Unclock();
3689 
3690 			// Check for maps without any BSP data at all (e.g. SLIGE)
3691 			times[1].Clock();
3692 			P_LoadSectors (map, missingtex);
3693 			times[1].Unclock();
3694 
3695 			times[2].Clock();
3696 			P_LoadSideDefs (map);
3697 			times[2].Unclock();
3698 
3699 			times[3].Clock();
3700 			if (!map->HasBehavior)
3701 				P_LoadLineDefs (map);
3702 			else
3703 				P_LoadLineDefs2 (map);	// [RH] Load Hexen-style linedefs
3704 			times[3].Unclock();
3705 
3706 			times[4].Clock();
3707 			P_LoadSideDefs2 (map, missingtex);
3708 			times[4].Unclock();
3709 
3710 			times[5].Clock();
3711 			P_FinishLoadingLineDefs ();
3712 			times[5].Unclock();
3713 
3714 			if (!map->HasBehavior)
3715 				P_LoadThings (map);
3716 			else
3717 				P_LoadThings2 (map);	// [RH] Load Hexen-style things
3718 
3719 			SetCompatibilityParams();
3720 		}
3721 		else
3722 		{
3723 			times[0].Clock();
3724 			P_ParseTextMap(map, missingtex);
3725 			times[0].Unclock();
3726 		}
3727 
3728 		times[6].Clock();
3729 		P_LoopSidedefs (true);
3730 		times[6].Unclock();
3731 
3732 		linemap.Clear();
3733 		linemap.ShrinkToFit();
3734 
3735 		SummarizeMissingTextures(missingtex);
3736 	}
3737 	else
3738 	{
3739 		ForceNodeBuild = true;
3740 		level.maptype = MAPTYPE_BUILD;
3741 	}
3742 	bool reloop = false;
3743 
3744 	if (!ForceNodeBuild)
3745 	{
3746 		// Check for compressed nodes first, then uncompressed nodes
3747 		FWadLump test;
3748 		DWORD id = MAKE_ID('X','x','X','x'), idcheck = 0, idcheck2 = 0, idcheck3 = 0, idcheck4 = 0, idcheck5 = 0, idcheck6 = 0;
3749 
3750 		if (map->Size(ML_ZNODES) != 0)
3751 		{
3752 			// Test normal nodes first
3753 			map->Seek(ML_ZNODES);
3754 			idcheck = MAKE_ID('Z','N','O','D');
3755 			idcheck2 = MAKE_ID('X','N','O','D');
3756 		}
3757 		else if (map->Size(ML_GLZNODES) != 0)
3758 		{
3759 			map->Seek(ML_GLZNODES);
3760 			idcheck = MAKE_ID('Z','G','L','N');
3761 			idcheck2 = MAKE_ID('Z','G','L','2');
3762 			idcheck3 = MAKE_ID('Z','G','L','3');
3763 			idcheck4 = MAKE_ID('X','G','L','N');
3764 			idcheck5 = MAKE_ID('X','G','L','2');
3765 			idcheck6 = MAKE_ID('X','G','L','3');
3766 		}
3767 
3768 		map->file->Read (&id, 4);
3769 		if (id != 0 && (id == idcheck || id == idcheck2 || id == idcheck3 || id == idcheck4 || id == idcheck5 || id == idcheck6))
3770 		{
3771 			try
3772 			{
3773 				P_LoadZNodes (*map->file, id);
3774 			}
3775 			catch (CRecoverableError &error)
3776 			{
3777 				Printf ("Error loading nodes: %s\n", error.GetMessage());
3778 
3779 				ForceNodeBuild = true;
3780 				if (subsectors != NULL)
3781 				{
3782 					delete[] subsectors;
3783 					subsectors = NULL;
3784 				}
3785 				if (segs != NULL)
3786 				{
3787 					delete[] segs;
3788 					segs = NULL;
3789 				}
3790 				if (nodes != NULL)
3791 				{
3792 					delete[] nodes;
3793 					nodes = NULL;
3794 				}
3795 			}
3796 		}
3797 		else if (!map->isText)	// regular nodes are not supported for text maps
3798 		{
3799 			// If all 3 node related lumps are empty there's no need to output a message.
3800 			// This just means that the map has no nodes and the engine is supposed to build them.
3801 			if (map->Size(ML_SEGS) != 0 || map->Size(ML_SSECTORS) != 0 || map->Size(ML_NODES) != 0)
3802 			{
3803 				if (!P_CheckV4Nodes(map))
3804 				{
3805 					times[7].Clock();
3806 					P_LoadSubsectors<mapsubsector_t, mapseg_t> (map);
3807 					times[7].Unclock();
3808 
3809 					times[8].Clock();
3810 					if (!ForceNodeBuild) P_LoadNodes<mapnode_t, mapsubsector_t> (map);
3811 					times[8].Unclock();
3812 
3813 					times[9].Clock();
3814 					if (!ForceNodeBuild) P_LoadSegs<mapseg_t> (map);
3815 					times[9].Unclock();
3816 				}
3817 				else
3818 				{
3819 					times[7].Clock();
3820 					P_LoadSubsectors<mapsubsector4_t, mapseg4_t> (map);
3821 					times[7].Unclock();
3822 
3823 					times[8].Clock();
3824 					if (!ForceNodeBuild) P_LoadNodes<mapnode4_t, mapsubsector4_t> (map);
3825 					times[8].Unclock();
3826 
3827 					times[9].Clock();
3828 					if (!ForceNodeBuild) P_LoadSegs<mapseg4_t> (map);
3829 					times[9].Unclock();
3830 				}
3831 			}
3832 			else ForceNodeBuild = true;
3833 		}
3834 		else ForceNodeBuild = true;
3835 
3836 		// If loading the regular nodes failed try GL nodes before considering a rebuild
3837 		if (ForceNodeBuild)
3838 		{
3839 			if (P_LoadGLNodes(map))
3840 			{
3841 				ForceNodeBuild = false;
3842 				reloop = true;
3843 			}
3844 		}
3845 	}
3846 	else reloop = true;
3847 
3848 	unsigned int startTime=0, endTime=0;
3849 
3850 	bool BuildGLNodes;
3851 	if (ForceNodeBuild)
3852 	{
3853 		BuildGLNodes = RequireGLNodes || multiplayer || demoplayback || demorecording || genglnodes;
3854 
3855 		startTime = I_FPSTime ();
3856 		TArray<FNodeBuilder::FPolyStart> polyspots, anchors;
3857 		P_GetPolySpots (map, polyspots, anchors);
3858 		FNodeBuilder::FLevel leveldata =
3859 		{
3860 			vertexes, numvertexes,
3861 			sides, numsides,
3862 			lines, numlines,
3863 			0, 0, 0, 0
3864 		};
3865 		leveldata.FindMapBounds ();
3866 		// We need GL nodes if am_textured is on.
3867 		// In case a sync critical game mode is started, also build GL nodes to avoid problems
3868 		// if the different machines' am_textured setting differs.
3869 		FNodeBuilder builder (leveldata, polyspots, anchors, BuildGLNodes);
3870 		delete[] vertexes;
3871 		builder.Extract (nodes, numnodes,
3872 			segs, glsegextras, numsegs,
3873 			subsectors, numsubsectors,
3874 			vertexes, numvertexes);
3875 		endTime = I_FPSTime ();
3876 		DPrintf ("BSP generation took %.3f sec (%d segs)\n", (endTime - startTime) * 0.001, numsegs);
3877 		oldvertextable = builder.GetOldVertexTable();
3878 		reloop = true;
3879 	}
3880 	else
3881 	{
3882 		BuildGLNodes = false;
3883 		// Older ZDBSPs had problems with compressed sidedefs and assigned wrong sides to the segs if both sides were the same sidedef.
3884 		for(i=0;i<numsegs;i++)
3885 		{
3886 			seg_t * seg=&segs[i];
3887 			if (seg->backsector == seg->frontsector && seg->linedef)
3888 			{
3889 				fixed_t d1=P_AproxDistance(seg->v1->x-seg->linedef->v1->x,seg->v1->y-seg->linedef->v1->y);
3890 				fixed_t d2=P_AproxDistance(seg->v2->x-seg->linedef->v1->x,seg->v2->y-seg->linedef->v1->y);
3891 
3892 				if (d2<d1)	// backside
3893 				{
3894 					seg->sidedef = seg->linedef->sidedef[1];
3895 				}
3896 				else	// front side
3897 				{
3898 					seg->sidedef = seg->linedef->sidedef[0];
3899 				}
3900 			}
3901 		}
3902 	}
3903 
3904 	// Copy pointers to the old nodes so that R_PointInSubsector can use them
3905 	if (nodes && subsectors)
3906 	{
3907 		gamenodes = nodes;
3908 		numgamenodes = numnodes;
3909 		gamesubsectors = subsectors;
3910 		numgamesubsectors = numsubsectors;
3911 	}
3912 	else
3913 	{
3914 		gamenodes=NULL;
3915 	}
3916 
3917 	if (RequireGLNodes)
3918 	{
3919 		// Build GL nodes if we want a textured automap or GL nodes are forced to be built.
3920 		// If the original nodes being loaded are not GL nodes they will be kept around for
3921 		// use in P_PointInSubsector to avoid problems with maps that depend on the specific
3922 		// nodes they were built with (P:AR E1M3 is a good example for a map where this is the case.)
3923 		reloop |= P_CheckNodes(map, BuildGLNodes, endTime - startTime);
3924 		hasglnodes = true;
3925 	}
3926 	else
3927 	{
3928 		hasglnodes = P_CheckForGLNodes();
3929 	}
3930 
3931 	times[10].Clock();
3932 	P_LoadBlockMap (map);
3933 	times[10].Unclock();
3934 
3935 	times[11].Clock();
3936 	P_LoadReject (map, buildmap);
3937 	times[11].Unclock();
3938 
3939 	times[12].Clock();
3940 	P_GroupLines (buildmap);
3941 	times[12].Unclock();
3942 
3943 	times[13].Clock();
3944 	P_FloodZones ();
3945 	times[13].Unclock();
3946 
3947 	if (hasglnodes)
3948 	{
3949 		P_SetRenderSector();
3950 	}
3951 
3952 	bodyqueslot = 0;
3953 // phares 8/10/98: Clear body queue so the corpses from previous games are
3954 // not assumed to be from this one.
3955 
3956 	for (i = 0; i < BODYQUESIZE; i++)
3957 		bodyque[i] = NULL;
3958 
3959 	deathmatchstarts.Clear();
3960 	AllPlayerStarts.Clear();
3961 	memset(playerstarts, 0, sizeof(playerstarts));
3962 
3963 	if (!buildmap)
3964 	{
3965 		// [RH] Spawn slope creating things first.
3966 		P_SpawnSlopeMakers (&MapThingsConverted[0], &MapThingsConverted[MapThingsConverted.Size()], oldvertextable);
3967 		P_CopySlopes();
3968 
3969 		// Spawn 3d floors - must be done before spawning things so it can't be done in P_SpawnSpecials
3970 		P_Spawn3DFloors();
3971 
3972 		times[14].Clock();
3973 		P_SpawnThings(position);
3974 
3975 		for (i = 0; i < MAXPLAYERS; ++i)
3976 		{
3977 			if (playeringame[i] && players[i].mo != NULL)
3978 				players[i].health = players[i].mo->health;
3979 		}
3980 		times[14].Unclock();
3981 
3982 		times[15].Clock();
3983 		if (!map->HasBehavior && !map->isText)
3984 			P_TranslateTeleportThings ();	// [RH] Assign teleport destination TIDs
3985 		times[15].Unclock();
3986 	}
3987 	else
3988 	{
3989 		for (i = 0; i < numbuildthings; ++i)
3990 		{
3991 			SpawnMapThing (i, &buildthings[i], 0);
3992 		}
3993 		delete[] buildthings;
3994 	}
3995 	delete map;
3996 	if (oldvertextable != NULL)
3997 	{
3998 		delete[] oldvertextable;
3999 	}
4000 
4001 	// set up world state
4002 	P_SpawnSpecials ();
4003 
4004 	// This must be done BEFORE the PolyObj Spawn!!!
4005 	Renderer->PreprocessLevel();
4006 
4007 	times[16].Clock();
4008 	if (reloop) P_LoopSidedefs (false);
4009 	PO_Init ();	// Initialize the polyobjs
4010 	times[16].Unclock();
4011 
4012 	assert(sidetemp != NULL);
4013 	delete[] sidetemp;
4014 	sidetemp = NULL;
4015 
4016 	// if deathmatch, randomly spawn the active players
4017 	if (deathmatch)
4018 	{
4019 		for (i=0 ; i<MAXPLAYERS ; i++)
4020 		{
4021 			if (playeringame[i])
4022 			{
4023 				players[i].mo = NULL;
4024 				G_DeathMatchSpawnPlayer (i);
4025 			}
4026 		}
4027 	}
4028 	// the same, but for random single/coop player starts
4029 	else if (level.flags2 & LEVEL2_RANDOMPLAYERSTARTS)
4030 	{
4031 		for (i = 0; i < MAXPLAYERS; ++i)
4032 		{
4033 			if (playeringame[i])
4034 			{
4035 				players[i].mo = NULL;
4036 				FPlayerStart *mthing = G_PickPlayerStart(i);
4037 				P_SpawnPlayer(mthing, i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
4038 			}
4039 		}
4040 	}
4041 
4042 	// Don't count monsters in end-of-level sectors if option is on
4043 	if (dmflags2 & DF2_NOCOUNTENDMONST)
4044 	{
4045 		TThinkerIterator<AActor> it;
4046 		AActor * mo;
4047 
4048 		while ((mo=it.Next()))
4049 		{
4050 			if (mo->flags & MF_COUNTKILL)
4051 			{
4052 				if (mo->Sector->damageamount > 0 && (mo->Sector->Flags & (SECF_ENDGODMODE|SECF_ENDLEVEL)) == (SECF_ENDGODMODE|SECF_ENDLEVEL))
4053 				{
4054 					mo->ClearCounters();
4055 				}
4056 			}
4057 		}
4058 	}
4059 
4060 	T_PreprocessScripts();        // preprocess FraggleScript scripts
4061 
4062 	// build subsector connect matrix
4063 	//	UNUSED P_ConnectSubsectors ();
4064 
4065 	R_OldBlend = 0xffffffff;
4066 
4067 	// [RH] Remove all particles
4068 	P_ClearParticles ();
4069 
4070 	times[17].Clock();
4071 	// preload graphics and sounds
4072 	if (precache)
4073 	{
4074 		TexMan.PrecacheLevel ();
4075 		S_PrecacheLevel ();
4076 	}
4077 	times[17].Unclock();
4078 
4079 	if (deathmatch)
4080 	{
4081 		AnnounceGameStart ();
4082 	}
4083 
4084 	P_ResetSightCounters (true);
4085 	//Printf ("free memory: 0x%x\n", Z_FreeMemory());
4086 
4087 	if (showloadtimes)
4088 	{
4089 		Printf ("---Total load times---\n");
4090 		for (i = 0; i < 18; ++i)
4091 		{
4092 			static const char *timenames[] =
4093 			{
4094 				"load vertexes",
4095 				"load sectors",
4096 				"load sides",
4097 				"load lines",
4098 				"load sides 2",
4099 				"load lines 2",
4100 				"loop sides",
4101 				"load subsectors",
4102 				"load nodes",
4103 				"load segs",
4104 				"load blockmap",
4105 				"load reject",
4106 				"group lines",
4107 				"flood zones",
4108 				"load things",
4109 				"translate teleports",
4110 				"init polys",
4111 				"precache"
4112 			};
4113 			Printf ("Time%3d:%9.4f ms (%s)\n", i, times[i].TimeMS(), timenames[i]);
4114 		}
4115 	}
4116 	MapThingsConverted.Clear();
4117 	MapThingsUserDataIndex.Clear();
4118 	MapThingsUserData.Clear();
4119 
4120 	if (glsegextras != NULL)
4121 	{
4122 		delete[] glsegextras;
4123 		glsegextras = NULL;
4124 	}
4125 }
4126 
4127 
4128 
4129 //
4130 // P_Init
4131 //
P_Init()4132 void P_Init ()
4133 {
4134 	atterm (P_Shutdown);
4135 
4136 	P_InitEffects ();		// [RH]
4137 	P_InitTerrainTypes ();
4138 	P_InitKeyMessages ();
4139 	R_InitSprites ();
4140 }
4141 
P_Shutdown()4142 static void P_Shutdown ()
4143 {
4144 	R_DeinitSpriteData ();
4145 	P_DeinitKeyMessages ();
4146 	P_FreeLevelData ();
4147 	P_FreeExtraLevelData ();
4148 	ST_Clear();
4149 }
4150 
4151 #if 0
4152 #include "c_dispatch.h"
4153 CCMD (lineloc)
4154 {
4155 	if (argv.argc() != 2)
4156 	{
4157 		return;
4158 	}
4159 	int linenum = atoi (argv[1]);
4160 	if (linenum < 0 || linenum >= numlines)
4161 	{
4162 		Printf ("No such line\n");
4163 	}
4164 	Printf ("(%d,%d) -> (%d,%d)\n", lines[linenum].v1->x >> FRACBITS,
4165 		lines[linenum].v1->y >> FRACBITS,
4166 		lines[linenum].v2->x >> FRACBITS,
4167 		lines[linenum].v2->y >> FRACBITS);
4168 }
4169 #endif
4170