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 (§ors[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 = §ors[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 = §ors[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