/* ** p_3dfloor.cpp ** ** 3D-floor handling ** **--------------------------------------------------------------------------- ** Copyright 2005-2008 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** 4. Full disclosure of the entire project's source code, except for third ** party libraries is mandatory. (NOTE: This clause is non-negotiable!) ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include "templates.h" #include "p_local.h" #include "p_lnspec.h" #include "w_wad.h" #include "sc_man.h" #include "g_level.h" #include "r_data/colormaps.h" #ifdef _3DFLOORS EXTERN_CVAR(Int, vid_renderer) //========================================================================== // // 3D Floors // //========================================================================== //========================================================================== // // Wrappers to access the colormap information // //========================================================================== FDynamicColormap *F3DFloor::GetColormap() { // If there's no fog in either model or target sector this is easy and fast. if ((target->ColorMap->Fade == 0 && model->ColorMap->Fade == 0) || (flags & (FF_FADEWALLS|FF_FOG))) { return model->ColorMap; } else { // We must create a new colormap combining the properties we need return GetSpecialLights(model->ColorMap->Color, target->ColorMap->Fade, model->ColorMap->Desaturate); } } PalEntry F3DFloor::GetBlend() { // The model sector's fog is used as blend unless FF_FADEWALLS is set. if (!(flags & FF_FADEWALLS) && target->ColorMap->Fade != model->ColorMap->Fade) { return model->ColorMap->Fade; } else { return 0; } } void F3DFloor::UpdateColormap(FDynamicColormap *&map) { // If there's no fog in either model or target sector (or both have the same fog) this is easy and fast. if ((target->ColorMap->Fade == 0 && model->ColorMap->Fade == 0) || (flags & FF_FADEWALLS) || target->ColorMap->Fade == model->ColorMap->Fade) { map = model->ColorMap; } else { // since rebuilding the map is not a cheap operation let's only do it if something really changed. if (map->Color != model->ColorMap->Color || map->Fade != target->ColorMap->Fade || map->Desaturate != model->ColorMap->Desaturate) { map = GetSpecialLights(model->ColorMap->Color, target->ColorMap->Fade, model->ColorMap->Desaturate); } } } //========================================================================== // // Add one 3D floor to the sector // //========================================================================== static void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flags, int alpha) { F3DFloor* ffloor; unsigned i; if(!(flags & FF_THISINSIDE)) { for(i = 0; i < sec2->e->XFloor.attached.Size(); i++) if(sec2->e->XFloor.attached[i] == sec) return; sec2->e->XFloor.attached.Push(sec); } //Add the floor ffloor = new F3DFloor; ffloor->top.model = ffloor->bottom.model = ffloor->model = sec2; ffloor->target = sec; ffloor->ceilingclip = ffloor->floorclip = NULL; ffloor->validcount = 0; if (!(flags&FF_THINFLOOR)) { ffloor->bottom.plane = &sec2->floorplane; ffloor->bottom.texture = &sec2->planes[sector_t::floor].Texture; ffloor->bottom.texheight = &sec2->planes[sector_t::floor].TexZ; ffloor->bottom.isceiling = sector_t::floor; } else { ffloor->bottom.plane = &sec2->ceilingplane; ffloor->bottom.texture = &sec2->planes[sector_t::ceiling].Texture; ffloor->bottom.texheight = &sec2->planes[sector_t::ceiling].TexZ; ffloor->bottom.isceiling = sector_t::ceiling; } if (!(flags&FF_FIX)) { ffloor->top.plane = &sec2->ceilingplane; ffloor->top.texture = &sec2->planes[sector_t::ceiling].Texture; ffloor->top.texheight = &sec2->planes[sector_t::ceiling].TexZ; ffloor->toplightlevel = &sec2->lightlevel; ffloor->top.isceiling = sector_t::ceiling; } else // FF_FIX is a special case to patch rendering holes { ffloor->top.plane = &sec->floorplane; ffloor->top.texture = &sec2->planes[sector_t::floor].Texture; ffloor->top.texheight = &sec2->planes[sector_t::floor].TexZ; ffloor->toplightlevel = &sec->lightlevel; ffloor->top.isceiling = sector_t::floor; ffloor->top.model = sec; } // Hacks for Vavoom's idiotic implementation if (flags&FF_INVERTSECTOR) { // switch the planes F3DFloor::planeref sp = ffloor->top; ffloor->top=ffloor->bottom; ffloor->bottom=sp; if (flags&FF_SWIMMABLE) { // Vavoom floods the lower part if it is swimmable. // fortunately this plane won't be rendered - otherwise this wouldn't work... ffloor->bottom.plane=&sec->floorplane; ffloor->bottom.model=sec; ffloor->bottom.isceiling = sector_t::floor; } } ffloor->flags = flags; ffloor->master = master; ffloor->alpha = alpha; ffloor->top.vindex = ffloor->bottom.vindex = -1; // The engine cannot handle sloped translucent floors. Sorry if (ffloor->top.plane->a || ffloor->top.plane->b || ffloor->bottom.plane->a || ffloor->bottom.plane->b) { ffloor->alpha = FRACUNIT; ffloor->flags &= ~FF_ADDITIVETRANS; } if(flags & FF_THISINSIDE) { // switch the planes F3DFloor::planeref sp = ffloor->top; ffloor->top=ffloor->bottom; ffloor->bottom=sp; } sec->e->XFloor.ffloors.Push(ffloor); // kg3D - software renderer only hack // this is really required because of ceilingclip and floorclip if((vid_renderer == 0) && (flags & FF_BOTHPLANES)) { P_Add3DFloor(sec, sec2, master, FF_EXISTS | FF_THISINSIDE | FF_RENDERPLANES | FF_NOSHADE | FF_SEETHROUGH | FF_SHOOTTHROUGH | (flags & (FF_INVERTSECTOR | FF_TRANSLUCENT | FF_ADDITIVETRANS)), alpha); } } //========================================================================== // // Creates all 3D floors defined by one linedef // //========================================================================== static int P_Set3DFloor(line_t * line, int param, int param2, int alpha) { int s,i; int flags; int tag=line->args[0]; sector_t * sec = line->frontsector, * ss; for (s=-1; (s = P_FindSectorFromTag(tag,s)) >= 0;) { ss=§ors[s]; if (param==0) { flags=FF_EXISTS|FF_RENDERALL|FF_SOLID|FF_INVERTSECTOR; alpha = 255; for (i=0;ilinecount;i++) { line_t * l=sec->lines[i]; if (l->special==Sector_SetContents && l->frontsector==sec) { alpha=clamp(l->args[1], 0, 100); if (l->args[2] & 1) flags &= ~FF_SOLID; if (l->args[2] & 2) flags |= FF_SEETHROUGH; if (l->args[2] & 4) flags |= FF_SHOOTTHROUGH; if (l->args[2] & 8) flags |= FF_ADDITIVETRANS; if (alpha!=100) flags|=FF_TRANSLUCENT;//|FF_BOTHPLANES|FF_ALLSIDES; if (l->args[0]) { // Yes, Vavoom's 3D-floor definitions suck! // The content list changed in r1783 of Vavoom to be unified // among all its supported games, so it has now ten different // values instead of just five. static DWORD vavoomcolors[]={VC_EMPTY, VC_WATER, VC_LAVA, VC_NUKAGE, VC_SLIME, VC_HELLSLIME, VC_BLOOD, VC_SLUDGE, VC_HAZARD, VC_BOOMWATER}; flags|=FF_SWIMMABLE|FF_BOTHPLANES|FF_ALLSIDES|FF_FLOOD; l->frontsector->ColorMap = GetSpecialLights (l->frontsector->ColorMap->Color, vavoomcolors[l->args[0]], l->frontsector->ColorMap->Desaturate); } alpha=(alpha*255)/100; break; } } } else if (param==4) { flags=FF_EXISTS|FF_RENDERPLANES|FF_INVERTPLANES|FF_NOSHADE|FF_FIX; alpha=255; } else { static const int defflags[]= {0, FF_SOLID, FF_SWIMMABLE|FF_BOTHPLANES|FF_ALLSIDES|FF_SHOOTTHROUGH|FF_SEETHROUGH, FF_SHOOTTHROUGH|FF_SEETHROUGH, }; flags = defflags[param&3] | FF_EXISTS|FF_RENDERALL; if (param&4) flags |= FF_ALLSIDES|FF_BOTHPLANES; if (param&16) flags ^= FF_SEETHROUGH; if (param&32) flags ^= FF_SHOOTTHROUGH; if (param2&1) flags |= FF_NOSHADE; if (param2&2) flags |= FF_DOUBLESHADOW; if (param2&4) flags |= FF_FOG; if (param2&8) flags |= FF_THINFLOOR; if (param2&16) flags |= FF_UPPERTEXTURE; if (param2&32) flags |= FF_LOWERTEXTURE; if (param2&64) flags |= FF_ADDITIVETRANS|FF_TRANSLUCENT; // if flooding is used the floor must be non-solid and is automatically made shootthrough and seethrough if ((param2&128) && !(flags & FF_SOLID)) flags |= FF_FLOOD|FF_SEETHROUGH|FF_SHOOTTHROUGH; if (param2&512) flags |= FF_FADEWALLS; FTextureID tex = line->sidedef[0]->GetTexture(side_t::top); if (!tex.Exists() && alpha<255) { alpha = -tex.GetIndex(); } alpha = clamp(alpha, 0, 255); if (alpha==0) flags&=~(FF_RENDERALL|FF_BOTHPLANES|FF_ALLSIDES); else if (alpha!=255) flags|=FF_TRANSLUCENT; } P_Add3DFloor(ss, sec, line, flags, alpha); } // To be 100% safe this should be done even if the alpha by texture value isn't used. if (!line->sidedef[0]->GetTexture(side_t::top).isValid()) line->sidedef[0]->SetTexture(side_t::top, FNullTextureID()); return 1; } //========================================================================== // // P_PlayerOnSpecial3DFloor // Checks to see if a player is standing on or is inside a 3D floor (water) // and applies any specials.. // //========================================================================== void P_PlayerOnSpecial3DFloor(player_t* player) { sector_t * sector = player->mo->Sector; for(unsigned i=0;ie->XFloor.ffloors.Size();i++) { F3DFloor* rover=sector->e->XFloor.ffloors[i]; if (!(rover->flags & FF_EXISTS)) continue; if (rover->flags & FF_FIX) continue; // Check the 3D floor's type... if(rover->flags & FF_SOLID) { // Player must be on top of the floor to be affected... if(player->mo->z != rover->top.plane->ZatPoint(player->mo->x, player->mo->y)) continue; } else { //Water and DEATH FOG!!! heh if (player->mo->z > rover->top.plane->ZatPoint(player->mo->x, player->mo->y) || (player->mo->z + player->mo->height) < rover->bottom.plane->ZatPoint(player->mo->x, player->mo->y)) continue; } if (rover->model->special || rover->model->damage) P_PlayerInSpecialSector(player, rover->model); break; } } //========================================================================== // // P_CheckFor3DFloorHit // Checks whether the player's feet touch a solid 3D floor in the sector // //========================================================================== bool P_CheckFor3DFloorHit(AActor * mo) { sector_t * sector = mo->Sector; if ((mo->player && (mo->player->cheats & CF_PREDICTING))) return false; for(unsigned i=0;ie->XFloor.ffloors.Size();i++) { F3DFloor* rover=sector->e->XFloor.ffloors[i]; if (!(rover->flags & FF_EXISTS)) continue; if(rover->flags & FF_SOLID && rover->model->SecActTarget) { if(mo->floorz == rover->top.plane->ZatPoint(mo->x, mo->y)) { rover->model->SecActTarget->TriggerAction (mo, SECSPAC_HitFloor); return true; } } } return false; } //========================================================================== // // P_CheckFor3DCeilingHit // Checks whether the player's head touches a solid 3D floor in the sector // //========================================================================== bool P_CheckFor3DCeilingHit(AActor * mo) { sector_t * sector = mo->Sector; if ((mo->player && (mo->player->cheats & CF_PREDICTING))) return false; for(unsigned i=0;ie->XFloor.ffloors.Size();i++) { F3DFloor* rover=sector->e->XFloor.ffloors[i]; if (!(rover->flags & FF_EXISTS)) continue; if(rover->flags & FF_SOLID && rover->model->SecActTarget) { if(mo->ceilingz == rover->bottom.plane->ZatPoint(mo->x, mo->y)) { rover->model->SecActTarget->TriggerAction (mo, SECSPAC_HitCeiling); return true; } } } return false; } //========================================================================== // // P_Recalculate3DFloors // // This function sorts the ffloors by height and creates the lightlists // that the given sector uses to light floors/ceilings/walls according to the 3D floors. // //========================================================================== void P_Recalculate3DFloors(sector_t * sector) { F3DFloor * rover; F3DFloor * pick; unsigned pickindex; F3DFloor * clipped=NULL; fixed_t clipped_top; fixed_t clipped_bottom=0; fixed_t maxheight, minheight; unsigned i, j; lightlist_t newlight; TArray & ffloors=sector->e->XFloor.ffloors; TArray & lightlist = sector->e->XFloor.lightlist; // Sort the floors top to bottom for quicker access here and later // Translucent and swimmable floors are split if they overlap with solid ones. if (ffloors.Size()>1) { TArray oldlist; oldlist = ffloors; ffloors.Clear(); // first delete the old dynamic stuff for(i=0;iflags&FF_DYNAMIC) { delete rover; oldlist.Delete(i); i--; continue; } if (rover->flags&FF_CLIPPED) { rover->flags&=~FF_CLIPPED; rover->flags|=FF_EXISTS; } } while (oldlist.Size()) { pick=oldlist[0]; fixed_t height=pick->top.plane->ZatPoint(CenterSpot(sector)); // find highest starting ffloor - intersections are not supported! pickindex=0; for (j=1;jtop.plane->ZatPoint(CenterSpot(sector)); if (h2>height) { pick=oldlist[j]; pickindex=j; height=h2; } } oldlist.Delete(pickindex); if (pick->flags & FF_THISINSIDE) { // These have the floor higher than the ceiling and cannot be processed // by the clipping code below. ffloors.Push(pick); } else if (pick->flags&(FF_SWIMMABLE|FF_TRANSLUCENT) && pick->flags&FF_EXISTS) { clipped=pick; clipped_top=height; clipped_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector)); ffloors.Push(pick); } else if (clipped && clipped_bottomflags|=FF_CLIPPED; clipped->flags&=~FF_EXISTS; dyn->flags|=FF_DYNAMIC; dyn->bottom=pick->top; ffloors.Push(dyn); ffloors.Push(pick); fixed_t pick_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector)); if (pick_bottom<=clipped_bottom) { clipped=NULL; } else { // the translucent part extends below the clipper dyn=new F3DFloor; *dyn=*clipped; dyn->flags|=FF_DYNAMIC|FF_EXISTS; dyn->top=pick->bottom; ffloors.Push(dyn); } } else { clipped=NULL; ffloors.Push(pick); } } } // having the floors sorted makes this routine significantly simpler // Only some overlapping cases with FF_DOUBLESHADOW might create anomalies // but these are clearly undefined. if(ffloors.Size()) { lightlist.Resize(1); lightlist[0].plane = sector->ceilingplane; lightlist[0].p_lightlevel = §or->lightlevel; lightlist[0].caster = NULL; lightlist[0].lightsource = NULL; lightlist[0].extra_colormap = sector->ColorMap; lightlist[0].blend = 0; lightlist[0].flags = 0; maxheight = sector->CenterCeiling(); minheight = sector->CenterFloor(); for(i = 0; i < ffloors.Size(); i++) { rover=ffloors[i]; if ( !(rover->flags & FF_EXISTS) || rover->flags & FF_NOSHADE ) continue; fixed_t ff_top=rover->top.plane->ZatPoint(CenterSpot(sector)); if (ff_top < minheight) break; // reached the floor if (ff_top < maxheight) { newlight.plane = *rover->top.plane; newlight.p_lightlevel = rover->toplightlevel; newlight.caster = rover; newlight.lightsource = rover; newlight.extra_colormap = rover->GetColormap(); newlight.blend = rover->GetBlend(); newlight.flags = rover->flags; lightlist.Push(newlight); } else if (i==0) { fixed_t ff_bottom=rover->bottom.plane->ZatPoint(CenterSpot(sector)); if (ff_bottomtoplightlevel; lightlist[0].caster = rover; lightlist[0].lightsource = rover; lightlist[0].extra_colormap = rover->GetColormap(); lightlist[0].blend = rover->GetBlend(); lightlist[0].flags = rover->flags; } } if (rover->flags&FF_DOUBLESHADOW) { fixed_t ff_bottom=rover->bottom.plane->ZatPoint(CenterSpot(sector)); if(ff_bottom < maxheight && ff_bottom>minheight) { newlight.caster = rover; newlight.plane = *rover->bottom.plane; if (lightlist.Size()>1) { newlight.lightsource = lightlist[lightlist.Size()-2].lightsource; newlight.p_lightlevel = lightlist[lightlist.Size()-2].p_lightlevel; newlight.extra_colormap = lightlist[lightlist.Size()-2].extra_colormap; newlight.blend = lightlist[lightlist.Size()-2].blend; } else { newlight.lightsource = NULL; newlight.p_lightlevel = §or->lightlevel; newlight.extra_colormap = sector->ColorMap; newlight.blend = 0; } newlight.flags = rover->flags; lightlist.Push(newlight); } } } } } //========================================================================== // // recalculates 3D floors for all attached sectors // //========================================================================== void P_RecalculateAttached3DFloors(sector_t * sec) { extsector_t::xfloor &x = sec->e->XFloor; for(unsigned int i=0; i &lightlist = sector->e->XFloor.lightlist; for(unsigned i = 0; i < lightlist.Size(); i++) { lightlist_t *ll = &lightlist[i]; if (ll->lightsource != NULL) { ll->lightsource->UpdateColormap(ll->extra_colormap); ll->blend = ll->lightsource->GetBlend(); } else { ll->extra_colormap = sector->ColorMap; ll->blend = 0; } } } //========================================================================== // // recalculates light lists for all attached sectors // //========================================================================== void P_RecalculateAttachedLights(sector_t *sector) { extsector_t::xfloor &x = sector->e->XFloor; for(unsigned int i=0; i &lightlist = sector->e->XFloor.lightlist; fixed_t planeheight=plane->ZatPoint(CenterSpot(sector)); if(underside) planeheight--; for(i = 1; i < lightlist.Size(); i++) if (lightlist[i].plane.ZatPoint(CenterSpot(sector)) <= planeheight) return &lightlist[i - 1]; return &lightlist[lightlist.Size() - 1]; } //========================================================================== // // Extended P_LineOpening // //========================================================================== void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *linedef, fixed_t x, fixed_t y, fixed_t refx, fixed_t refy, bool restrict) { if(thing) { fixed_t thingbot, thingtop; thingbot = thing->z; thingtop = thingbot + (thing->height==0? 1:thing->height); extsector_t::xfloor *xf[2] = {&linedef->frontsector->e->XFloor, &linedef->backsector->e->XFloor}; // Check for 3D-floors in the sector (mostly identical to what Legacy does here) if(xf[0]->ffloors.Size() || xf[1]->ffloors.Size()) { fixed_t lowestceiling = open.top; fixed_t highestfloor = open.bottom; fixed_t lowestfloor[2] = { linedef->frontsector->floorplane.ZatPoint(x, y), linedef->backsector->floorplane.ZatPoint(x, y) }; FTextureID highestfloorpic; FTextureID lowestceilingpic; highestfloorpic.SetInvalid(); lowestceilingpic.SetInvalid(); for(int j=0;j<2;j++) { for(unsigned i=0;iffloors.Size();i++) { F3DFloor *rover = xf[j]->ffloors[i]; if (!(rover->flags & FF_EXISTS)) continue; if (!(rover->flags & FF_SOLID)) continue; fixed_t ff_bottom=rover->bottom.plane->ZatPoint(x, y); fixed_t ff_top=rover->top.plane->ZatPoint(x, y); fixed_t delta1 = abs(thingbot - ((ff_bottom + ff_top) / 2)); fixed_t delta2 = abs(thingtop - ((ff_bottom + ff_top) / 2)); if(ff_bottom < lowestceiling && delta1 >= delta2) { lowestceiling = ff_bottom; lowestceilingpic = *rover->bottom.texture; } if(ff_top > highestfloor && delta1 < delta2 && (!restrict || thing->z >= ff_top)) { highestfloor = ff_top; highestfloorpic = *rover->top.texture; } if(ff_top > lowestfloor[j] && ff_top <= thing->z + thing->MaxStepHeight) lowestfloor[j] = ff_top; } } if(highestfloor > open.bottom) { open.bottom = highestfloor; open.floorpic = highestfloorpic; } if(lowestceiling < open.top) { open.top = lowestceiling; open.ceilingpic = lowestceilingpic; } open.lowfloor = MIN(lowestfloor[0], lowestfloor[1]); } } } //========================================================================== // // Spawns 3D floors // //========================================================================== void P_Spawn3DFloors (void) { static int flagvals[] = {128+512, 2+512, 512}; int i; line_t * line; for (i=0,line=lines;ispecial) { case ExtraFloor_LightOnly: // Note: I am spawning both this and ZDoom's ExtraLight data // I don't want to mess with both at the same time during rendering // so inserting this into the 3D-floor table as well seemed to be // the best option. // // This does not yet handle case 0 properly! if (line->args[1] < 0 || line->args[1] > 2) line->args[1] = 0; P_Set3DFloor(line, 3, flagvals[line->args[1]], 0); break; case Sector_Set3DFloor: // The flag high-byte/line id is only needed in Hexen format. // UDMF can set both of these parameters without any restriction of the usable values. // In Doom format the translators can take full integers for the tag and the line ID always is the same as the tag. if (level.maptype == MAPTYPE_HEXEN) { if (line->args[1]&8) { line->id = line->args[4]; } else { line->args[0]+=256*line->args[4]; line->args[4]=0; } } P_Set3DFloor(line, line->args[1]&~8, line->args[2], line->args[3]); break; default: continue; } line->special=0; line->args[0] = line->args[1] = line->args[2] = line->args[3] = line->args[4] = 0; } // kg3D - do it in software for (i = 0; i < numsectors; i++) { P_Recalculate3DFloors(§ors[i]); } } //========================================================================== // // Returns a 3D floorplane appropriate for the given coordinates // //========================================================================== secplane_t P_FindFloorPlane(sector_t * sector, fixed_t x, fixed_t y, fixed_t z) { secplane_t retplane = sector->floorplane; if (sector->e) // apparently this can be called when the data is already gone { for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) { F3DFloor * rover= sector->e->XFloor.ffloors[i]; if(!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; if (rover->top.plane->ZatPoint(x, y) == z) { retplane = *rover->top.plane; if (retplane.c<0) retplane.FlipVert(); break; } } } return retplane; } //========================================================================== // // Gives the index to an extra floor above or below the given location. // -1 means normal floor or ceiling // //========================================================================== int P_Find3DFloor(sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool above, bool floor, fixed_t &cmpz) { // If no sector given, find the one appropriate if (sec == NULL) sec = R_PointInSubsector(x, y)->sector; // Above normal ceiling cmpz = sec->ceilingplane.ZatPoint(x, y); if (z >= cmpz) return -1; // Below normal floor cmpz = sec->floorplane.ZatPoint(x, y); if (z <= cmpz) return -1; // Looking through planes from top to bottom for (int i = 0; i < (signed)sec->e->XFloor.ffloors.Size(); ++i) { F3DFloor *rover = sec->e->XFloor.ffloors[i]; // We are only interested in solid 3D floors here if(!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; if (above) { // z is above that floor if (floor && (z >= (cmpz = rover->top.plane->ZatPoint(x, y)))) return i - 1; // z is above that ceiling if (z >= (cmpz = rover->bottom.plane->ZatPoint(x, y))) return i - 1; } else // below { // z is below that ceiling if (!floor && (z <= (cmpz = rover->bottom.plane->ZatPoint(x, y)))) return i; // z is below that floor if (z <= (cmpz = rover->top.plane->ZatPoint(x, y))) return i; } } // Failsafe return -1; } #endif