1 /** @file dmu_lib.cpp Helper routines for accessing the DMU API.
2  *
3  * @authors Copyright © 2006-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
5  *
6  * @par License
7  * GPL: http://www.gnu.org/licenses/gpl.html
8  *
9  * <small>This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2 of the License, or (at your
12  * option) any later version. This program is distributed in the hope that it
13  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details. You should have received a copy of the GNU
16  * General Public License along with this program; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18  * 02110-1301 USA</small>
19  */
20 
21 #include <cstdio>
22 #include <cstring>
23 
24 #include "common.h"
25 #include "dmu_lib.h"
26 #include "p_terraintype.h"
27 
28 #include "dmu_lib.h"
29 
P_PathXYTraverse2(coord_t fromX,coord_t fromY,coord_t toX,coord_t toY,int flags,traverser_t callback,void * context)30 int P_PathXYTraverse2(coord_t fromX, coord_t fromY, coord_t toX, coord_t toY,
31     int flags, traverser_t callback, void *context)
32 {
33     vec2d_t from = { fromX, fromY };
34     vec2d_t to   = { toX, toY };
35     return P_PathTraverse2(from, to, flags, callback, context);
36 }
37 
P_PathXYTraverse(coord_t fromX,coord_t fromY,coord_t toX,coord_t toY,traverser_t callback,void * context)38 int P_PathXYTraverse(coord_t fromX, coord_t fromY, coord_t toX, coord_t toY,
39     traverser_t callback, void *context)
40 {
41     vec2d_t from = { fromX, fromY };
42     vec2d_t to   = { toX, toY };
43     return P_PathTraverse(from, to, callback, context);
44 }
45 
46 struct TagList
47 {
48     iterlist_t *list;
49     int tag;
50 };
51 
52 static TagList *lineTagLists;
53 static uint numLineTagLists;
54 
55 static TagList *sectorTagLists;
56 static uint numSectorTagLists;
57 
P_AllocDummyLine()58 Line *P_AllocDummyLine()
59 {
60     xline_t *extra = (xline_t *)Z_Calloc(sizeof(xline_t), PU_GAMESTATIC, 0);
61     return (Line *)P_AllocDummy(DMU_LINE, extra);
62 }
63 
P_FreeDummyLine(Line * line)64 void P_FreeDummyLine(Line *line)
65 {
66     Z_Free(P_DummyExtraData(line));
67     P_FreeDummy(line);
68 }
69 
P_CopyLine(Line * dest,Line * src)70 void P_CopyLine(Line *dest, Line *src)
71 {
72     xline_t *xsrc  = P_ToXLine(src);
73     xline_t *xdest = P_ToXLine(dest);
74 
75     if(src == dest)
76         return; // no point copying self
77 
78     // Copy the built-in properties
79     for(int i = 0; i < 2; ++i) // For each side
80     {
81         int sidx = (i==0? DMU_FRONT : DMU_BACK);
82 
83         Side *sidefrom = (Side *)P_GetPtrp(src, sidx);
84         Side *sideto = (Side *)P_GetPtrp(dest, sidx);
85 
86         if(!sidefrom || !sideto)
87             continue;
88 
89         float temp[4];
90         coord_t itemp[2];
91 
92         P_SetPtrp    (sideto,   DMU_TOP_MATERIAL,               P_GetPtrp(sidefrom, DMU_TOP_MATERIAL));
93         P_GetDoublepv(sidefrom, DMU_TOP_MATERIAL_OFFSET_XY,     itemp);
94         P_SetDoublepv(sideto,   DMU_TOP_MATERIAL_OFFSET_XY,     itemp);
95         P_GetFloatpv (sidefrom, DMU_TOP_COLOR,                  temp);
96         P_SetFloatpv (sideto,   DMU_TOP_COLOR,                  temp);
97 
98         P_SetPtrp    (sideto,   DMU_MIDDLE_MATERIAL,            P_GetPtrp(sidefrom, DMU_MIDDLE_MATERIAL));
99         P_GetDoublepv(sidefrom, DMU_MIDDLE_MATERIAL_OFFSET_XY,  itemp);
100         P_SetDoublepv(sideto,   DMU_MIDDLE_MATERIAL_OFFSET_XY,  itemp);
101         P_SetFloatpv (sideto,   DMU_MIDDLE_COLOR,               temp);
102         P_SetIntp    (sideto,   DMU_MIDDLE_BLENDMODE,           P_GetIntp(sidefrom, DMU_MIDDLE_BLENDMODE));
103 
104         P_SetPtrp    (sideto,   DMU_BOTTOM_MATERIAL,            P_GetPtrp(sidefrom, DMU_BOTTOM_MATERIAL));
105         P_GetDoublepv(sidefrom, DMU_BOTTOM_MATERIAL_OFFSET_XY,  itemp);
106         P_SetDoublepv(sideto,   DMU_BOTTOM_MATERIAL_OFFSET_XY,  itemp);
107         P_GetFloatpv (sidefrom, DMU_BOTTOM_COLOR,               temp);
108         P_SetFloatpv (sideto,   DMU_BOTTOM_COLOR,               temp);
109     }
110 
111     // Copy the extended properties too
112 #if __JDOOM__ || __JHERETIC__ || __JDOOM64__
113     xdest->special = xsrc->special;
114     xdest->tag     = xsrc->tag;
115     if(xsrc->xg && xdest->xg)
116         std::memcpy(xdest->xg, xsrc->xg, sizeof(*xdest->xg));
117     else
118         xdest->xg = 0;
119 #else
120     xdest->special = xsrc->special;
121     xdest->arg1 = xsrc->arg1;
122     xdest->arg2 = xsrc->arg2;
123     xdest->arg3 = xsrc->arg3;
124     xdest->arg4 = xsrc->arg4;
125     xdest->arg5 = xsrc->arg5;
126 #endif
127 }
128 
P_CopySector(Sector * dest,Sector * src)129 void P_CopySector(Sector *dest, Sector *src)
130 {
131     xsector_t *xsrc = P_ToXSector(src);
132     xsector_t *xdest = P_ToXSector(dest);
133 
134     if(src == dest)
135         return; // no point copying self.
136 
137     // Copy the built-in properties.
138     float ftemp[4];
139     coord_t dtemp[2];
140 
141     P_SetFloatp  (dest, DMU_LIGHT_LEVEL,                P_GetFloatp(src, DMU_LIGHT_LEVEL));
142     P_GetFloatpv (src,  DMU_COLOR,                      ftemp);
143     P_SetFloatpv (dest, DMU_COLOR,                      ftemp);
144 
145     P_SetDoublep (dest, DMU_FLOOR_HEIGHT,               P_GetDoublep(src, DMU_FLOOR_HEIGHT));
146     P_SetPtrp    (dest, DMU_FLOOR_MATERIAL,             P_GetPtrp(src, DMU_FLOOR_MATERIAL));
147     P_GetFloatpv (src,  DMU_FLOOR_COLOR,                ftemp);
148     P_SetFloatpv (dest, DMU_FLOOR_COLOR,                ftemp);
149     P_GetDoublepv(src,  DMU_FLOOR_MATERIAL_OFFSET_XY,   dtemp);
150     P_SetDoublepv(dest, DMU_FLOOR_MATERIAL_OFFSET_XY,   dtemp);
151     P_SetIntp    (dest, DMU_FLOOR_SPEED,                P_GetIntp(src, DMU_FLOOR_SPEED));
152     P_SetDoublep (dest, DMU_FLOOR_TARGET_HEIGHT,        P_GetFloatp(src, DMU_FLOOR_TARGET_HEIGHT));
153 
154     P_SetDoublep (dest, DMU_CEILING_HEIGHT,             P_GetDoublep(src, DMU_CEILING_HEIGHT));
155     P_SetPtrp    (dest, DMU_CEILING_MATERIAL,           P_GetPtrp(src, DMU_CEILING_MATERIAL));
156     P_GetFloatpv (src,  DMU_CEILING_COLOR,              ftemp);
157     P_SetFloatpv (dest, DMU_CEILING_COLOR,              ftemp);
158     P_GetDoublepv(src,  DMU_CEILING_MATERIAL_OFFSET_XY, dtemp);
159     P_SetDoublepv(dest, DMU_CEILING_MATERIAL_OFFSET_XY, dtemp);
160     P_SetIntp    (dest, DMU_CEILING_SPEED,              P_GetIntp(src, DMU_CEILING_SPEED));
161     P_SetDoublep (dest, DMU_CEILING_TARGET_HEIGHT,      P_GetFloatp(src, DMU_CEILING_TARGET_HEIGHT));
162 
163     // Copy the extended properties too
164 #if __JDOOM__ || __JHERETIC__ || __JDOOM64__
165     xdest->special = xsrc->special;
166     xdest->soundTraversed = xsrc->soundTraversed;
167     xdest->soundTarget = xsrc->soundTarget;
168 #if __JHERETIC__
169     xdest->seqType = xsrc->seqType;
170 #endif
171     xdest->SP_floororigheight = xsrc->SP_floororigheight;
172     xdest->SP_ceilorigheight = xsrc->SP_ceilorigheight;
173     xdest->origLight = xsrc->origLight;
174     std::memcpy(xdest->origRGB, xsrc->origRGB, sizeof(float) * 3);
175     if(xsrc->xg && xdest->xg)
176         std::memcpy(xdest->xg, xsrc->xg, sizeof(*xdest->xg));
177     else
178         xdest->xg = 0;
179 #else
180     xdest->special = xsrc->special;
181     xdest->soundTraversed = xsrc->soundTraversed;
182     xdest->soundTarget = xsrc->soundTarget;
183     xdest->seqType = xsrc->seqType;
184 #endif
185 }
186 
P_BuildLineTagLists()187 void P_BuildLineTagLists()
188 {
189     P_DestroyLineTagLists();
190 
191     for(int i = 0; i < numlines; ++i)
192     {
193         Line *line  = (Line *)P_ToPtr(DMU_LINE, i);
194         xline_t *xline = P_ToXLine(line);
195 
196 #if !__JHEXEN__
197         if(xline->tag)
198         {
199            iterlist_t *list = P_GetLineIterListForTag(xline->tag, true);
200            IterList_PushBack(list, line);
201         }
202 #else
203         switch(xline->special)
204         {
205         default: break;
206         case 121: ///< Line_SetIdentification.
207             if(xline->arg1)
208             {
209                 iterlist_t *list = P_GetLineIterListForTag((int) xline->arg1, true);
210                 IterList_PushBack(list, line);
211             }
212             xline->special = 0;
213             break;
214         }
215 #endif
216     }
217 }
218 
P_DestroyLineTagLists()219 void P_DestroyLineTagLists()
220 {
221     if(numLineTagLists == 0)
222         return;
223 
224     for(uint i = 0; i < numLineTagLists; ++i)
225     {
226         IterList_Clear(lineTagLists[i].list);
227         IterList_Delete(lineTagLists[i].list);
228     }
229 
230     free(lineTagLists);
231     lineTagLists = 0;
232     numLineTagLists = 0;
233 }
234 
P_GetLineIterListForTag(int tag,dd_bool createNewList)235 iterlist_t *P_GetLineIterListForTag(int tag, dd_bool createNewList)
236 {
237     // Do we have an existing list for this tag?
238     for(uint i = 0; i < numLineTagLists; ++i)
239     {
240         if(lineTagLists[i].tag == tag)
241             return lineTagLists[i].list;
242     }
243 
244     if(!createNewList)
245         return 0;
246 
247     // Nope, we need to allocate another.
248     numLineTagLists++;
249     lineTagLists = (TagList *)realloc(lineTagLists, sizeof(TagList) * numLineTagLists);
250     TagList *tagList = &lineTagLists[numLineTagLists - 1];
251     tagList->tag = tag;
252 
253     return (tagList->list = IterList_New());
254 }
255 
P_BuildSectorTagLists()256 void P_BuildSectorTagLists()
257 {
258     P_DestroySectorTagLists();
259 
260     for(int i = 0; i < numsectors; ++i)
261     {
262         Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i);
263         xsector_t *xsec = P_ToXSector(sec);
264 
265         if(xsec->tag)
266         {
267             iterlist_t *list = P_GetSectorIterListForTag(xsec->tag, true);
268             IterList_PushBack(list, sec);
269         }
270     }
271 }
272 
P_DestroySectorTagLists()273 void P_DestroySectorTagLists()
274 {
275     if(numSectorTagLists == 0)
276         return;
277 
278     for(uint i = 0; i < numSectorTagLists; ++i)
279     {
280         IterList_Clear(sectorTagLists[i].list);
281         IterList_Delete(sectorTagLists[i].list);
282     }
283 
284     free(sectorTagLists);
285     sectorTagLists = 0;
286     numSectorTagLists = 0;
287 }
288 
P_GetSectorIterListForTag(int tag,dd_bool createNewList)289 iterlist_t *P_GetSectorIterListForTag(int tag, dd_bool createNewList)
290 {
291     // Do we have an existing list for this tag?
292     for(uint i = 0; i < numSectorTagLists; ++i)
293     {
294         if(sectorTagLists[i].tag == tag)
295             return sectorTagLists[i].list;
296     }
297 
298     if(!createNewList)
299         return 0;
300 
301     // Nope, we need to allocate another.
302     numSectorTagLists++;
303     sectorTagLists = (TagList *)realloc(sectorTagLists, sizeof(TagList) * numSectorTagLists);
304     TagList *tagList = &sectorTagLists[numSectorTagLists - 1];
305     tagList->tag = tag;
306 
307     return (tagList->list = IterList_New());
308 }
309 
P_BuildAllTagLists()310 void P_BuildAllTagLists()
311 {
312     P_BuildSectorTagLists();
313     P_BuildLineTagLists();
314 }
315 
P_DestroyAllTagLists()316 void P_DestroyAllTagLists()
317 {
318     P_DestroyLineTagLists();
319     P_DestroySectorTagLists();
320 }
321 
P_GetNextSector(Line * line,Sector * sec)322 Sector *P_GetNextSector(Line *line, Sector *sec)
323 {
324     if(!sec || !line)
325         return 0;
326 
327     if(!(P_ToXLine(line)->flags & ML_TWOSIDED))
328         return 0;
329 
330     Sector *frontSec = (Sector *)P_GetPtrp(line, DMU_FRONT_SECTOR);
331     if(frontSec == sec)
332         return (Sector *)P_GetPtrp(line, DMU_BACK_SECTOR);
333     return frontSec;
334 }
335 
findExtremalLightLevelInAdjacentSectors(void * ptr,void * context)336 int findExtremalLightLevelInAdjacentSectors(void *ptr, void *context)
337 {
338     findlightlevelparams_t *params = (findlightlevelparams_t *) context;
339     Sector *other = P_GetNextSector((Line *) ptr, params->baseSec);
340 
341     if(!other)
342         return false; // Continue iteration.
343 
344     float lightLevel = P_GetFloatp(other, DMU_LIGHT_LEVEL);
345     if(params->flags & FELLF_MIN)
346     {
347         if(lightLevel < params->val)
348         {
349             params->val = lightLevel;
350             params->foundSec = other;
351             if(params->val <= 0)
352                 return true; // Stop iteration. Can't get any darker.
353         }
354     }
355     else if(lightLevel > params->val)
356     {
357         params->val = lightLevel;
358         params->foundSec = other;
359         if(params->val >= 1)
360             return true; // Stop iteration. Can't get any brighter.
361     }
362     return false; // Continue iteration.
363 }
364 
P_FindSectorSurroundingLowestLight(Sector * sec,float * val)365 Sector *P_FindSectorSurroundingLowestLight(Sector *sec, float *val)
366 {
367     findlightlevelparams_t params;
368     params.flags = FELLF_MIN;
369     params.val = DDMAXFLOAT;
370     params.baseSec = sec;
371     params.foundSec = 0;
372     P_Iteratep(sec, DMU_LINE, findExtremalLightLevelInAdjacentSectors, &params);
373     if(*val)
374         *val = params.val;
375     return params.foundSec;
376 }
377 
P_FindSectorSurroundingHighestLight(Sector * sec,float * val)378 Sector *P_FindSectorSurroundingHighestLight(Sector *sec, float *val)
379 {
380     findlightlevelparams_t params;
381     params.flags = 0;
382     params.val = DDMINFLOAT;
383     params.baseSec = sec;
384     params.foundSec = 0;
385     P_Iteratep(sec, DMU_LINE, findExtremalLightLevelInAdjacentSectors, &params);
386     if(val)
387         *val = params.val;
388     return params.foundSec;
389 }
390 
findNextLightLevel(void * ptr,void * context)391 int findNextLightLevel(void* ptr, void *context)
392 {
393     findnextlightlevelparams_t *params = (findnextlightlevelparams_t *) context;
394     Line *li = (Line *) ptr;
395 
396     Sector *other = P_GetNextSector(li, params->baseSec);
397     if(!other)
398         return false; // Continue iteration.
399 
400     float otherLight = P_GetFloatp(other, DMU_LIGHT_LEVEL);
401     if(params->flags & FNLLF_ABOVE)
402     {
403         if(otherLight < params->val && otherLight > params->baseLight)
404         {
405             params->val = otherLight;
406             params->foundSec = other;
407 
408             if(!(params->val > 0))
409                 return true; // Stop iteration. Can't get any darker.
410         }
411     }
412     else if(otherLight > params->val && otherLight < params->baseLight)
413     {
414         params->val = otherLight;
415         params->foundSec = other;
416 
417         if(!(params->val < 1))
418             return true; // Stop iteration. Can't get any brighter.
419     }
420     return false; // Continue iteration.
421 }
422 
P_FindSectorSurroundingNextLowestLight(Sector * sec,float baseLight,float * val)423 Sector *P_FindSectorSurroundingNextLowestLight(Sector *sec, float baseLight, float *val)
424 {
425     findnextlightlevelparams_t params;
426     params.flags = 0;
427     params.val = DDMINFLOAT;
428     params.baseSec = sec;
429     params.baseLight = baseLight;
430     params.foundSec = 0;
431     P_Iteratep(sec, DMU_LINE, findNextLightLevel, &params);
432     if(*val)
433         *val = params.val;
434     return params.foundSec;
435 }
436 
P_FindSectorSurroundingNextHighestLight(Sector * sec,float baseLight,float * val)437 Sector *P_FindSectorSurroundingNextHighestLight(Sector *sec, float baseLight, float *val)
438 {
439     findnextlightlevelparams_t params;
440     params.flags = FNLLF_ABOVE;
441     params.val = DDMAXFLOAT;
442     params.baseSec = sec;
443     params.baseLight = baseLight;
444     params.foundSec = 0;
445     P_Iteratep(sec, DMU_LINE, findNextLightLevel, &params);
446     if(*val)
447         *val = params.val;
448     return params.foundSec;
449 }
450 
findExtremalPlaneHeight(void * ptr,void * context)451 int findExtremalPlaneHeight(void *ptr, void *context)
452 {
453     findextremalplaneheightparams_t *params = (findextremalplaneheightparams_t *) context;
454     Sector *other = P_GetNextSector((Line *) ptr, params->baseSec);
455 
456     if(!other)
457         return false; // Continue iteration.
458 
459     coord_t height = P_GetDoublep(other, ((params->flags & FEPHF_FLOOR)? DMU_FLOOR_HEIGHT : DMU_CEILING_HEIGHT));
460     if(params->flags & FEPHF_MIN)
461     {
462         if(height < params->val)
463         {
464             params->val = height;
465             params->foundSec = other;
466         }
467     }
468     else if(height > params->val)
469     {
470         params->val = height;
471         params->foundSec = other;
472     }
473 
474     return false; // Continue iteration.
475 }
476 
P_FindSectorSurroundingLowestFloor(Sector * sec,coord_t max,coord_t * val)477 Sector *P_FindSectorSurroundingLowestFloor(Sector *sec, coord_t max, coord_t *val)
478 {
479     findextremalplaneheightparams_t params;
480     params.flags = FEPHF_MIN | FEPHF_FLOOR;
481     params.val = max;
482     params.baseSec = sec;
483     params.foundSec = 0;
484     P_Iteratep(sec, DMU_LINE, findExtremalPlaneHeight, &params);
485     if(val)
486         *val = params.val;
487     return params.foundSec;
488 }
489 
P_FindSectorSurroundingHighestFloor(Sector * sec,coord_t min,coord_t * val)490 Sector *P_FindSectorSurroundingHighestFloor(Sector *sec, coord_t min, coord_t *val)
491 {
492     findextremalplaneheightparams_t params;
493     params.flags = FEPHF_FLOOR;
494     params.val = min;
495     params.baseSec = sec;
496     params.foundSec = 0;
497     P_Iteratep(sec, DMU_LINE, findExtremalPlaneHeight, &params);
498     if(val)
499         *val = params.val;
500     return params.foundSec;
501 }
502 
P_FindSectorSurroundingLowestCeiling(Sector * sec,coord_t max,coord_t * val)503 Sector *P_FindSectorSurroundingLowestCeiling(Sector *sec, coord_t max, coord_t *val)
504 {
505     findextremalplaneheightparams_t params;
506     params.flags = FEPHF_MIN;
507     params.val = max;
508     params.baseSec = sec;
509     params.foundSec = 0;
510     P_Iteratep(sec, DMU_LINE, findExtremalPlaneHeight, &params);
511     if(val)
512         *val = params.val;
513     return params.foundSec;
514 }
515 
P_FindSectorSurroundingHighestCeiling(Sector * sec,coord_t min,coord_t * val)516 Sector *P_FindSectorSurroundingHighestCeiling(Sector *sec, coord_t min, coord_t *val)
517 {
518     findextremalplaneheightparams_t params;
519     params.flags = 0;
520     params.val = min;
521     params.baseSec = sec;
522     params.foundSec = 0;
523     P_Iteratep(sec, DMU_LINE, findExtremalPlaneHeight, &params);
524     if(val)
525         *val = params.val;
526     return params.foundSec;
527 }
528 
findNextPlaneHeight(void * ptr,void * context)529 int findNextPlaneHeight(void *ptr, void *context)
530 {
531     findnextplaneheightparams_t *params = (findnextplaneheightparams_t *) context;
532     Sector *other = P_GetNextSector((Line *) ptr, params->baseSec);
533 
534     if(!other)
535         return false; // Continue iteration.
536 
537     coord_t otherHeight = P_GetDoublep(other, ((params->flags & FNPHF_FLOOR)? DMU_FLOOR_HEIGHT : DMU_CEILING_HEIGHT));
538     if(params->flags & FNPHF_ABOVE)
539     {
540         if(otherHeight < params->val && otherHeight > params->baseHeight)
541         {
542             params->val = otherHeight;
543             params->foundSec = other;
544         }
545     }
546     else if(otherHeight > params->val && otherHeight < params->baseHeight)
547     {
548         params->val = otherHeight;
549         params->foundSec = other;
550     }
551     return false; // Continue iteration.
552 }
553 
P_FindSectorSurroundingNextHighestFloor(Sector * sec,coord_t baseHeight,coord_t * val)554 Sector *P_FindSectorSurroundingNextHighestFloor(Sector *sec, coord_t baseHeight, coord_t *val)
555 {
556     findnextplaneheightparams_t params;
557     params.flags = FNPHF_FLOOR | FNPHF_ABOVE;
558     params.val = DDMAXFLOAT;
559     params.baseSec = sec;
560     params.baseHeight = baseHeight;
561     params.foundSec = 0;
562     P_Iteratep(sec, DMU_LINE, findNextPlaneHeight, &params);
563     if(val)
564         *val = params.val;
565     return params.foundSec;
566 }
567 
P_FindSectorSurroundingNextHighestCeiling(Sector * sec,coord_t baseHeight,coord_t * val)568 Sector *P_FindSectorSurroundingNextHighestCeiling(Sector *sec, coord_t baseHeight, coord_t *val)
569 {
570     findnextplaneheightparams_t params;
571     params.flags = FNPHF_ABOVE;
572     params.val = DDMAXFLOAT;
573     params.baseSec = sec;
574     params.baseHeight = baseHeight;
575     params.foundSec = 0;
576     P_Iteratep(sec, DMU_LINE, findNextPlaneHeight, &params);
577     if(val)
578         *val = params.val;
579     return params.foundSec;
580 }
581 
P_FindSectorSurroundingNextLowestFloor(Sector * sec,coord_t baseHeight,coord_t * val)582 Sector *P_FindSectorSurroundingNextLowestFloor(Sector *sec, coord_t baseHeight, coord_t *val)
583 {
584     findnextplaneheightparams_t params;
585     params.flags = FNPHF_FLOOR;
586     params.val = DDMINFLOAT;
587     params.baseSec = sec;
588     params.baseHeight = baseHeight;
589     params.foundSec = 0;
590     P_Iteratep(sec, DMU_LINE, findNextPlaneHeight, &params);
591     if(val)
592         *val = params.val;
593     return params.foundSec;
594 }
595 
P_FindSectorSurroundingNextLowestCeiling(Sector * sec,coord_t baseHeight,coord_t * val)596 Sector *P_FindSectorSurroundingNextLowestCeiling(Sector *sec, coord_t baseHeight, coord_t *val)
597 {
598     findnextplaneheightparams_t params;
599     params.flags = 0;
600     params.val = DDMINFLOAT;
601     params.baseSec = sec;
602     params.baseHeight = baseHeight;
603     params.foundSec = 0;
604     P_Iteratep(sec, DMU_LINE, findNextPlaneHeight, &params);
605     if(val)
606         *val = params.val;
607     return params.foundSec;
608 }
609 
P_SectorLight(Sector * sector)610 float P_SectorLight(Sector *sector)
611 {
612     return P_GetFloatp(sector, DMU_LIGHT_LEVEL);
613 }
614 
P_SectorSetLight(Sector * sector,float level)615 void P_SectorSetLight(Sector *sector, float level)
616 {
617     P_SetFloatp(sector, DMU_LIGHT_LEVEL, level);
618 }
619 
P_SectorModifyLight(Sector * sector,float value)620 void P_SectorModifyLight(Sector *sector, float value)
621 {
622     float level = MINMAX_OF(0.f, P_SectorLight(sector) + value, 1.f);
623     P_SectorSetLight(sector, level);
624 }
625 
P_SectorModifyLightx(Sector * sector,fixed_t value)626 void P_SectorModifyLightx(Sector *sector, fixed_t value)
627 {
628     P_SetFloatp(sector, DMU_LIGHT_LEVEL, P_SectorLight(sector) + FIX2FLT(value) / 255.0f);
629 }
630 
P_PlaneMaterialTerrainType(Sector * sec,int plane)631 terraintype_t const *P_PlaneMaterialTerrainType(Sector *sec, int plane)
632 {
633     return P_TerrainTypeForMaterial((world_Material *)P_GetPtrp(sec, (plane? DMU_CEILING_MATERIAL : DMU_FLOOR_MATERIAL)));
634 }
635 
P_TranslateSideMaterialOrigin(Side * side,SideSection section,float deltaXY[2])636 void P_TranslateSideMaterialOrigin(Side *side, SideSection section, float deltaXY[2])
637 {
638     DENG_ASSERT(side);
639     DENG_ASSERT(VALID_SIDESECTION(section));
640 
641     uint const dmuSurfaceOriginFlags = DMU_OFFSET_XY | DMU_FLAG_FOR_SIDESECTION(section);
642     float origin[2];
643 
644     if(IS_ZERO(deltaXY[0]) && IS_ZERO(deltaXY[1])) return;
645 
646     P_GetFloatpv(side, dmuSurfaceOriginFlags, origin);
647     if(NON_ZERO(deltaXY[0]))
648     {
649         origin[0] += deltaXY[0];
650     }
651     if(NON_ZERO(deltaXY[1]))
652     {
653         origin[1] += deltaXY[1];
654     }
655     P_SetFloatpv(side, dmuSurfaceOriginFlags, origin);
656 }
657 
P_TranslateSideMaterialOriginXY(Side * side,SideSection section,float deltaX,float deltaY)658 void P_TranslateSideMaterialOriginXY(Side *side, SideSection section,
659     float deltaX, float deltaY)
660 {
661     float delta[2];
662     delta[0] = deltaX;
663     delta[1] = deltaY;
664     P_TranslateSideMaterialOrigin(side, section, delta);
665 }
666 
P_TranslatePlaneMaterialOrigin(Plane * plane,float deltaXY[2])667 void P_TranslatePlaneMaterialOrigin(Plane *plane, float deltaXY[2])
668 {
669     float origin[2];
670 
671     DENG_ASSERT(plane);
672 
673     if(IS_ZERO(deltaXY[0]) && IS_ZERO(deltaXY[1])) return;
674 
675     P_GetFloatpv(plane, DMU_OFFSET_XY, origin);
676     if(NON_ZERO(deltaXY[0]))
677     {
678         origin[0] += deltaXY[0];
679     }
680     if(NON_ZERO(deltaXY[1]))
681     {
682         origin[1] += deltaXY[1];
683     }
684     P_SetFloatpv(plane, DMU_OFFSET_XY, origin);
685 }
686 
P_TranslatePlaneMaterialOriginXY(Plane * plane,float deltaX,float deltaY)687 void P_TranslatePlaneMaterialOriginXY(Plane *plane, float deltaX, float deltaY)
688 {
689     float delta[2];
690     delta[0] = deltaX;
691     delta[1] = deltaY;
692     P_TranslatePlaneMaterialOrigin(plane, delta);
693 }
694